Skip to content

Commit

Permalink
feat(walletd): seed wallet send siacoin v2 transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfreska committed Feb 4, 2025
1 parent cf3a6b9 commit 0a937dd
Show file tree
Hide file tree
Showing 40 changed files with 1,991 additions and 209 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-deers-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'walletd': minor
---

Seed wallets now support and automatically switch to sending V2 transactions once the consensus height hits the V2 hardfork allow height.
6 changes: 5 additions & 1 deletion apps/walletd-e2e/src/fixtures/beforeTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import {
export async function beforeTest(page: Page) {
await mockApiSiaScanExchangeRates({ page })
await mockApiSiaCentralHostsNetworkAverages({ page })
await setupCluster({ walletdCount: 1, renterdCount: 1 })
await setupCluster({
walletdCount: 1,
renterdCount: 1,
networkVersion: 'transition',
})
const walletdNode = clusterd.nodes.find((n) => n.type === 'walletd')

await login({
Expand Down
74 changes: 74 additions & 0 deletions apps/walletd-e2e/src/fixtures/seedSendSiacoin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { expect } from '@playwright/test'
import { unlockOpenWallet } from './wallet'
import { navigateToWallet } from './navigate'
import { fillComposeTransactionSiacoin } from './sendSiacoinDialog'
import { step, testRequiresClipboardPermissions } from '@siafoundation/e2e'

export const sendSiacoinWithSeedWallet = step(
'send siacoin with a seed wallet',
async (
page,
{
browserName,
walletName,
mnemonic,
changeAddress,
receiveAddress,
amount,
fee,
}: {
browserName: string
walletName: string
mnemonic: string
changeAddress: string
receiveAddress: string
amount: number
fee: number
}
) => {
testRequiresClipboardPermissions(browserName)
const amountWithFeeString = `${(amount + fee).toFixed(3)} SC`

await navigateToWallet(page, walletName)
await unlockOpenWallet(page, mnemonic)
await page.getByLabel('send').click()
await fillComposeTransactionSiacoin({
page,
receiveAddress,
changeAddress,
amount,
})
const sendDialog = page.getByRole('dialog', { name: 'Send' })
await expect(
sendDialog.getByText('The wallet is currently unlocked')
).toBeVisible()
await expect(sendDialog.getByText('Total')).toBeVisible()
await expect(sendDialog.getByText(amountWithFeeString)).toBeVisible()

await page
.getByRole('button', { name: 'Sign and broadcast transaction' })
.click()
await expect(
page.getByText('Transaction successfully broadcast')
).toBeVisible()
await expect(sendDialog.getByText(receiveAddress.slice(0, 5))).toBeVisible()
await expect(sendDialog.getByText(changeAddress.slice(0, 5))).toBeVisible()
await expect(sendDialog.getByText('Total')).toBeVisible()
await expect(sendDialog.getByText(amountWithFeeString)).toBeVisible()
await sendDialog.getByRole('button', { name: 'Close' }).click()

await page.reload()
await expect(page.getByTestId('eventsTable')).toBeVisible()
await expect(
page.getByTestId('eventsTable').locator('tbody tr').first()
).toBeVisible()
await expect(
page
.getByTestId('eventsTable')
.locator('tbody tr')
.first()
.getByTestId('amount')
.getByText(`-${amountWithFeeString}`)
).toBeVisible()
}
)
4 changes: 2 additions & 2 deletions apps/walletd-e2e/src/fixtures/sendSiacoinDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ export const fillComposeTransactionSiacoin = step(
page: Page
receiveAddress: string
changeAddress: string
amount: string
amount: number
}) => {
await fillTextInputByName(page, 'receiveAddress', receiveAddress)
await page.getByLabel('customChangeAddress').click()
await fillTextInputByName(page, 'changeAddress', changeAddress)
await fillTextInputByName(page, 'siacoin', amount)
await fillTextInputByName(page, 'siacoin', String(amount))
await page.getByRole('button', { name: 'Generate transaction' }).click()
}
)
46 changes: 36 additions & 10 deletions apps/walletd-e2e/src/fixtures/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fillTextareaByName,
waitForTableToReload,
step,
expectThenClick,
} from '@siafoundation/e2e'
import { navigateToWallet } from './navigate'

Expand Down Expand Up @@ -45,7 +46,7 @@ export const recoverWallet = step(
export const deleteWallet = step(
'delete wallet',
async (page: Page, name: string) => {
await openWalletContextMenu(page, name)
await openWalletRowContextMenu(page, name)
await page.getByRole('menuitem', { name: 'Delete wallet' }).click()
await fillTextInputByName(page, 'name', name)
await page.locator('input[name=name]').press('Enter')
Expand All @@ -54,14 +55,29 @@ export const deleteWallet = step(
}
)

export const unlockWallet = step(
export const unlockOpenWallet = step(
'unlock wallet',
async (page: Page, name: string, mnemonic: string) => {
await openWalletContextMenu(page, name)
await page.getByRole('menuitem', { name: 'Unlock wallet' }).click()
await fillTextInputByName(page, 'mnemonic', mnemonic)
await page.locator('input[name=mnemonic]').press('Enter')
await expect(page.getByRole('dialog')).toBeHidden()
async (page: Page, mnemonic: string) => {
await openWalletNavContextMenu(page)
// Wait for menu to open.
await expect(
page.getByRole('menuitem', { name: 'Delete wallet' })
).toBeVisible()
// If we see a "lock wallet" button, its already unlocked.
const isLocked = await page
.getByRole('menuitem', { name: 'Unlock wallet' })
.isVisible()
if (isLocked) {
await expectThenClick(
page.getByRole('menuitem', { name: 'Unlock wallet' })
)
await fillTextInputByName(page, 'mnemonic', mnemonic)
await page.locator('input[name=mnemonic]').press('Enter')
await expect(page.getByRole('dialog')).toBeHidden()
} else {
// Close context menu.
await page.keyboard.press('Escape')
}
}
)

Expand Down Expand Up @@ -153,15 +169,25 @@ export const openWallet = step(
}
)

export const openWalletContextMenu = step(
'open wallet context menu',
export const openWalletRowContextMenu = step(
'open wallet row context menu',
async (page: Page, name: string) => {
const menu = page.getByRole('row', { name }).getByRole('button').first()
await expect(menu).toBeVisible()
await menu.click()
}
)

export const openWalletNavContextMenu = step(
'open wallet nav context menu',
async (page: Page) => {
const menu = page.getByLabel('wallet context menu')
await expect(menu).toBeVisible()
await menu.click()
return menu
}
)

export const openWalletsContextMenu = step(
'open wallets context menu',
async (page: Page) => {
Expand Down
65 changes: 27 additions & 38 deletions apps/walletd-e2e/src/specs/seedSendSiacoin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { test, expect } from '@playwright/test'
import { recoverWallet, rescanWallets, walletInList } from '../fixtures/wallet'
import { navigateToWallet } from '../fixtures/navigate'
import { fillComposeTransactionSiacoin } from '../fixtures/sendSiacoinDialog'
import { random } from '@technically/lodash'
import {
afterTest,
Expand All @@ -10,6 +9,8 @@ import {
} from '../fixtures/beforeTest'
import { toHastings } from '@siafoundation/units'
import { testRequiresClipboardPermissions } from '@siafoundation/e2e'
import { sendSiacoinWithSeedWallet } from '../fixtures/seedSendSiacoin'
import { mine, getCurrentHeight } from '@siafoundation/clusterd'

test.beforeEach(async ({ page }) => {
await beforeTest(page)
Expand All @@ -19,7 +20,12 @@ test.afterEach(async () => {
await afterTest()
})

test('send siacoin with a seed wallet', async ({ page, browserName }) => {
test('send siacoin with a seed wallet pre and post v2 fork allow height', async ({
page,
browserName,
}) => {
const height = await getCurrentHeight()
console.log('height', height)
testRequiresClipboardPermissions(browserName)
// Mnemonic and address0 for the wallet.
const name = 'test-send-siacoin-seed-wallet'
Expand All @@ -34,52 +40,35 @@ test('send siacoin with a seed wallet', async ({ page, browserName }) => {
const receiveAddress =
'5739945c21e60afd70eaf97ccd33ea27836e0219212449f39e4b38acaa8b3119aa4150a9ef0f'
const changeAddress = address0
const amount = String(random(1, 20))
const amountWithFeeString = `${amount}.004 SC`

await recoverWallet(page, name, mnemonic)
await walletInList(page, name)
await rescanWallets(page)
await navigateToWallet(page, name)
const navbar = page.getByTestId('navbar')
await expect(navbar.getByText('1.000 MS')).toBeVisible({ timeout: 20_000 })
await page.getByLabel('send').click()
await fillComposeTransactionSiacoin({
page,

await sendSiacoinWithSeedWallet(page, {
browserName,
walletName: name,
mnemonic,
receiveAddress,
changeAddress,
amount,
amount: random(1, 20),
fee: 0.004,
})
const sendDialog = page.getByRole('dialog', { name: 'Send' })
await expect(
sendDialog.getByText('The wallet is currently unlocked')
).toBeVisible()
await expect(sendDialog.getByText('Total')).toBeVisible()
await expect(sendDialog.getByText(amountWithFeeString)).toBeVisible()

await page
.getByRole('button', { name: 'Sign and broadcast transaction' })
.click()
await expect(
page.getByText('Transaction successfully broadcast')
).toBeVisible()
await expect(sendDialog.getByText(receiveAddress.slice(0, 5))).toBeVisible()
await expect(sendDialog.getByText(changeAddress.slice(0, 5))).toBeVisible()
await expect(sendDialog.getByText('Total')).toBeVisible()
await expect(sendDialog.getByText(amountWithFeeString)).toBeVisible()
await sendDialog.getByRole('button', { name: 'Close' }).click()

await mine(100)
await page.reload()
await expect(page.getByTestId('eventsTable')).toBeVisible()
await expect(
page.getByTestId('eventsTable').locator('tbody tr').first()
).toBeVisible()
await expect(
page
.getByTestId('eventsTable')
.locator('tbody tr')
.first()
.getByTestId('amount')
.getByText(`-${amountWithFeeString}`)
).toBeVisible()

await sendSiacoinWithSeedWallet(page, {
browserName,
walletName: name,
mnemonic,
receiveAddress,
changeAddress,
amount: random(21, 40),
// Fee is calculated differently in v2 send flow.
fee: 0.02,
})
})
1 change: 1 addition & 0 deletions apps/walletd/components/Wallet/WalletActionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export function WalletActionsMenu() {
wallet={wallet}
trigger={
<Button
aria-label="wallet context menu"
size="small"
tip="Wallet settings"
tipAlign="end"
Expand Down
1 change: 0 additions & 1 deletion apps/walletd/components/WalletContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export function WalletContextMenu({
{metadata.type === 'seed' ? (
state.status === 'unlocked' ? (
<DropdownMenuItem
aria-label="Wallet context menu"
onClick={(e) => e.stopPropagation()}
onSelect={() => actions.lock()}
>
Expand Down
12 changes: 6 additions & 6 deletions apps/walletd/dialogs/WalletSendLedgerDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import BigNumber from 'bignumber.js'
import { useMemo, useState } from 'react'
import { useWalletBalance } from '@siafoundation/walletd-react'
import { useComposeForm } from '../_sharedWalletSend/useComposeForm'
import { useComposeForm } from '../_sharedWalletSend/useComposeFormV1'
import { useSendForm } from './useSendForm'
import {
SendParams,
SendParamsV1,
SendStep,
emptySendParams,
emptySendParamsV1,
} from '../_sharedWalletSend/types'
import { SendFlowDialog } from '../_sharedWalletSend/SendFlowDialog'
import { SendFlowDialogV1 } from '../_sharedWalletSend/SendFlowDialogV1'
import { useWalletAddresses } from '../../hooks/useWalletAddresses'

export type WalletSendLedgerDialogParams = {
Expand All @@ -31,7 +31,7 @@ export function WalletSendLedgerDialog({
const { walletId } = dialogParams || {}
const [step, setStep] = useState<SendStep>('compose')
const [signedTxnId, setSignedTxnId] = useState<string>()
const [sendParams, setSendParams] = useState<SendParams>(emptySendParams)
const [sendParams, setSendParams] = useState<SendParamsV1>(emptySendParamsV1)
const balance = useWalletBalance({
disabled: !walletId,
params: {
Expand Down Expand Up @@ -95,7 +95,7 @@ export function WalletSendLedgerDialog({
}, [step, compose, send])

return (
<SendFlowDialog
<SendFlowDialogV1
trigger={trigger}
open={open}
onOpenChange={(val) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Transaction } from '@siafoundation/types'
import { useCallback } from 'react'
import { SendParams } from '../_sharedWalletSend/types'
import { SendParamsV1 } from '../_sharedWalletSend/types'

type Props = {
fund: (params: SendParams) => Promise<{
fund: (params: SendParamsV1) => Promise<{
fundedTransaction?: Transaction
toSign?: string[]
error?: string
Expand All @@ -23,7 +23,7 @@ type Props = {

export function useFundAndSign({ fund, cancel, sign }: Props) {
const fundAndSign = useCallback(
async (params: SendParams) => {
async (params: SendParamsV1) => {
const {
fundedTransaction,
toSign,
Expand Down
Loading

0 comments on commit 0a937dd

Please sign in to comment.