diff --git a/apps/walletd-e2e/src/fixtures/createWallet.ts b/apps/walletd-e2e/src/fixtures/createWallet.ts index 35d83c34c..5244a17e9 100644 --- a/apps/walletd-e2e/src/fixtures/createWallet.ts +++ b/apps/walletd-e2e/src/fixtures/createWallet.ts @@ -1,5 +1,5 @@ import { expect, Page } from '@playwright/test' -import { mockApiWallet } from '../mocks/mock' +import { mockApiWallet, mockApiWallets } from '@siafoundation/mocks' import { Wallet, WalletAddressesResponse, @@ -7,16 +7,15 @@ import { WalletFundResponse, WalletOutputsSiacoinResponse, } from '@siafoundation/react-walletd' -import { mockApiWallets } from '../mocks/wallets' export async function createWallet({ page, - seed, + mnemonic, newWallet, responses = {}, }: { page: Page - seed: string + mnemonic: string newWallet: Wallet responses?: { balance?: WalletBalanceResponse @@ -45,6 +44,6 @@ export async function createWallet({ await expect( page.getByText(`Wallet ${newWallet.name.slice(0, 5)}`) ).toBeVisible() - await page.locator('input[name=mnemonic]').fill(seed) + await page.locator('input[name=mnemonic]').fill(mnemonic) await page.getByRole('button', { name: 'Continue' }).click() } diff --git a/apps/walletd-e2e/src/specs/login.spec.ts b/apps/walletd-e2e/src/specs/login.spec.ts index 58a2676ba..33345056c 100644 --- a/apps/walletd-e2e/src/specs/login.spec.ts +++ b/apps/walletd-e2e/src/specs/login.spec.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test' -import { mockApiDefaults } from '../mocks/mock' +import { mockApiDefaults } from '@siafoundation/mocks' import { login } from '../fixtures/login' test('login', async ({ page }) => { diff --git a/apps/walletd-e2e/src/specs/seedGenerateAddresses/seedGenerateAddresses.spec.ts b/apps/walletd-e2e/src/specs/seedGenerateAddresses.spec.ts similarity index 56% rename from apps/walletd-e2e/src/specs/seedGenerateAddresses/seedGenerateAddresses.spec.ts rename to apps/walletd-e2e/src/specs/seedGenerateAddresses.spec.ts index 882350bb0..494ce7665 100644 --- a/apps/walletd-e2e/src/specs/seedGenerateAddresses/seedGenerateAddresses.spec.ts +++ b/apps/walletd-e2e/src/specs/seedGenerateAddresses.spec.ts @@ -1,41 +1,40 @@ import { test, expect } from '@playwright/test' -import { mockApiDefaults } from '../../mocks/mock' -import { login } from '../../fixtures/login' -import { createWallet } from '../../fixtures/createWallet' -import { navigateToWallet } from '../../fixtures/navigateToWallet' +import { login } from '../fixtures/login' +import { createWallet } from '../fixtures/createWallet' +import { navigateToWallet } from '../fixtures/navigateToWallet' import { - seed, - newWallet, - mockWalletAddressesResponse, -} from './seedGenerateAddresses.mock' -import { cloneDeep } from '@technically/lodash' + getMockScenarioSeedWallet, + mockApiDefaults, +} from '@siafoundation/mocks' +import { WalletAddressesResponse } from '@siafoundation/react-walletd' -const defaultMockWalletResponses = { - addresses: mockWalletAddressesResponse, +function getMockWalletAddressesResponse(): WalletAddressesResponse { + return [] +} + +function getDefaultMockWalletResponses() { + return { + addresses: getMockWalletAddressesResponse(), + } } test('generate new addresses', async ({ page }) => { await mockApiDefaults({ page }) await login({ page }) - const mockAddressesInvalid = cloneDeep(mockWalletAddressesResponse) - mockAddressesInvalid.forEach((address) => { - address.metadata.publicKey = undefined - }) + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic } = mocks await createWallet({ page, newWallet, - seed, - responses: defaultMockWalletResponses, + mnemonic, + responses: getDefaultMockWalletResponses(), }) await navigateToWallet({ page, wallet: newWallet }) await page.getByLabel('view addresses').click() await page.getByRole('button', { name: 'Add addresses' }).click() await page.locator('input[name=count]').fill('5') await page.getByRole('button', { name: 'Continue' }).click() - await expect( - page.getByText('60838523a4bdeeec5b4f70a6678da48a77ad58fe...') - ).toBeVisible() await expect( page.getByText('65b40f6a720352ad5b9546b9f5077209672914cc...') ).toBeVisible() @@ -48,4 +47,7 @@ test('generate new addresses', async ({ page }) => { await expect( page.getByText('170173c40ca0f39f9618da30af14c390c7ce7024...') ).toBeVisible() + await expect( + page.getByText('90c6057cdd2463eca61f83796e83152dbba28b6c...') + ).toBeVisible() }) diff --git a/apps/walletd-e2e/src/specs/seedGenerateAddresses/seedGenerateAddresses.mock.ts b/apps/walletd-e2e/src/specs/seedGenerateAddresses/seedGenerateAddresses.mock.ts deleted file mode 100644 index f4a327f00..000000000 --- a/apps/walletd-e2e/src/specs/seedGenerateAddresses/seedGenerateAddresses.mock.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { WalletAddress } from '@siafoundation/react-walletd' - -export const seed = - 'ridge business wish transfer home glove office salt wealth baby journey diary' - -export const newWallet = { - id: '100', - name: 'test send wallet', - description: 'wallet description', - dateCreated: new Date().toISOString(), - lastUpdated: new Date().toISOString(), - metadata: { - type: 'seed', - seedHash: - 'd4ad57da37936a053b0587fcc4964e9de89da8fbd610f7de190c32e22e6acd2a1c8f9f88bf219ffac04801b709712f3a2e64cc4f0271c402a6c4659573024440', - }, -} - -export const mockWalletAddressesResponse: WalletAddress[] = [ - { - address: - 'addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981', - description: '', - metadata: { - index: 0, - publicKey: - 'ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535', - }, - }, - { - address: - 'addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8', - description: '', - metadata: { - index: 1, - publicKey: - 'ed25519:624d6d477a8f4ceac873e6dd9138740f9322cb34a24246f96f9d64c021172f43', - }, - }, - { - address: - 'addr:170173c40ca0f39f9618da30af14c390c7ce70248a3662a7a5d3d5a8a31c9fbfa2071e9f6518', - description: '', - metadata: { - index: 2, - publicKey: - 'ed25519:65cac661a4acf36847c0aa67cbc6956e3449fd82a7430cfd673ea7fedbfcf5fa', - }, - }, -] diff --git a/apps/walletd-e2e/src/specs/seedSendSiacoin/seedSendSiacoin.spec.ts b/apps/walletd-e2e/src/specs/seedSendSiacoin.spec.ts similarity index 70% rename from apps/walletd-e2e/src/specs/seedSendSiacoin/seedSendSiacoin.spec.ts rename to apps/walletd-e2e/src/specs/seedSendSiacoin.spec.ts index 8f978625b..29b8a8652 100644 --- a/apps/walletd-e2e/src/specs/seedSendSiacoin/seedSendSiacoin.spec.ts +++ b/apps/walletd-e2e/src/specs/seedSendSiacoin.spec.ts @@ -1,41 +1,40 @@ import { test, expect } from '@playwright/test' -import { mockApiDefaults } from '../../mocks/mock' -import { login } from '../../fixtures/login' -import { createWallet } from '../../fixtures/createWallet' -import { navigateToWallet } from '../../fixtures/navigateToWallet' +import { login } from '../fixtures/login' +import { createWallet } from '../fixtures/createWallet' +import { navigateToWallet } from '../fixtures/navigateToWallet' +import { fillComposeTransactionSiacoin } from '../fixtures/sendSiacoinDialog' import { - seed, - newWallet, - receiveAddress, - changeAddress, - mockWalletFundResponse, - mockWalletOutputsSiacoinResponse, - mockWalletAddressesResponse, -} from './seedSendSiacoin.mock' -import { fillComposeTransactionSiacoin } from '../../fixtures/sendSiacoinDialog' -import { cloneDeep } from '@technically/lodash' + getMockScenarioSeedWallet, + mockApiDefaults, +} from '@siafoundation/mocks' -const defaultMockWalletResponses = { - fund: mockWalletFundResponse, - outputsSiacoin: mockWalletOutputsSiacoinResponse, - addresses: mockWalletAddressesResponse, +function getDefaultMockWalletResponses( + mocks: ReturnType +) { + return { + fund: mocks.walletFundResponse, + outputsSiacoin: mocks.walletOutputsSiacoinResponse, + addresses: mocks.walletAddressesResponse, + } } test('send siacoin with a seed wallet', async ({ page }) => { await mockApiDefaults({ page }) await login({ page }) + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic, receiveAddress, changeAddress } = mocks await createWallet({ page, newWallet, - seed, - responses: defaultMockWalletResponses, + mnemonic, + responses: getDefaultMockWalletResponses(mocks), }) await navigateToWallet({ page, wallet: newWallet }) await page.getByLabel('send').click() await fillComposeTransactionSiacoin({ page, - receiveAddress, - changeAddress, + receiveAddress: mocks.receiveAddress, + changeAddress: mocks.changeAddress, amount: '1', }) await expect(page.getByText('The wallet is currently unlocked')).toBeVisible() @@ -46,7 +45,7 @@ test('send siacoin with a seed wallet', async ({ page }) => { .getByRole('button', { name: 'Sign and broadcast transaction' }) .click() await expect( - page.getByText('Transaction successfully broadcasted') + page.getByText('Transaction successfully broadcast') ).toBeVisible() await expect(page.getByText(receiveAddress.slice(0, 5))).toBeVisible() await expect(page.getByText(changeAddress.slice(0, 5))).toBeVisible() @@ -58,16 +57,18 @@ test('errors if the input to sign is not found on the transaction', async ({ page, }) => { await mockApiDefaults({ page }) - const mockFundInvalid = cloneDeep(mockWalletFundResponse) + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic, receiveAddress, changeAddress } = mocks + const mockFundInvalid = mocks.walletFundResponse mockFundInvalid.transaction.siacoinInputs[0].parentID = 'scoid:bb3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb' await login({ page }) await createWallet({ page, newWallet, - seed, + mnemonic, responses: { - ...defaultMockWalletResponses, + ...getDefaultMockWalletResponses(mocks), fund: mockFundInvalid, }, }) @@ -91,7 +92,9 @@ test('errors if the input to sign is not found on the transaction', async ({ test('errors if the inputs matching utxo is not found', async ({ page }) => { await mockApiDefaults({ page }) - const mockOutputsInvalid = cloneDeep(mockWalletOutputsSiacoinResponse) + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic, receiveAddress, changeAddress } = mocks + const mockOutputsInvalid = mocks.walletOutputsSiacoinResponse mockOutputsInvalid.forEach((output) => { output.id = 'not the matching id' }) @@ -99,9 +102,9 @@ test('errors if the inputs matching utxo is not found', async ({ page }) => { await createWallet({ page, newWallet, - seed, + mnemonic: mnemonic, responses: { - ...defaultMockWalletResponses, + ...getDefaultMockWalletResponses(mocks), outputsSiacoin: mockOutputsInvalid, }, }) @@ -127,16 +130,18 @@ test('errors if the address is missing its index', async ({ page }) => { await mockApiDefaults({ page }) await login({ page }) - const mockAddressesInvalid = cloneDeep(mockWalletAddressesResponse) + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic, receiveAddress, changeAddress } = mocks + const mockAddressesInvalid = mocks.walletAddressesResponse mockAddressesInvalid.forEach((address) => { address.metadata.index = undefined }) await createWallet({ page, newWallet, - seed, + mnemonic: mnemonic, responses: { - ...defaultMockWalletResponses, + ...getDefaultMockWalletResponses(mocks), addresses: mockAddressesInvalid, }, }) @@ -162,16 +167,18 @@ test('errors if the address is missing its public key', async ({ page }) => { await mockApiDefaults({ page }) await login({ page }) - const mockAddressesInvalid = cloneDeep(mockWalletAddressesResponse) + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic, receiveAddress, changeAddress } = mocks + const mockAddressesInvalid = mocks.walletAddressesResponse mockAddressesInvalid.forEach((address) => { - address.metadata.publicKey = undefined + address.metadata.unlockConditions.publicKeys[0] = undefined }) await createWallet({ page, newWallet, - seed, + mnemonic: mnemonic, responses: { - ...defaultMockWalletResponses, + ...getDefaultMockWalletResponses(mocks), addresses: mockAddressesInvalid, }, }) diff --git a/apps/walletd-e2e/src/specs/walletCreate.spec.ts b/apps/walletd-e2e/src/specs/walletCreate.spec.ts index 929309078..a8b4c54f4 100644 --- a/apps/walletd-e2e/src/specs/walletCreate.spec.ts +++ b/apps/walletd-e2e/src/specs/walletCreate.spec.ts @@ -1,28 +1,17 @@ import { test, expect } from '@playwright/test' -import { mockApiDefaults } from '../mocks/mock' import { login } from '../fixtures/login' import { createWallet } from '../fixtures/createWallet' - -const seed = - 'credit tonight gauge army noodle reopen pepper property try mom taste solid' - -const newWallet = { - id: '100', - name: 'new wallet 1', - description: 'wallet description', - dateCreated: new Date().toISOString(), - lastUpdated: new Date().toISOString(), - metadata: { - type: 'seed', - seedHash: - '9fe676742eb80d61fea4476e2d33a088d708a24c2762fa028f9bfbe693612d15b83223f7ba5bc2e0fe2390d243a47a18144f19acd34b105757b1ac7751da821f', - }, -} +import { + getMockScenarioSeedWallet, + mockApiDefaults, +} from '@siafoundation/mocks' test('wallet create', async ({ page }) => { + const mocks = getMockScenarioSeedWallet() + const { newWallet, mnemonic } = mocks await mockApiDefaults({ page }) await login({ page }) - await createWallet({ page, newWallet, seed }) + await createWallet({ page, newWallet, mnemonic }) await expect(page.locator(`span:text('${newWallet.name}')`)).toBeVisible() await page.locator(`span:text('${newWallet.name}')`).click() await expect(page.getByText('The wallet has no')).toBeVisible() diff --git a/apps/walletd/components/Node/index.tsx b/apps/walletd/components/Node/index.tsx index b57f2392c..84e6018ac 100644 --- a/apps/walletd/components/Node/index.tsx +++ b/apps/walletd/components/Node/index.tsx @@ -29,7 +29,7 @@ export function Node() { const transactionCount = txPool.data ? (txPool.data.transactions?.length || 0) + - (txPool.data.v2Transactions?.length || 0) + (txPool.data.v2transactions?.length || 0) : 0 return ( diff --git a/apps/walletd/contexts/addresses/dataset.tsx b/apps/walletd/contexts/addresses/dataset.tsx index 842dfd871..430543a06 100644 --- a/apps/walletd/contexts/addresses/dataset.tsx +++ b/apps/walletd/contexts/addresses/dataset.tsx @@ -2,10 +2,40 @@ import { useDatasetEmptyState, ClientFilterItem, } from '@siafoundation/design-system' -import { useWalletAddresses } from '@siafoundation/react-walletd' +import { + WalletAddressMetadata, + WalletAddressesResponse, + useWalletAddresses, +} from '@siafoundation/react-walletd' import { useMemo } from 'react' import { AddressData } from './types' -import { useDialog } from '../dialog' +import { OpenDialog, useDialog } from '../dialog' + +export function transformAddressesResponse( + response: WalletAddressesResponse, + walletId: string, + openDialog: OpenDialog +) { + const data: AddressData[] = response.map((addressObject) => { + const { address, description, metadata, spendPolicy } = addressObject + const datum: AddressData = { + id: address, + address, + description: description, + spendPolicy: spendPolicy, + metadata: (metadata || {}) as WalletAddressMetadata, + walletId, + onClick: () => + openDialog('addressUpdate', { + walletId: walletId, + address, + }), + raw: addressObject, + } + return datum + }) + return data +} export function useDataset({ walletId, @@ -21,27 +51,7 @@ export function useDataset({ if (!response.data) { return null } - const data: AddressData[] = response.data.map((addressObject) => { - const { address, description, metadata, spendPolicy } = addressObject - return { - id: address, - address, - description: description, - spendPolicy: spendPolicy, - metadata: { - index: metadata?.index as number, - publicKey: metadata?.publicKey as string, - }, - walletId, - onClick: () => - openDialog('addressUpdate', { - walletId: walletId, - address, - }), - raw: addressObject, - } - }) - return data + return transformAddressesResponse(response.data, walletId, openDialog) }, [response.data, openDialog, walletId]) const dataState = useDatasetEmptyState( diff --git a/apps/walletd/contexts/addresses/types.ts b/apps/walletd/contexts/addresses/types.ts index 8bdf60fdf..5fa488856 100644 --- a/apps/walletd/contexts/addresses/types.ts +++ b/apps/walletd/contexts/addresses/types.ts @@ -1,4 +1,7 @@ -import { WalletAddress } from '@siafoundation/react-walletd' +import { + WalletAddress, + WalletAddressMetadata, +} from '@siafoundation/react-walletd' export type CellContext = { siascanUrl: string @@ -9,12 +12,10 @@ export type AddressData = { address: string description?: string spendPolicy?: string - metadata: { - index?: number - publicKey?: string - } + metadata: WalletAddressMetadata walletId: string raw?: WalletAddress + onClick?: () => void } export type TableColumnId = 'actions' | 'address' | 'index' diff --git a/apps/walletd/contexts/dialog.tsx b/apps/walletd/contexts/dialog.tsx index b6d63c8f7..21c56d7f1 100644 --- a/apps/walletd/contexts/dialog.tsx +++ b/apps/walletd/contexts/dialog.tsx @@ -72,7 +72,7 @@ import { } from '../dialogs/WalletAddressesGenerateLedgerDialog' // import { CmdKDialog } from '../components/CmdKDialog' -type DialogParams = { +export type DialogParams = { cmdk?: void settings?: WalletdSettingsDialogParams walletSendSeed?: WalletSendSeedDialogParams @@ -95,13 +95,18 @@ type DialogParams = { walletUnlock?: WalletUnlockDialogParams } -type DialogType = keyof DialogParams +export type OpenDialog = ( + type: D, + params?: DialogParams[D] +) => void + +export type DialogType = keyof DialogParams function useDialogMain() { const [dialog, setDialog] = useState() const [params, setParams] = useState({}) - const openDialog = useCallback( + const openDialog = useCallback( (type: D, params?: DialogParams[D]) => { setParams((p) => ({ ...p, diff --git a/apps/walletd/contexts/wallets/index.tsx b/apps/walletd/contexts/wallets/index.tsx index 4ee5b5725..b6ae556b6 100644 --- a/apps/walletd/contexts/wallets/index.tsx +++ b/apps/walletd/contexts/wallets/index.tsx @@ -4,14 +4,16 @@ import { useClientFilters, useClientFilteredDataset, } from '@siafoundation/design-system' -import { useWallets as useWalletsData } from '@siafoundation/react-walletd' +import { + WalletMetadata, + useWallets as useWalletsData, +} from '@siafoundation/react-walletd' import { createContext, useContext, useEffect, useMemo } from 'react' import { WalletData, columnsDefaultVisible, defaultSortField, sortOptions, - WalletMetadata, } from './types' import { columns } from './columns' import { useRouter } from 'next/router' @@ -33,9 +35,10 @@ function useWalletsMain() { const { openDialog } = useDialog() const { setOnLockCallback } = useAppSettings() const { - seedCache, + mnemonicCache, walletActivityAt, - saveWalletSeed, + cacheWalletMnemonic, + cachedMnemonicCount, lockAllWallets, walletAutoLockTimeout, setWalletAutoLockTimeout, @@ -57,22 +60,29 @@ function useWalletsMain() { const data: WalletData[] = response.data.map((wallet) => { const { id, name, description, dateCreated, lastUpdated, metadata } = wallet - return { + const datum: WalletData = { + // Transformed data from the API response id, name, description, createdAt: new Date(dateCreated).getTime() || 0, updatedAt: new Date(lastUpdated).getTime() || 0, metadata: (metadata || {}) as WalletMetadata, + // Copy of the original data for merging into PUT updates + raw: wallet, + // State is data that is not persisted in the database, + // rather it is from the temporary session caches state: { - seed: seedCache[id], - status: seedCache[id] ? 'unlocked' : 'locked', + mnemonic: mnemonicCache[id], + status: mnemonicCache[id] ? 'unlocked' : 'locked', activityAt: walletActivityAt[id], }, + // Wallet methods actions: { unlock: () => openDialog('walletUnlock', { walletId: id }), - lock: () => saveWalletSeed(id, undefined), + lock: () => cacheWalletMnemonic(id, undefined), }, + // Table row click handler onClick: () => router.push({ pathname: routes.wallet.view, @@ -80,17 +90,17 @@ function useWalletsMain() { id, }, }), - raw: wallet, } + return datum }) return data }, [ router, response.data, - seedCache, + mnemonicCache, walletActivityAt, openDialog, - saveWalletSeed, + cacheWalletMnemonic, ]) const wallet = dataset?.find((w) => w.id === (router.query.id as string)) @@ -152,7 +162,7 @@ function useWalletsMain() { dataState, error: response.error, datasetCount: datasetFiltered?.length || 0, - unlockedCount: dataset?.filter((d) => d.state.seed).length || 0, + unlockedCount: cachedMnemonicCount, columns: filteredTableColumns, dataset: datasetFiltered, context, @@ -174,7 +184,7 @@ function useWalletsMain() { resetFilters, sortDirection, resetDefaultColumnVisibility, - saveWalletSeed, + cacheWalletMnemonic, lockAllWallets, walletAutoLockTimeout, setWalletAutoLockTimeout, diff --git a/apps/walletd/contexts/wallets/types.ts b/apps/walletd/contexts/wallets/types.ts index 5a8643450..7afdc3782 100644 --- a/apps/walletd/contexts/wallets/types.ts +++ b/apps/walletd/contexts/wallets/types.ts @@ -1,11 +1,4 @@ -import { Wallet } from '@siafoundation/react-walletd' - -export type WalletType = 'seed' | 'watch' | 'ledger' - -export type WalletMetadata = { - type: WalletType - seedHash?: string -} +import { Wallet, WalletMetadata } from '@siafoundation/react-walletd' export type WalletData = { id: string @@ -15,7 +8,7 @@ export type WalletData = { updatedAt?: number metadata: WalletMetadata state: { - seed?: string + mnemonic?: string activityAt?: number status: 'unlocked' | 'locked' } @@ -24,6 +17,7 @@ export type WalletData = { lock: () => void } raw?: Wallet + onClick?: () => void } export type TableColumnId = diff --git a/apps/walletd/contexts/wallets/useWalletSeedCache.ts b/apps/walletd/contexts/wallets/useWalletSeedCache.ts index 9e9e3185e..941a2ed72 100644 --- a/apps/walletd/contexts/wallets/useWalletSeedCache.ts +++ b/apps/walletd/contexts/wallets/useWalletSeedCache.ts @@ -17,8 +17,13 @@ export function useWalletSeedCache() { const [walletActivityAt, setWalletActivityAt] = useState< Record >({}) - const [seedCache, _setSeedCache] = useState>({}) - const seedCount = useMemo(() => Object.keys(seedCache).length, [seedCache]) + const [mnemonicCache, _setMnemonicCache] = useState>( + {} + ) + const cachedMnemonicCount = useMemo( + () => Object.keys(mnemonicCache).length, + [mnemonicCache] + ) const updateWalletActivityAt = useCallback( (walletId: string) => { @@ -30,17 +35,17 @@ export function useWalletSeedCache() { [setWalletActivityAt] ) - const saveWalletSeed = useCallback( - (walletId: string, seed: string | undefined) => { - _setSeedCache((seeds) => ({ - ...seeds, - [walletId]: seed, + const cacheWalletMnemonic = useCallback( + (walletId: string, mnemonic: string | undefined) => { + _setMnemonicCache((mnemonics) => ({ + ...mnemonics, + [walletId]: mnemonic, })) - if (seed) { + if (mnemonic) { updateWalletActivityAt(walletId) } }, - [_setSeedCache, updateWalletActivityAt] + [_setMnemonicCache, updateWalletActivityAt] ) const evictStale = useCallback(() => { @@ -49,25 +54,25 @@ export function useWalletSeedCache() { } const now = new Date().getTime() const ago = now - walletAutoLockTimeout - for (const [walletId, seed] of Object.entries(seedCache)) { - if (seed) { + for (const [walletId, mnemonic] of Object.entries(mnemonicCache)) { + if (mnemonic) { const timestamp = walletActivityAt[walletId] || 0 if (timestamp < ago) { - saveWalletSeed(walletId, undefined) + cacheWalletMnemonic(walletId, undefined) } } } }, [ - seedCache, + mnemonicCache, walletActivityAt, - saveWalletSeed, + cacheWalletMnemonic, walletAutoLockTimeout, walletAutoLockEnabled, ]) const lockAllWallets = useCallback(() => { - _setSeedCache({}) - }, [_setSeedCache]) + _setMnemonicCache({}) + }, [_setMnemonicCache]) const router = useRouter() const onAction = useCallback(() => { @@ -102,10 +107,10 @@ export function useWalletSeedCache() { return { walletActivityAt, updateWalletActivityAt, - seedCache, - saveWalletSeed, + mnemonicCache, + cacheWalletMnemonic, lockAllWallets, - seedCount, + cachedMnemonicCount, walletAutoLockTimeout, setWalletAutoLockTimeout, setWalletAutoLockEnabled, diff --git a/apps/walletd/dialogs/FieldMnemonic.tsx b/apps/walletd/dialogs/FieldMnemonic.tsx index e7b0a0e24..f9ecb9649 100644 --- a/apps/walletd/dialogs/FieldMnemonic.tsx +++ b/apps/walletd/dialogs/FieldMnemonic.tsx @@ -19,7 +19,7 @@ export function FieldMnemonic< >({ walletId, name, form, fields, actionText }: Props) { const { dataset, walletAutoLockEnabled, walletAutoLockTimeout } = useWallets() const wallet = dataset?.find((w) => w.id === walletId) - const cachedSeed = wallet?.state.seed + const cachedSeed = wallet?.state.mnemonic const isSeedCached = !!cachedSeed const cacheTime = humanTimeAndUnits(walletAutoLockTimeout) const fullActionText = actionText ? ` and ${actionText}` : '' diff --git a/apps/walletd/dialogs/WalletAddLedgerDialog/index.tsx b/apps/walletd/dialogs/WalletAddLedgerDialog/index.tsx index 6527929a8..8a7ee8b8a 100644 --- a/apps/walletd/dialogs/WalletAddLedgerDialog/index.tsx +++ b/apps/walletd/dialogs/WalletAddLedgerDialog/index.tsx @@ -12,12 +12,19 @@ import { } from '@siafoundation/design-system' import { useCallback, useEffect } from 'react' import { useForm } from 'react-hook-form' -import { useWalletAdd, useWalletAddressAdd } from '@siafoundation/react-walletd' +import { + WalletAddressMetadata, + WalletMetadata, + useWalletAdd, + useWalletAddressAdd, +} from '@siafoundation/react-walletd' import { useDialog } from '../../contexts/dialog' import { useWallets } from '../../contexts/wallets' import { walletAddTypes } from '../../config/walletTypes' import { DeviceConnectForm } from '../DeviceConnectForm' import { useLedger } from '../../contexts/ledger' +import { UnlockConditions } from '@siafoundation/types' +import { getSDK } from '@siafoundation/sdk' const defaultValues = { name: '', @@ -124,8 +131,15 @@ export function WalletAddLedgerDialog({ trigger, open, onOpenChange }: Props) { const addAddress0 = useCallback( async ( walletId: string, - { publicKey, address }: { publicKey: string; address: string } + { + unlockConditions, + address, + }: { unlockConditions: UnlockConditions; address: string } ) => { + const metadata: WalletAddressMetadata = { + index: 0, + unlockConditions, + } const response = await addressAdd.put({ params: { id: walletId, @@ -133,10 +147,8 @@ export function WalletAddLedgerDialog({ trigger, open, onOpenChange }: Props) { payload: { address, description: '', - metadata: { - index: 0, - publicKey, - }, + // TODO: add spendPolicy? + metadata, }, }) if (response.error) { @@ -155,24 +167,29 @@ export function WalletAddLedgerDialog({ trigger, open, onOpenChange }: Props) { if (!device.publicKey0 || !device.address0) { return } + // TODO: ledger save as unlockConditions? + const metadata: WalletMetadata = { + type: 'ledger', + publicKey0: device.publicKey0, + address0: device.address0, + } const response = await walletAdd.post({ payload: { name: values.name, description: values.description, - metadata: { - type: 'ledger', - publicKey0: device.publicKey0, - address0: device.address0, - }, + metadata, }, }) if (response.error) { triggerErrorToast(response.error) } else { - addAddress0(response.data.id, { - publicKey: device.publicKey0, - address: device.address0, - }) + const uc = getSDK().wallet.standardUnlockConditions(device.publicKey0) + if (!uc.error) { + addAddress0(response.data.id, { + unlockConditions: uc.unlockConditions, + address: device.address0, + }) + } openDialog('walletLedgerAddressGenerate', { walletId: response.data.id, walletJustCreated: true, diff --git a/apps/walletd/dialogs/WalletAddNewDialog/index.tsx b/apps/walletd/dialogs/WalletAddNewDialog/index.tsx index 8508a4156..3e19b9e1c 100644 --- a/apps/walletd/dialogs/WalletAddNewDialog/index.tsx +++ b/apps/walletd/dialogs/WalletAddNewDialog/index.tsx @@ -13,14 +13,14 @@ import { import { Redo16, Copy16 } from '@siafoundation/react-icons' import { MouseEvent, useCallback, useEffect } from 'react' import { useForm } from 'react-hook-form' -import { useWalletAdd } from '@siafoundation/react-walletd' +import { WalletMetadata, useWalletAdd } from '@siafoundation/react-walletd' import { useDialog } from '../../contexts/dialog' import { useWallets } from '../../contexts/wallets' import { walletAddTypes } from '../../config/walletTypes' import { blake2bHex } from 'blakejs' import { SeedLayout } from '../SeedLayout' import { SeedIcon } from '@siafoundation/react-icons' -import { getWalletWasm } from '../../lib/wasm' +import { getSDK } from '@siafoundation/sdk' const defaultValues = { name: '', @@ -74,7 +74,7 @@ function getFields({ required: 'required', validate: { valid: (value: string) => { - const { error } = getWalletWasm().seedFromPhrase(value) + const { error } = getSDK().wallet.keyPairFromSeedPhrase(value, 0) return !error || 'seed should be 12 word BIP39 mnemonic' }, copied: (_, values) => values.hasCopied || 'Copy seed to continue', @@ -118,7 +118,7 @@ export function WalletAddNewDialog({ trigger, open, onOpenChange }: Props) { }, [mnemonic, form]) const regenerateMnemonic = useCallback(async () => { - const { phrase } = getWalletWasm().newSeedPhrase() + const { phrase } = getSDK().wallet.generateSeedPhrase() form.setValue('hasCopied', false) form.setValue('mnemonic', phrase) form.clearErrors(['hasCopied', 'mnemonic']) @@ -138,16 +138,16 @@ export function WalletAddNewDialog({ trigger, open, onOpenChange }: Props) { const onSubmit = useCallback( async (values: Values) => { - const { seed } = getWalletWasm().seedFromPhrase(values.mnemonic) - const seedHash = blake2bHex(seed) + const mnemonicHash = blake2bHex(values.mnemonic) + const metadata: WalletMetadata = { + type: 'seed', + mnemonicHash, + } const response = await walletAdd.post({ payload: { name: values.name, description: values.description, - metadata: { - type: 'seed', - seedHash, - }, + metadata, }, }) if (response.error) { diff --git a/apps/walletd/dialogs/WalletAddRecoverDialog/index.tsx b/apps/walletd/dialogs/WalletAddRecoverDialog/index.tsx index 3a4dd87c8..dc1172e8f 100644 --- a/apps/walletd/dialogs/WalletAddRecoverDialog/index.tsx +++ b/apps/walletd/dialogs/WalletAddRecoverDialog/index.tsx @@ -12,12 +12,12 @@ import { useCallback } from 'react' import { useForm } from 'react-hook-form' import { useDialog } from '../../contexts/dialog' import { useWallets } from '../../contexts/wallets' -import { useWalletAdd } from '@siafoundation/react-walletd' +import { WalletMetadata, useWalletAdd } from '@siafoundation/react-walletd' import { blake2bHex } from 'blakejs' import { SeedLayout } from '../SeedLayout' import { SeedIcon } from '@siafoundation/react-icons' import { walletAddTypes } from '../../config/walletTypes' -import { getWalletWasm } from '../../lib/wasm' +import { getSDK } from '@siafoundation/sdk' const defaultValues = { name: '', @@ -63,7 +63,7 @@ function getFields({ required: 'required', validate: { valid: (value: string) => { - const { error } = getWalletWasm().seedFromPhrase(value) + const { error } = getSDK().wallet.keyPairFromSeedPhrase(value, 0) return !error || 'seed should be 12 word BIP39 mnemonic' }, }, @@ -95,16 +95,16 @@ export function WalletAddRecoverDialog({ trigger, open, onOpenChange }: Props) { const onSubmit = useCallback( async (values: Values) => { - const { seed } = getWalletWasm().seedFromPhrase(values.mnemonic) - const seedHash = blake2bHex(seed) + const mnemonicHash = blake2bHex(values.mnemonic) + const metadata: WalletMetadata = { + type: 'seed', + mnemonicHash, + } const response = await walletAdd.post({ payload: { name: values.name, description: values.description, - metadata: { - type: 'seed', - seedHash, - }, + metadata, }, }) if (response.error) { diff --git a/apps/walletd/dialogs/WalletAddWatchDialog/index.tsx b/apps/walletd/dialogs/WalletAddWatchDialog/index.tsx index c59d91d74..7c5033663 100644 --- a/apps/walletd/dialogs/WalletAddWatchDialog/index.tsx +++ b/apps/walletd/dialogs/WalletAddWatchDialog/index.tsx @@ -11,7 +11,7 @@ import { } from '@siafoundation/design-system' import { useCallback } from 'react' import { useForm } from 'react-hook-form' -import { useWalletAdd } from '@siafoundation/react-walletd' +import { WalletMetadata, useWalletAdd } from '@siafoundation/react-walletd' import { useDialog } from '../../contexts/dialog' import { useWallets } from '../../contexts/wallets' import { walletAddTypes } from '../../config/walletTypes' @@ -77,13 +77,14 @@ export function WalletAddWatchDialog({ trigger, open, onOpenChange }: Props) { const onSubmit = useCallback( async (values: Values) => { + const metadata: WalletMetadata = { + type: 'watch', + } const response = await walletAdd.post({ payload: { name: values.name, description: values.description, - metadata: { - type: 'watch', - }, + metadata, }, }) if (response.error) { diff --git a/apps/walletd/dialogs/WalletAddressesAddDialog.tsx b/apps/walletd/dialogs/WalletAddressesAddDialog.tsx index d1ca5747e..d334f3266 100644 --- a/apps/walletd/dialogs/WalletAddressesAddDialog.tsx +++ b/apps/walletd/dialogs/WalletAddressesAddDialog.tsx @@ -11,6 +11,7 @@ import { useDialogFormHelpers, } from '@siafoundation/design-system' import { + WalletAddressMetadata, useResubscribe, useWalletAddressAdd, } from '@siafoundation/react-walletd' @@ -122,6 +123,7 @@ export function WalletAddressesAddDialog({ let successful = 0 for (let i = 0; i < total; i++) { const addr = addrs[i] + const metadata: WalletAddressMetadata = {} const response = await addressAdd.put({ params: { id: walletId, @@ -129,7 +131,7 @@ export function WalletAddressesAddDialog({ payload: { address: addr, description: '', - metadata: {}, + metadata, }, }) if (response.error) { diff --git a/apps/walletd/dialogs/WalletAddressesGenerateLedgerDialog/index.tsx b/apps/walletd/dialogs/WalletAddressesGenerateLedgerDialog/index.tsx index 19dd77219..877f658c7 100644 --- a/apps/walletd/dialogs/WalletAddressesGenerateLedgerDialog/index.tsx +++ b/apps/walletd/dialogs/WalletAddressesGenerateLedgerDialog/index.tsx @@ -11,7 +11,10 @@ import { triggerSuccessToast, useDialogFormHelpers, } from '@siafoundation/design-system' -import { useWalletAddressAdd } from '@siafoundation/react-walletd' +import { + WalletAddressMetadata, + useWalletAddressAdd, +} from '@siafoundation/react-walletd' import { useCallback, useEffect, useMemo, useState } from 'react' import { useForm } from 'react-hook-form' import { useWallets } from '../../contexts/wallets' @@ -20,6 +23,7 @@ import { DeviceConnectForm } from '../DeviceConnectForm' import { useLedger } from '../../contexts/ledger' import { LedgerAddress } from './LedgerAddress' import { useWalletAddresses } from '../../hooks/useWalletAddresses' +import { getSDK } from '@siafoundation/sdk' export type WalletAddressesGenerateLedgerDialogParams = { walletId: string @@ -222,7 +226,8 @@ export function WalletAddressesGenerateLedgerDialog({ isNew: !existing, index: Number(index), address: existing?.address || address, - publicKey: existing?.metadata.publicKey || publicKey, + publicKey: + existing?.metadata.unlockConditions.publicKeys[0] || publicKey, } } return indiciesWithAddresses @@ -238,10 +243,30 @@ export function WalletAddressesGenerateLedgerDialog({ const saveAddresses = useCallback(async () => { const count = newGeneratedAddresses.length + function toastBatchError(count: number, i: number) { + if (count === 1) { + triggerErrorToast('Error saving address.') + } else { + triggerErrorToast( + `Error saving addresses. ${ + i > 0 ? 'Not all addresses were saved.' : '' + }` + ) + } + } for (const [ i, { address, publicKey, index }, ] of newGeneratedAddresses.entries()) { + const uc = getSDK().wallet.standardUnlockConditions(publicKey) + if (uc.error) { + toastBatchError(count, i) + return + } + const metadata: WalletAddressMetadata = { + index, + unlockConditions: uc.unlockConditions, + } const response = await addressAdd.put({ params: { id: walletId, @@ -249,22 +274,12 @@ export function WalletAddressesGenerateLedgerDialog({ payload: { address, description: '', - metadata: { - index, - publicKey, - }, + // TODO: add spendPolicy? + metadata, }, }) if (response.error) { - if (count === 1) { - triggerErrorToast('Error saving address.') - } else { - triggerErrorToast( - `Error saving addresses. ${ - i > 0 ? 'Not all addresses were saved.' : '' - }` - ) - } + toastBatchError(count, i) return } } diff --git a/apps/walletd/dialogs/WalletAddressesGenerateSeedDialog/index.tsx b/apps/walletd/dialogs/WalletAddressesGenerateSeedDialog/index.tsx index 4e1ec85c4..8a0ee81ef 100644 --- a/apps/walletd/dialogs/WalletAddressesGenerateSeedDialog/index.tsx +++ b/apps/walletd/dialogs/WalletAddressesGenerateSeedDialog/index.tsx @@ -8,16 +8,18 @@ import { triggerSuccessToast, useDialogFormHelpers, } from '@siafoundation/design-system' -import { useWalletAddressAdd } from '@siafoundation/react-walletd' +import { + WalletAddressMetadata, + useWalletAddressAdd, +} from '@siafoundation/react-walletd' import { useCallback, useEffect, useState } from 'react' import { useForm } from 'react-hook-form' -import { getWalletWasm } from '../../lib/wasm' import { useWallets } from '../../contexts/wallets' import BigNumber from 'bignumber.js' import { getFieldMnemonic, MnemonicFieldType } from '../../lib/fieldMnemonic' import { FieldMnemonic } from '../FieldMnemonic' -import { useWalletCachedSeed } from '../../hooks/useWalletCachedSeed' import { useWalletAddresses } from '../../hooks/useWalletAddresses' +import { getSDK } from '@siafoundation/sdk' export type WalletAddressesGenerateSeedDialogParams = { walletId: string @@ -39,17 +41,17 @@ function getDefaultValues(lastIndex: number) { } function getFields({ - seedHash, + mnemonicHash, mnemonicFieldType, setMnemonicFieldType, }: { - seedHash?: string + mnemonicHash?: string mnemonicFieldType: MnemonicFieldType setMnemonicFieldType: (type: MnemonicFieldType) => void }): ConfigFields, never> { return { mnemonic: getFieldMnemonic({ - seedHash, + mnemonicHash, setMnemonicFieldType, mnemonicFieldType, }), @@ -83,7 +85,7 @@ export function WalletAddressesGenerateSeedDialog({ }: Props) { const { walletId } = params || {} const { lastIndex } = useWalletAddresses({ id: walletId }) - const { dataset, saveWalletSeed } = useWallets() + const { dataset, cacheWalletMnemonic } = useWallets() const wallet = dataset?.find((w) => w.id === walletId) const nextIndex = lastIndex + 1 const defaultValues = getDefaultValues(nextIndex) @@ -113,39 +115,43 @@ export function WalletAddressesGenerateSeedDialog({ const count = form.watch('count') const fields = getFields({ - seedHash: wallet?.metadata.seedHash, + mnemonicHash: wallet?.metadata.mnemonicHash, mnemonicFieldType, setMnemonicFieldType, }) const addressAdd = useWalletAddressAdd() - const { getSeedFromCacheOrForm } = useWalletCachedSeed(walletId) const generateAddresses = useCallback( async (mnemonic: string, index: number, count: number) => { - const seedResponse = getSeedFromCacheOrForm({ mnemonic }) - if (seedResponse.error) { - triggerErrorToast(seedResponse.error) - return - } - const { seed } = seedResponse for (let i = index; i < index + count; i++) { - const pkResponse = getWalletWasm().publicKeyAndAddressFromSeed(seed, i) - if (pkResponse.error) { + const kp = getSDK().wallet.keyPairFromSeedPhrase(mnemonic, i) + if (kp.error) { triggerErrorToast('Error generating addresses.') return } + const suh = getSDK().wallet.standardUnlockHash(kp.publicKey) + if (suh.error) { + triggerErrorToast('Error generating unlock hash.') + return + } + const uc = getSDK().wallet.standardUnlockConditions(kp.publicKey) + if (uc.error) { + triggerErrorToast('Error generating unlock conditions.') + return + } + const metadata: WalletAddressMetadata = { + index: i, + unlockConditions: uc.unlockConditions, + } const response = await addressAdd.put({ params: { id: walletId, }, payload: { - address: pkResponse.address, + address: suh.address, description: '', - // spendPolicy: '', // TODO: use sdk and replace - metadata: { - index: i, - publicKey: pkResponse.publicKey, - }, + // TODO: add spendPolicy? + metadata, }, }) if (response.error) { @@ -168,22 +174,20 @@ export function WalletAddressesGenerateSeedDialog({ } // if successfully generated an address, cache the seed - saveWalletSeed(walletId, seed) + cacheWalletMnemonic(walletId, mnemonic) closeAndReset() }, - [ - getSeedFromCacheOrForm, - closeAndReset, - addressAdd, - walletId, - saveWalletSeed, - ] + [closeAndReset, addressAdd, walletId, cacheWalletMnemonic] ) const onSubmit = useCallback(() => { - return generateAddresses(mnemonic, index.toNumber(), count.toNumber()) - }, [generateAddresses, mnemonic, index, count]) + return generateAddresses( + wallet.state.mnemonic || mnemonic, + index.toNumber(), + count.toNumber() + ) + }, [generateAddresses, mnemonic, index, count, wallet]) return ( void isSeedCached: boolean -}): ConfigFields { +}): ConfigFields { return { mnemonic: isSeedCached ? { @@ -42,7 +44,7 @@ function getFields({ validation: {}, } : getFieldMnemonic({ - seedHash, + mnemonicHash, setMnemonicFieldType, mnemonicFieldType, }), @@ -51,10 +53,10 @@ function getFields({ export function useSendForm({ walletId, params, onConfirm }: Props) { const signAndBroadcast = useSignAndBroadcast() - const { isSeedCached, getSeedFromCacheOrForm } = useWalletCachedSeed(walletId) + const { isSeedCached } = useWalletCachedSeed(walletId) const { dataset } = useWallets() const wallet = dataset?.find((w) => w.id === walletId) - const seedHash = wallet?.metadata.seedHash + const mnemonicHash = wallet?.metadata.mnemonicHash const form = useForm({ mode: 'all', @@ -68,22 +70,16 @@ export function useSendForm({ walletId, params, onConfirm }: Props) { getFields({ mnemonicFieldType, setMnemonicFieldType, - seedHash, + mnemonicHash, isSeedCached, }), - [mnemonicFieldType, setMnemonicFieldType, seedHash, isSeedCached] + [mnemonicFieldType, setMnemonicFieldType, mnemonicHash, isSeedCached] ) const onValid = useCallback( - async (values: typeof defaultValues) => { - const seedResponse = getSeedFromCacheOrForm(values) - if (seedResponse.error) { - triggerErrorToast(seedResponse.error) - return - } - + async (values: Values) => { const { error } = await signAndBroadcast({ - seed: seedResponse.seed, + mnemonic: wallet.state.mnemonic || values.mnemonic, params, }) @@ -94,7 +90,7 @@ export function useSendForm({ walletId, params, onConfirm }: Props) { onConfirm({}) }, - [getSeedFromCacheOrForm, signAndBroadcast, params, onConfirm] + [signAndBroadcast, params, onConfirm, wallet] ) const onInvalid = useOnInvalid(fields) diff --git a/apps/walletd/dialogs/WalletSendSeedDialog/useSignAndBroadcast.tsx b/apps/walletd/dialogs/WalletSendSeedDialog/useSignAndBroadcast.tsx index fe86ab607..012272ca6 100644 --- a/apps/walletd/dialogs/WalletSendSeedDialog/useSignAndBroadcast.tsx +++ b/apps/walletd/dialogs/WalletSendSeedDialog/useSignAndBroadcast.tsx @@ -14,7 +14,7 @@ import { useCancel } from '../_sharedWalletSend/useCancel' import { useFund } from '../_sharedWalletSend/useFund' export function useSignAndBroadcast() { - const { wallet, saveWalletSeed } = useWallets() + const { wallet, cacheWalletMnemonic } = useWallets() const walletId = wallet?.id const siacoinOutputs = useWalletOutputsSiacoin({ @@ -37,7 +37,7 @@ export function useSignAndBroadcast() { const broadcast = useBroadcast({ cancel }) return useCallback( - async ({ seed, params }: { seed: string; params: SendParams }) => { + async ({ mnemonic, params }: { mnemonic: string; params: SendParams }) => { if (!addresses) { return { error: 'No addresses found', @@ -57,11 +57,11 @@ export function useSignAndBroadcast() { } } const { signedTransaction, error: signingError } = signTransactionSeed({ - seed, + mnemonic, transaction: fundedTransaction, toSign, - cs: cs.data, - cn: cn.data, + consensusState: cs.data, + consensusNetwork: cn.data, addresses, siacoinOutputs: siacoinOutputs.data, siafundOutputs: siafundOutputs.data, @@ -74,7 +74,7 @@ export function useSignAndBroadcast() { } // if successfully signed cache the seed - saveWalletSeed(walletId, seed) + cacheWalletMnemonic(walletId, mnemonic) // broadcast return broadcast({ signedTransaction }) @@ -88,7 +88,7 @@ export function useSignAndBroadcast() { cn.data, siacoinOutputs.data, siafundOutputs.data, - saveWalletSeed, + cacheWalletMnemonic, broadcast, ] ) diff --git a/apps/walletd/dialogs/WalletUnlockDialog.tsx b/apps/walletd/dialogs/WalletUnlockDialog.tsx index 6f4e63ce5..7058d54dc 100644 --- a/apps/walletd/dialogs/WalletUnlockDialog.tsx +++ b/apps/walletd/dialogs/WalletUnlockDialog.tsx @@ -8,7 +8,6 @@ import { } from '@siafoundation/design-system' import { useCallback, useState } from 'react' import { useForm } from 'react-hook-form' -import { getWalletWasm } from '../lib/wasm' import { useWallets } from '../contexts/wallets' import { getFieldMnemonic, MnemonicFieldType } from '../lib/fieldMnemonic' import { FieldMnemonic } from './FieldMnemonic' @@ -31,17 +30,17 @@ function getDefaultValues() { } function getFields({ - seedHash, + mnemonicHash, mnemonicFieldType, setMnemonicFieldType, }: { - seedHash?: string + mnemonicHash?: string mnemonicFieldType: MnemonicFieldType setMnemonicFieldType: (type: MnemonicFieldType) => void }): ConfigFields, never> { return { mnemonic: getFieldMnemonic({ - seedHash, + mnemonicHash, setMnemonicFieldType, mnemonicFieldType, }), @@ -55,7 +54,7 @@ export function WalletUnlockDialog({ onOpenChange, }: Props) { const { walletId } = params || {} - const { dataset, saveWalletSeed } = useWallets() + const { dataset, cacheWalletMnemonic } = useWallets() const wallet = dataset?.find((w) => w.id === walletId) const defaultValues = getDefaultValues() const [mnemonicFieldType, setMnemonicFieldType] = @@ -72,18 +71,17 @@ export function WalletUnlockDialog({ }) const fields = getFields({ - seedHash: wallet?.metadata.seedHash, + mnemonicHash: wallet?.metadata.mnemonicHash, mnemonicFieldType, setMnemonicFieldType, }) const onValid = useCallback( (values: typeof defaultValues) => { - const { seed } = getWalletWasm().seedFromPhrase(values.mnemonic) - saveWalletSeed(walletId, seed) + cacheWalletMnemonic(walletId, values.mnemonic) closeAndReset() }, - [walletId, saveWalletSeed, closeAndReset] + [walletId, cacheWalletMnemonic, closeAndReset] ) const onInvalid = useOnInvalid(fields) diff --git a/apps/walletd/dialogs/_sharedWalletSend/SendDone.tsx b/apps/walletd/dialogs/_sharedWalletSend/SendDone.tsx index 856a5d179..d1d1a408e 100644 --- a/apps/walletd/dialogs/_sharedWalletSend/SendDone.tsx +++ b/apps/walletd/dialogs/_sharedWalletSend/SendDone.tsx @@ -16,7 +16,7 @@ export function SendDone({ params, transactionId }: Props) { - Transaction successfully broadcasted. + Transaction successfully broadcast. ) diff --git a/apps/walletd/dialogs/_sharedWalletSend/useBroadcast.tsx b/apps/walletd/dialogs/_sharedWalletSend/useBroadcast.tsx index cfc6e3047..1af876046 100644 --- a/apps/walletd/dialogs/_sharedWalletSend/useBroadcast.tsx +++ b/apps/walletd/dialogs/_sharedWalletSend/useBroadcast.tsx @@ -16,7 +16,7 @@ export function useBroadcast({ cancel }: { cancel: (t: Transaction) => void }) { const broadcastResponse = await txPoolBroadcast.post({ payload: { transactions: [signedTransaction], - v2Transactions: [], + v2transactions: [], }, }) if (broadcastResponse.error) { diff --git a/apps/walletd/hooks/useWalletCachedSeed.tsx b/apps/walletd/hooks/useWalletCachedSeed.tsx index 3f1f309a5..cd2473155 100644 --- a/apps/walletd/hooks/useWalletCachedSeed.tsx +++ b/apps/walletd/hooks/useWalletCachedSeed.tsx @@ -1,27 +1,12 @@ -import { getWalletWasm } from '../lib/wasm' import { useWallets } from '../contexts/wallets' export function useWalletCachedSeed(walletId: string) { const { dataset } = useWallets() const wallet = dataset?.find((w) => w.id === walletId) - const cachedSeed = wallet?.state.seed - const isSeedCached = !!cachedSeed - - const getSeedFromCacheOrForm = ({ - mnemonic, - }: { - mnemonic: string - }): { seed?: string; error?: string } => { - if (isSeedCached) { - return { seed: cachedSeed } - } else { - return getWalletWasm().seedFromPhrase(mnemonic) - } - } - + const cachedMnemonic = wallet?.state.mnemonic + const isSeedCached = !!cachedMnemonic return { - cachedSeed, + cachedMnemonic, isSeedCached, - getSeedFromCacheOrForm, } } diff --git a/apps/walletd/jest.config.ts b/apps/walletd/jest.config.ts index 1e6c79b1c..4c8c45bc9 100644 --- a/apps/walletd/jest.config.ts +++ b/apps/walletd/jest.config.ts @@ -22,4 +22,5 @@ export default { 'next/dist/build/jest/__mocks__/nextFontMock.js' ), }, + setupFiles: ['./jest.polyfills.js'], } diff --git a/apps/walletd/jest.polyfills.js b/apps/walletd/jest.polyfills.js new file mode 100644 index 000000000..5cc3a0873 --- /dev/null +++ b/apps/walletd/jest.polyfills.js @@ -0,0 +1,31 @@ +// jest.polyfills.js +/** + * @note The block below contains polyfills for Node.js globals + * required for Jest to function when running JSDOM tests. + * These HAVE to be require's and HAVE to be in this exact + * order, since "undici" depends on the "TextEncoder" global API. + * + * Consider migrating to a more modern test runner if + * you don't want to deal with this. + */ + +const { TextDecoder, TextEncoder, ReadableStream } = require('node:util') + +Object.defineProperties(globalThis, { + TextDecoder: { value: TextDecoder }, + TextEncoder: { value: TextEncoder }, + ReadableStream: { value: ReadableStream }, +}) + +const { Blob, File } = require('node:buffer') +const { fetch, Headers, FormData, Request, Response } = require('undici') + +Object.defineProperties(globalThis, { + fetch: { value: fetch, writable: true }, + Blob: { value: Blob }, + File: { value: File }, + Headers: { value: Headers }, + FormData: { value: FormData }, + Request: { value: Request }, + Response: { value: Response }, +}) diff --git a/apps/walletd/lib/__snapshots__/signLedger.spec.ts.snap b/apps/walletd/lib/__snapshots__/signLedger.spec.ts.snap index 159a6a372..5bf802e79 100644 --- a/apps/walletd/lib/__snapshots__/signLedger.spec.ts.snap +++ b/apps/walletd/lib/__snapshots__/signLedger.spec.ts.snap @@ -8,39 +8,34 @@ Object { ], "siacoinInputs": Array [ Object { - "parentID": "scoid:b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5", + "parentID": "scoid:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb", "unlockConditions": Object { "publicKeys": Array [ - "ed25519:b5b9196a3c19f94982bcdba250a973181b22112437832a8f818f4aa73b8add74", + "ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535", ], "signaturesRequired": 1, "timelock": 0, }, }, - ], - "siacoinOutputs": Array [ - Object { - "address": "addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8", - "value": "95408980544305197274920800", - }, - ], - "siafundInputs": Array [ Object { - "claimAddress": "addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8", - "parentID": "sfoid:b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852", + "parentID": "scoid:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724", "unlockConditions": Object { "publicKeys": Array [ - "ed25519:8a7496aa59f17a4aae68c7e41e09d5ca94e64ba27f74cdb0b143f70dcc67b206", + "ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535", ], "signaturesRequired": 1, "timelock": 0, }, }, ], - "siafundOutputs": Array [ + "siacoinOutputs": Array [ + Object { + "address": "addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8", + "value": "1000000000000000000000000", + }, Object { - "address": "addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f", - "value": 1, + "address": "addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981", + "value": "97984280000000000000000000", }, ], "signatures": Array [ @@ -48,7 +43,7 @@ Object { "coveredFields": Object { "wholeTransaction": true, }, - "parentID": "b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852", + "parentID": "aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb", "publicKeyIndex": 0, "signature": "Xt1EJckLmWXU+7HHHDN9bRV5KRuLdC4YY01LzaAMF269QH4hWV8zFkY3kCWs65svhb9HhA1Ix1MRGvhN9orBDpAA", "timelock": 0, @@ -57,7 +52,7 @@ Object { "coveredFields": Object { "wholeTransaction": true, }, - "parentID": "b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5", + "parentID": "32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724", "publicKeyIndex": 0, "signature": "fvmSaRzlO/n2L5tsT32e82kWqHnIjQJ8cqjWOc37TtlK6p/vIiOG+TO98HfvbgObTOYVqlKMtUyxTOjGb3bfCpAA", "timelock": 0, diff --git a/apps/walletd/lib/__snapshots__/signSeed.spec.ts.snap b/apps/walletd/lib/__snapshots__/signSeed.spec.ts.snap index 0531bbcfa..35b1718d8 100644 --- a/apps/walletd/lib/__snapshots__/signSeed.spec.ts.snap +++ b/apps/walletd/lib/__snapshots__/signSeed.spec.ts.snap @@ -8,39 +8,34 @@ Object { ], "siacoinInputs": Array [ Object { - "parentID": "scoid:b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5", + "parentID": "scoid:aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb", "unlockConditions": Object { "publicKeys": Array [ - "ed25519:b5b9196a3c19f94982bcdba250a973181b22112437832a8f818f4aa73b8add74", + "ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535", ], "signaturesRequired": 1, "timelock": 0, }, }, - ], - "siacoinOutputs": Array [ - Object { - "address": "addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8", - "value": "95408980544305197274920800", - }, - ], - "siafundInputs": Array [ Object { - "claimAddress": "addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8", - "parentID": "sfoid:b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852", + "parentID": "scoid:32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724", "unlockConditions": Object { "publicKeys": Array [ - "ed25519:8a7496aa59f17a4aae68c7e41e09d5ca94e64ba27f74cdb0b143f70dcc67b206", + "ed25519:ee122b2169bdae5776b55609e384e0c58372cd5c529d4edc9b9918b26f8e5535", ], "signaturesRequired": 1, "timelock": 0, }, }, ], - "siafundOutputs": Array [ + "siacoinOutputs": Array [ + Object { + "address": "addr:90c6057cdd2463eca61f83796e83152dbba28b6cb9a74831a043833051ec9f422726bfff2ee8", + "value": "1000000000000000000000000", + }, Object { - "address": "addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f", - "value": 1, + "address": "addr:f2dbf56b5b0c698d7fbf43f646c76169d84e597e8b37fada97348beeecaa812d400ac4ce7981", + "value": "97984280000000000000000000", }, ], "signatures": Array [ @@ -48,17 +43,19 @@ Object { "coveredFields": Object { "wholeTransaction": true, }, - "parentID": "h:b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852", + "parentID": "aa3e781330c9b3991e0141807df1327fadf114ca6c37acb9e58004f942d91dfb", "publicKeyIndex": 0, - "signature": "26nxM6vdUBMuoEXzWxZ5bC6cXstOl2LhzWLbG5QsfNni7j+5+Ro1AjK5mLO5jBVpLpQ7cbUb6DaUDsIyKABDCQ==", + "signature": "+FW9B+8owhXpQbJ9IZ3Iz2AC9qRepOrnPvAYHv1fWaDag723cVJKmCimvXkg2sy0CHRq8SWONNC2neJ55ratDg==", + "timelock": 0, }, Object { "coveredFields": Object { "wholeTransaction": true, }, - "parentID": "h:b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5", + "parentID": "32e430158591b4073a6834e9f4c4b67162e348844f569f4e472896bb72efb724", "publicKeyIndex": 0, - "signature": "mNrsyxNfQN7WKm3JY17+o5kxvF42Ww6mODH5Ft6fdgEgjk3zOe8/pTJmJSvErQ+j/BOT7DzWVd1ETOplsZAcDg==", + "signature": "nwdpK6jp47lEE0waJjK92JHpni/xC7/ZheKbPu/tldxdHK3+8/GvH1Cpmp+LTJSfueXBWZ56vDdHDHdE13EDAg==", + "timelock": 0, }, ], }, diff --git a/apps/walletd/lib/fieldMnemonic.tsx b/apps/walletd/lib/fieldMnemonic.tsx index c2e870619..eeceb09f6 100644 --- a/apps/walletd/lib/fieldMnemonic.tsx +++ b/apps/walletd/lib/fieldMnemonic.tsx @@ -2,7 +2,7 @@ import { Button, ConfigField } from '@siafoundation/design-system' import { View16, ViewOff16 } from '@siafoundation/react-icons' import { FieldValues } from 'react-hook-form' import { blake2bHex } from 'blakejs' -import { getWalletWasm } from './wasm' +import { getSDK } from '@siafoundation/sdk' export type MnemonicFieldType = 'text' | 'password' @@ -10,11 +10,11 @@ export function getFieldMnemonic< Values extends FieldValues, Categories extends string >({ - seedHash, + mnemonicHash, mnemonicFieldType: mnemonicType, setMnemonicFieldType: setMnemonicType, }: { - seedHash?: string + mnemonicHash?: string mnemonicFieldType: MnemonicFieldType setMnemonicFieldType: (type: MnemonicFieldType) => void }): ConfigField { @@ -42,17 +42,17 @@ export function getFieldMnemonic< required: 'required', validate: { valid: (value: string) => { - const { error } = getWalletWasm().seedFromPhrase(value) + const { error } = getSDK().wallet.keyPairFromSeedPhrase(value, 0) return !error || 'seed should be 12 word BIP39 mnemonic' }, match: (mnemonic: string) => { - const { seed } = getWalletWasm().seedFromPhrase(mnemonic) return ( - blake2bHex(seed) === seedHash || 'seed does not match' + blake2bHex(mnemonic) === mnemonicHash || + 'seed phrase does not match' // Maybe re-enabled this so that wallets added via daemon can pass - // validation. Would need to add seedHash to metadata afterwards. + // validation. Would need to add mnemonicHash to metadata afterwards. // Potential issues if wrong valid seed is entered. - // !seedHash || blake2bHex(seed) === seedHash || 'seed does not match' + // !mnemonicHash || blake2bHex(seed) === mnemonicHash || 'seed does not match' ) }, }, diff --git a/apps/walletd/lib/sign.ts b/apps/walletd/lib/sign.ts index 18e803d26..ec4e54962 100644 --- a/apps/walletd/lib/sign.ts +++ b/apps/walletd/lib/sign.ts @@ -53,21 +53,11 @@ export function addUnlockConditionsAndSignatures({ } if (siacoinUtxo) { - // build the unlock conditions with the utxo funding element's public key - siacoinInput.unlockConditions = { - timelock: 0, - publicKeys: [address.metadata.publicKey], - signaturesRequired: 1, - } + siacoinInput.unlockConditions = address.metadata.unlockConditions } if (siafundUtxo) { - // build the unlock conditions with the utxo funding element's public key - siafundInput.unlockConditions = { - timelock: 0, - publicKeys: [address.metadata.publicKey], - signaturesRequired: 1, - } + siafundInput.unlockConditions = address.metadata.unlockConditions } if (!transaction.signatures) { @@ -114,10 +104,13 @@ export function getSiacoinUtxoAndAddress({ if (!addressData) { return { error: 'Missing address' } } + if (!addressData.metadata) { + return { error: 'Missing address metadata' } + } if (addressData.metadata.index === undefined) { return { error: 'Missing address index' } } - if (!addressData.metadata.publicKey) { + if (!addressData.metadata.unlockConditions.publicKeys[0]) { return { error: 'Missing address public key' } } @@ -153,10 +146,13 @@ export function getSiafundUtxoAndAddress({ if (!addressData) { return { error: 'Missing address' } } + if (!addressData.metadata) { + return { error: 'Missing address metadata' } + } if (addressData.metadata.index === undefined) { return { error: 'Missing address index' } } - if (!addressData.metadata.publicKey) { + if (!addressData.metadata.unlockConditions.publicKeys[0]) { return { error: 'Missing address public key' } } diff --git a/apps/walletd/lib/signLedger.spec.ts b/apps/walletd/lib/signLedger.spec.ts index f803acd96..d88962d17 100644 --- a/apps/walletd/lib/signLedger.spec.ts +++ b/apps/walletd/lib/signLedger.spec.ts @@ -1,45 +1,40 @@ +import { initSDK } from '@siafoundation/sdk' import { signTransactionLedger } from './signLedger' -import { TextEncoder, TextDecoder } from 'util' -import { loadWASMTestEnv } from './wasmTestEnv' -import { - getMockDevice, - getAddresses, - getSiacoinOutputs, - getSiafundOutputs, - getToSign, - getTransaction, -} from './testMocks' -global.TextEncoder = TextEncoder -global.TextDecoder = TextDecoder +import { getMockAddresses, getMockDevice } from './testMocks' +import { getMockScenarioSeedWallet } from '@siafoundation/mocks' + +beforeEach(async () => { + await initSDK() +}) describe('signLedger', () => { describe('siacoin', () => { it('builds and signs valid transaction', async () => { - await loadWASMTestEnv() const device = getMockDevice() + const mocks = getMockScenarioSeedWallet() expect( await signTransactionLedger({ device, - transaction: getTransaction(), - toSign: getToSign(), - addresses: getAddresses(), - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, + addresses: getMockAddresses(mocks), }) ).toMatchSnapshot() }) it('errors when a toSign utxo is missing', async () => { - await loadWASMTestEnv() const device = getMockDevice() + const mocks = getMockScenarioSeedWallet() expect( await signTransactionLedger({ device, - transaction: getTransaction(), - toSign: [getToSign()[0], 'not in siacoinOutputs'], - addresses: getAddresses(), - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), + transaction: mocks.walletFundResponse.transaction, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, + addresses: getMockAddresses(mocks), + toSign: [mocks.walletFundResponse.toSign[0], 'not in siacoinOutputs'], }) ).toEqual({ error: 'Missing utxo', @@ -47,13 +42,15 @@ describe('signLedger', () => { }) it('errors when a public keys address is missing', async () => { - await loadWASMTestEnv() const device = getMockDevice() + const mocks = getMockScenarioSeedWallet() expect( await signTransactionLedger({ device, - transaction: getTransaction(), - toSign: getToSign(), + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, addresses: [ { id: 'id', @@ -64,8 +61,6 @@ describe('signLedger', () => { }, }, ], - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing address', @@ -73,23 +68,24 @@ describe('signLedger', () => { }) it('errors when an address is missing its index', async () => { - await loadWASMTestEnv() const device = getMockDevice() + const mocks = getMockScenarioSeedWallet() expect( await signTransactionLedger({ device, - transaction: getTransaction(), - toSign: getToSign(), + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, addresses: [ { id: 'id', walletId: 'id', - address: getSiacoinOutputs()[1].siacoinOutput.address, + address: + mocks.walletOutputsSiacoinResponse[1].siacoinOutput.address, metadata: {}, }, ], - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing address index', @@ -97,18 +93,18 @@ describe('signLedger', () => { }) it('errors when an address is missing its public key', async () => { - await loadWASMTestEnv() const device = getMockDevice() - const addresses = getAddresses() - addresses[0].metadata.publicKey = undefined + const mocks = getMockScenarioSeedWallet() + const addresses = getMockAddresses(mocks) + addresses[0].metadata.unlockConditions.publicKeys[0] = undefined expect( await signTransactionLedger({ device, - transaction: getTransaction(), - toSign: getToSign(), + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, addresses, - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing address public key', diff --git a/apps/walletd/lib/signLedger.ts b/apps/walletd/lib/signLedger.ts index 743ddced1..9fc760079 100644 --- a/apps/walletd/lib/signLedger.ts +++ b/apps/walletd/lib/signLedger.ts @@ -3,10 +3,10 @@ import { SiafundElement, Transaction, } from '@siafoundation/types' -import { getWalletWasm } from './wasm' import { AddressData } from '../contexts/addresses/types' import { LedgerDevice } from '../contexts/ledger/types' import { addUnlockConditionsAndSignatures, getToSignMetadata } from './sign' +import { getSDK } from '@siafoundation/sdk' export async function signTransactionLedger({ device, @@ -86,9 +86,8 @@ async function signTransactionIndex({ signatureIndex: number keyIndex: number }): Promise<{ transaction?: Transaction; error?: string }> { - const { encodedTransaction, error } = getWalletWasm().encodeTransaction( - JSON.stringify(transaction) - ) + const { encodedTransaction, error } = + getSDK().wallet.encodeTransaction(transaction) if (error) { return { error } } diff --git a/apps/walletd/lib/signSeed.spec.ts b/apps/walletd/lib/signSeed.spec.ts index e8ac268c4..c01fb7b2c 100644 --- a/apps/walletd/lib/signSeed.spec.ts +++ b/apps/walletd/lib/signSeed.spec.ts @@ -1,49 +1,41 @@ import { signTransactionSeed } from './signSeed' -import { TextEncoder, TextDecoder } from 'util' -import { loadWASMTestEnv } from './wasmTestEnv' -import { - getAddresses, - getConsensusNetwork, - getConsensusState, - getSiacoinOutputs, - getSiafundOutputs, - getToSign, - getTransaction, -} from './testMocks' -global.TextEncoder = TextEncoder -global.TextDecoder = TextDecoder +import { initSDK } from '@siafoundation/sdk' +import { getMockScenarioSeedWallet } from '@siafoundation/mocks' +import { getMockAddresses } from './testMocks' -const seed = '352ef42e07c0fe6e57d15ace7a7ac6cef8ddd187c76c1131fc172967e817ff58' +beforeEach(async () => { + await initSDK() +}) describe('signSeed', () => { it('builds and signs valid transaction', async () => { - await loadWASMTestEnv() + const mocks = getMockScenarioSeedWallet() expect( signTransactionSeed({ - seed, - transaction: getTransaction(), - toSign: getToSign(), - cs: getConsensusState(), - cn: getConsensusNetwork(), - addresses: getAddresses(), - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), + mnemonic: mocks.mnemonic, + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + consensusState: mocks.consensusState, + consensusNetwork: mocks.consensusNetwork, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, + addresses: getMockAddresses(mocks), }) ).toMatchSnapshot() }) it('errors when a toSign utxo is missing', async () => { - await loadWASMTestEnv() + const mocks = getMockScenarioSeedWallet() expect( signTransactionSeed({ - seed, - transaction: getTransaction(), + mnemonic: mocks.mnemonic, + transaction: mocks.walletFundResponse.transaction, + consensusState: mocks.consensusState, + consensusNetwork: mocks.consensusNetwork, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, + addresses: getMockAddresses(mocks), toSign: ['not in siacoinOutputs'], - cs: getConsensusState(), - cn: getConsensusNetwork(), - addresses: getAddresses(), - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing utxo', @@ -51,14 +43,16 @@ describe('signSeed', () => { }) it('errors when a public keys address is missing', async () => { - await loadWASMTestEnv() + const mocks = getMockScenarioSeedWallet() expect( signTransactionSeed({ - seed, - transaction: getTransaction(), - toSign: getToSign(), - cs: getConsensusState(), - cn: getConsensusNetwork(), + mnemonic: mocks.mnemonic, + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + consensusState: mocks.consensusState, + consensusNetwork: mocks.consensusNetwork, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, addresses: [ { id: 'id', @@ -69,8 +63,6 @@ describe('signSeed', () => { }, }, ], - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing address', @@ -78,24 +70,25 @@ describe('signSeed', () => { }) it('errors when an address is missing its index', async () => { - await loadWASMTestEnv() + const mocks = getMockScenarioSeedWallet() expect( signTransactionSeed({ - seed, - transaction: getTransaction(), - toSign: getToSign(), - cs: getConsensusState(), - cn: getConsensusNetwork(), + mnemonic: mocks.mnemonic, + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + consensusState: mocks.consensusState, + consensusNetwork: mocks.consensusNetwork, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, addresses: [ { id: 'id', walletId: 'id', - address: getSiacoinOutputs()[1].siacoinOutput.address, + address: + mocks.walletOutputsSiacoinResponse[1].siacoinOutput.address, metadata: {}, }, ], - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing address index', @@ -103,19 +96,19 @@ describe('signSeed', () => { }) it('errors when an address is missing its public key', async () => { - await loadWASMTestEnv() - const addresses = getAddresses() - addresses[0].metadata.publicKey = undefined + const mocks = getMockScenarioSeedWallet() + const addresses = getMockAddresses(mocks) + addresses[0].metadata.unlockConditions.publicKeys[0] = undefined expect( signTransactionSeed({ - seed, - transaction: getTransaction(), - toSign: getToSign(), - cs: getConsensusState(), - cn: getConsensusNetwork(), + mnemonic: mocks.mnemonic, + transaction: mocks.walletFundResponse.transaction, + toSign: mocks.walletFundResponse.toSign, + consensusState: mocks.consensusState, + consensusNetwork: mocks.consensusNetwork, + siacoinOutputs: mocks.walletOutputsSiacoinResponse, + siafundOutputs: mocks.walletOutputsSiafundResponse, addresses, - siacoinOutputs: getSiacoinOutputs(), - siafundOutputs: getSiafundOutputs(), }) ).toEqual({ error: 'Missing address public key', diff --git a/apps/walletd/lib/signSeed.ts b/apps/walletd/lib/signSeed.ts index 5e9e62c0a..f16b3d17f 100644 --- a/apps/walletd/lib/signSeed.ts +++ b/apps/walletd/lib/signSeed.ts @@ -5,30 +5,30 @@ import { ConsensusState, ConsensusNetwork, } from '@siafoundation/types' -import { getWalletWasm } from './wasm' import { AddressData } from '../contexts/addresses/types' import { addUnlockConditionsAndSignatures, getToSignMetadata } from './sign' +import { getSDK } from '@siafoundation/sdk' export function signTransactionSeed({ - seed, + mnemonic, transaction, toSign, - cs, - cn, + consensusState, + consensusNetwork, addresses, siacoinOutputs, siafundOutputs, }: { - seed: string - cs: ConsensusState - cn: ConsensusNetwork + mnemonic: string + consensusState: ConsensusState + consensusNetwork: ConsensusNetwork transaction: Transaction toSign: string[] addresses: AddressData[] siacoinOutputs: SiacoinElement[] siafundOutputs: SiafundElement[] }): { signedTransaction?: Transaction; error?: string } { - if (!cs) { + if (!consensusState) { return { error: 'No consensus state' } } if (!addresses) { @@ -65,8 +65,8 @@ export function signTransactionSeed({ return { error: utxoAddressError } } - const pkResponse = getWalletWasm().privateKeyFromSeed( - seed, + const pkResponse = getSDK().wallet.keyPairFromSeedPhrase( + mnemonic, address.metadata.index ) @@ -76,11 +76,10 @@ export function signTransactionSeed({ } } - // signTransaction generates a new transaction object with the signature - const { transaction: signedTxn, error } = getWalletWasm().signTransaction( - JSON.stringify(cs), - JSON.stringify(cn), - JSON.stringify(transaction), + const { signature, error } = getSDK().wallet.signTransactionV1( + consensusState, + consensusNetwork, + transaction, i, pkResponse.privateKey ) @@ -89,7 +88,7 @@ export function signTransactionSeed({ error, } } - transaction = signedTxn + transaction.signatures[i].signature = signature } return { diff --git a/apps/walletd/lib/testMocks.ts b/apps/walletd/lib/testMocks.ts index 2072cd536..322f28639 100644 --- a/apps/walletd/lib/testMocks.ts +++ b/apps/walletd/lib/testMocks.ts @@ -1,13 +1,7 @@ import Sia from '@siacentral/ledgerjs-sia' import { LedgerDevice } from '../contexts/ledger/types' -import { - ConsensusNetwork, - ConsensusState, - SiacoinElement, - SiafundElement, - Transaction, -} from '@siafoundation/types' -import { AddressData } from '../contexts/addresses/types' +import { transformAddressesResponse } from '../contexts/addresses/dataset' +import { Wallet, WalletAddressesResponse } from '@siafoundation/react-walletd' export function getMockDevice() { return { @@ -34,383 +28,16 @@ export function getMockDevice() { } as LedgerDevice } -export function getTransaction(): Transaction { - return { - siacoinInputs: [ - { - parentID: - 'scoid:b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5', - unlockConditions: { - timelock: 0, - publicKeys: [ - 'ed25519:b5b9196a3c19f94982bcdba250a973181b22112437832a8f818f4aa73b8add74', - ], - signaturesRequired: 1, - }, - }, - ], - siacoinOutputs: [ - { - value: '95408980544305197274920800', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - ], - siafundInputs: [ - { - parentID: - 'sfoid:b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852', - unlockConditions: { - timelock: 0, - publicKeys: [ - 'ed25519:8a7496aa59f17a4aae68c7e41e09d5ca94e64ba27f74cdb0b143f70dcc67b206', - ], - signaturesRequired: 1, - }, - claimAddress: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - ], - siafundOutputs: [ - { - value: 1, - address: - 'addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f', - }, - ], - minerFees: ['3930000000000000000000'], - } -} - -export function getToSign(): string[] { - return [ - 'h:b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852', - 'h:b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5', - ] -} - -export function getAddresses(): AddressData[] { - return [ - { - id: 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - description: '', - metadata: { - publicKey: - 'ed25519:b5b9196a3c19f94982bcdba250a973181b22112437832a8f818f4aa73b8add74', - index: 1, - }, - walletId: 'ad18cbe1-3281-4ec7-a7ad-93615009fbbc', - }, - { - id: 'addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f', - address: - 'addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f', - description: '', - metadata: { - publicKey: - 'ed25519:8a7496aa59f17a4aae68c7e41e09d5ca94e64ba27f74cdb0b143f70dcc67b206', - index: 2, - }, - walletId: 'ad18cbe1-3281-4ec7-a7ad-93615009fbbc', - }, - { - id: 'addr:fc9bc3482711e9f83642d07be385c0d434892245842b4c3f3b83b26d42cec15fe1aaac1be1ff', - address: - 'addr:fc9bc3482711e9f83642d07be385c0d434892245842b4c3f3b83b26d42cec15fe1aaac1be1ff', - description: '', - metadata: { - publicKey: - 'ed25519:e80ab90d5baab391ec2e8fe31bf100f7ca3d4b5e3055eacf86afd42ab05798ba', - index: 0, - }, - walletId: 'ad18cbe1-3281-4ec7-a7ad-93615009fbbc', - }, - ] -} - -export function getSiacoinOutputs(): SiacoinElement[] { - return [ - { - id: 'h:31cf3ddc946d71d219fb1fbe9a11804e607b6d5ad1b4bf7b3678a2faa701a42e', - leafIndex: 157143, - merkleProof: [ - 'h:743645ee8b7bd0bc755f693472d8ad7fa3c5772f447fd9a46381f7851d22cb92', - 'h:9d613d671b45af9e850ba1cae69fa0fc86b218ee0d469eea7ec3df3e83c1ee2e', - 'h:9c114bd973790e6265836fd459882604494ff2656c79cdedb0173836e03fa88d', - 'h:677019b966d4e16a5f6304d49eed25adb2eb06c5bf589f4cac9e1195db348c58', - 'h:e96f6ae41fcec0f989ca00fc8697326b497515b5204569a2c21e17d4aa2e41ed', - 'h:00b971528955045e0c3ce29675188c5d2b4ddae18b78103aa2a3ae04c9fa2298', - 'h:73a8e33614511b21d98b49df6d96e92413dad51ba48007bd56a39692244e936d', - 'h:7b42dc5e0f6cfd84104bbae8279f917ae4fc383b6da5b792106e27559e14a4d5', - 'h:913008cd339f08d4c2c28e734ab617ae2917b838c67f8afa4d2d38043ca51aa3', - ], - siacoinOutput: { - value: '992140000000000000000000', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - maturityHeight: 0, - }, - { - id: 'h:7ebd499ad589f2b5987b4fdb7fc8b6aa5fab6eaff3f604c61b66ec5777ad9366', - leafIndex: 157141, - merkleProof: [ - 'h:bdbb8d00932cfd2f08e9a32f322ae006583548784031447a6db03e593b619bf6', - 'h:42bb6b4c240897c17373914540cd11373a0b4d39f1c18f6bb939cd7055bb5106', - 'h:9c114bd973790e6265836fd459882604494ff2656c79cdedb0173836e03fa88d', - 'h:677019b966d4e16a5f6304d49eed25adb2eb06c5bf589f4cac9e1195db348c58', - 'h:e96f6ae41fcec0f989ca00fc8697326b497515b5204569a2c21e17d4aa2e41ed', - 'h:00b971528955045e0c3ce29675188c5d2b4ddae18b78103aa2a3ae04c9fa2298', - 'h:73a8e33614511b21d98b49df6d96e92413dad51ba48007bd56a39692244e936d', - 'h:7b42dc5e0f6cfd84104bbae8279f917ae4fc383b6da5b792106e27559e14a4d5', - 'h:913008cd339f08d4c2c28e734ab617ae2917b838c67f8afa4d2d38043ca51aa3', - ], - siacoinOutput: { - value: '996070000000000000000000', - address: - 'addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f', - }, - maturityHeight: 0, - }, - { - id: 'h:a1c8769809d7122dd7d99bb7ef17a7e8919a8d6967fc1607364f57eb93d8aaf5', - leafIndex: 157144, - merkleProof: [ - 'h:e82ad5ed328410bfcb00ec4b8e1c28ac07a9da7c3038ef7ef8c0b262fd70323e', - 'h:8684f565bd83846bd2dd5fc177933635bbc901372a4a0cc72c975296453ff750', - 'h:951ba8356477c4621de211ee00e62496cff865f8daa75530867e4c9e4745cc30', - 'h:6cb63de6d3d47b25f96e88929cf87faec2afa1858c5afcffff3f59aa7533ab8b', - 'h:e96f6ae41fcec0f989ca00fc8697326b497515b5204569a2c21e17d4aa2e41ed', - 'h:00b971528955045e0c3ce29675188c5d2b4ddae18b78103aa2a3ae04c9fa2298', - 'h:73a8e33614511b21d98b49df6d96e92413dad51ba48007bd56a39692244e936d', - 'h:7b42dc5e0f6cfd84104bbae8279f917ae4fc383b6da5b792106e27559e14a4d5', - 'h:913008cd339f08d4c2c28e734ab617ae2917b838c67f8afa4d2d38043ca51aa3', - ], - siacoinOutput: { - value: '55711757555233190591737', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - maturityHeight: 45923, - }, - { - id: 'h:b222428602c8382b67a769d17e1cdc0952f37f2441a872b92671a6ed76cf22f5', - leafIndex: 155364, - merkleProof: [ - 'h:e5d791d9e928201d72460af12ae664eabc8116926620d8014b5498637b59e993', - 'h:7159d4f5825b2357530e9c24bc9bd1b9bda961ea642460969bfa769d9ab84d9c', - 'h:08961a734e757612378b8afbd9d30631d3b48b5830e9631ddfed76b05215d64f', - 'h:68d3178e805c140913f987d0f56775f687a884d657d434f6f4443d536db4fdf6', - 'h:67b2e29b5b1071b8bdb431556ccd48fd3f5edad0a97a98037f79cf292ea8bd09', - 'h:b4f209894a2759c36fb1b62252cdbd1b7c90d61d86f587b5042269dc938734f1', - 'h:19b42d9757d8c3a80d64c630190f140c23887d64fd0dd482db6bf5b3986226d5', - 'h:559236122641cca96efbb897ef800c91513674af9341964b2aca16e5593aa134', - 'h:35d5d6c421b6d87dd3b15f4fe624215ba009952e4c348be727df8ebb14d54e8d', - 'h:b6af69cf68b309c8129c40f81ae2324ac6486a631d2de1bdbfcea6cac755df0e', - 'h:c1cf1a3ff946efc4f9cafa7f0ec25487c6cb2d9121f706c29f7e48b43790f8e7', - 'h:442f5a2b1a5fe821cc811e9e1b746ebefb5b50441fc510cfed1787105e957aa6', - 'h:02537976f2e05843dbab0a6c5d09447cd9cc76a391f31ce764bf3a85a513dab8', - ], - siacoinOutput: { - value: '95412910544305197274920800', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - maturityHeight: 45278, - }, - { - id: 'h:b77b2aa8466032d774b324734cd1998e58476ce173ad412960f3f952abdfcd6f', - leafIndex: 157142, - merkleProof: [ - 'h:031f4de5a38fbb2df09eb9321f5f230b517f67a1b9919a9ea2f82eb9ad210f95', - 'h:9d613d671b45af9e850ba1cae69fa0fc86b218ee0d469eea7ec3df3e83c1ee2e', - 'h:9c114bd973790e6265836fd459882604494ff2656c79cdedb0173836e03fa88d', - 'h:677019b966d4e16a5f6304d49eed25adb2eb06c5bf589f4cac9e1195db348c58', - 'h:e96f6ae41fcec0f989ca00fc8697326b497515b5204569a2c21e17d4aa2e41ed', - 'h:00b971528955045e0c3ce29675188c5d2b4ddae18b78103aa2a3ae04c9fa2298', - 'h:73a8e33614511b21d98b49df6d96e92413dad51ba48007bd56a39692244e936d', - 'h:7b42dc5e0f6cfd84104bbae8279f917ae4fc383b6da5b792106e27559e14a4d5', - 'h:913008cd339f08d4c2c28e734ab617ae2917b838c67f8afa4d2d38043ca51aa3', - ], - siacoinOutput: { - value: '47992140000000000000000000', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - maturityHeight: 0, - }, - { - id: 'h:f961362f6e60e9b85e33b77204ffdb9aec1f601d7e5b709ca2a41f1a048fd899', - leafIndex: 155372, - merkleProof: [ - 'h:26d070a7270455443e5f171dc75319a304d58265ebb3581229ce6bca842e403a', - 'h:d38fd124fb45cf5bd8ecfbfed3634ee93bea6478bd3504d611dd73a69a129d8a', - 'h:98d22e8c71ffece777e343aaddb97fe96f552a3762e4290c0ce69b73462ce192', - 'h:eac2f8bcf3663ebe914cf9721bef97b3d5f9422620a3c1aefd7c429846b0c044', - 'h:67b2e29b5b1071b8bdb431556ccd48fd3f5edad0a97a98037f79cf292ea8bd09', - 'h:b4f209894a2759c36fb1b62252cdbd1b7c90d61d86f587b5042269dc938734f1', - 'h:19b42d9757d8c3a80d64c630190f140c23887d64fd0dd482db6bf5b3986226d5', - 'h:559236122641cca96efbb897ef800c91513674af9341964b2aca16e5593aa134', - 'h:35d5d6c421b6d87dd3b15f4fe624215ba009952e4c348be727df8ebb14d54e8d', - 'h:b6af69cf68b309c8129c40f81ae2324ac6486a631d2de1bdbfcea6cac755df0e', - 'h:c1cf1a3ff946efc4f9cafa7f0ec25487c6cb2d9121f706c29f7e48b43790f8e7', - 'h:442f5a2b1a5fe821cc811e9e1b746ebefb5b50441fc510cfed1787105e957aa6', - 'h:02537976f2e05843dbab0a6c5d09447cd9cc76a391f31ce764bf3a85a513dab8', - ], - siacoinOutput: { - value: '0', - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - maturityHeight: 45280, - }, - ] -} - -export function getSiafundOutputs(): SiafundElement[] { - return [ - { - id: 'h:425a60eee280854b7f3eb59b1613370bcc0ae3a02859f866f80e7b310475e1e8', - leafIndex: 155367, - merkleProof: [ - 'h:baf7bd62d901ae9b36ee5bb81bc9c9df1a06b2b24a92c91fc5006fbcfa989add', - 'h:3cf19954058b6c174edec23237efa0bb81cf71efeb9af73cbb6a8514d3a8028e', - 'h:08961a734e757612378b8afbd9d30631d3b48b5830e9631ddfed76b05215d64f', - 'h:68d3178e805c140913f987d0f56775f687a884d657d434f6f4443d536db4fdf6', - 'h:67b2e29b5b1071b8bdb431556ccd48fd3f5edad0a97a98037f79cf292ea8bd09', - 'h:b4f209894a2759c36fb1b62252cdbd1b7c90d61d86f587b5042269dc938734f1', - 'h:19b42d9757d8c3a80d64c630190f140c23887d64fd0dd482db6bf5b3986226d5', - 'h:559236122641cca96efbb897ef800c91513674af9341964b2aca16e5593aa134', - 'h:35d5d6c421b6d87dd3b15f4fe624215ba009952e4c348be727df8ebb14d54e8d', - 'h:b6af69cf68b309c8129c40f81ae2324ac6486a631d2de1bdbfcea6cac755df0e', - 'h:c1cf1a3ff946efc4f9cafa7f0ec25487c6cb2d9121f706c29f7e48b43790f8e7', - 'h:442f5a2b1a5fe821cc811e9e1b746ebefb5b50441fc510cfed1787105e957aa6', - 'h:02537976f2e05843dbab0a6c5d09447cd9cc76a391f31ce764bf3a85a513dab8', - ], - siafundOutput: { - value: 99, - address: - 'addr:934b885229a9f98153401d7a647a1862aede399c656f33ec8492dfffca557ca907a3d22089c8', - }, - claimStart: '32152366120469300091412640000', - }, - { - id: 'h:b53e88ce69f19f0bf1d3496479f20b72e1133c719e82278830ee6618bb582852', - leafIndex: 157146, - merkleProof: [ - 'h:01a92b66744b2a0198ef7a325268d8cae43858ca6f1d2677f9059327cedf6640', - 'h:19fecb42a55c9ed127354db7eaddbdf784e0dce3f2cd5d931e30d31c15bd6311', - 'h:951ba8356477c4621de211ee00e62496cff865f8daa75530867e4c9e4745cc30', - 'h:6cb63de6d3d47b25f96e88929cf87faec2afa1858c5afcffff3f59aa7533ab8b', - 'h:e96f6ae41fcec0f989ca00fc8697326b497515b5204569a2c21e17d4aa2e41ed', - 'h:00b971528955045e0c3ce29675188c5d2b4ddae18b78103aa2a3ae04c9fa2298', - 'h:73a8e33614511b21d98b49df6d96e92413dad51ba48007bd56a39692244e936d', - 'h:7b42dc5e0f6cfd84104bbae8279f917ae4fc383b6da5b792106e27559e14a4d5', - 'h:913008cd339f08d4c2c28e734ab617ae2917b838c67f8afa4d2d38043ca51aa3', - ], - siafundOutput: { - value: 1, - address: - 'addr:eb2ee5169dd9aaab804b38f7e70043690ac21da1144990a4a28c1dcf66cd7ee9845aef03006f', - }, - claimStart: '32709483696021631997330010000', - }, - ] -} - -export function getConsensusState(): ConsensusState { - return { - index: { - height: 45962, - id: 'bid:000000000cb8ef1dfeb66afa78bc0b3b2d1a7a1df948efba22f7fc1a5571e79f', - }, - prevTimestamps: [ - '2023-11-28T11:34:49-05:00', - '2023-11-28T11:22:41-05:00', - '2023-11-28T11:19:59-05:00', - '2023-11-28T11:10:13-05:00', - '2023-11-28T11:09:32-05:00', - '2023-11-28T11:07:38-05:00', - '2023-11-28T10:47:27-05:00', - '2023-11-28T09:58:20-05:00', - '2023-11-28T09:51:26-05:00', - '2023-11-28T09:50:31-05:00', - '2023-11-28T09:40:07-05:00', - ], - depth: - 'bid:00000000000203572d5b49ea0e554f31ba43d81854d4313433fbb59f6c0db0b3', - childTarget: - 'bid:00000001724087005d8de96a9feb9a37bd483392cbb691f9cc73b5c9d14cc861', - siafundPool: '33603845293260630383068710000', - oakTime: 117766000000000, - oakTarget: - 'bid:0000000001d8373aecb257ac55c0077f7fe0d8e7c02053cefe7215aa480fdc63', - foundationPrimaryAddress: - 'addr:053b2def3cbdd078c19d62ce2b4f0b1a3c5e0ffbeeff01280efb1f8969b2f5bb4fdc680f0807', - foundationFailsafeAddress: - 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', - totalWork: '139825201060364', - difficulty: '2969630008', - oakWork: '596072835270', - elements: { - numLeaves: 157715, - trees: [ - 'h:680fc2873f62d72a4b41f93f6d919ce6271265e04f8135549cb7d0bda5df08e2', - 'h:dbdd12bdea241c262c3a39a85d37c7cf44c858e031a17843638b60285d1777ba', - 'h:9b3f0603ce5237d86e5a3ea3fcdf7b235c17ef4ad0ca7a66623e0df30bd6be62', - 'h:cf7b5de9eecc85f208d137d56b5642d193d378478fa49476af6f2d232883f552', - 'h:670e74aafd7bffe4b07d9a5c6c52111d0aadbc4cf0d76c00a8f2b8ce345999c3', - 'h:b2a0a932a907641d183201aee929b128808a980a3f08db9eba19e50e978b9bdb', - 'h:ca27be5aae09ffc0e932ce785770723c85dc0598c3e578d0b61a045245323a6f', - ], - }, - attestations: 0, - } -} - -export function getConsensusNetwork(): ConsensusNetwork { - return { - name: 'zen', - initialCoinbase: '300000000000000000000000000000', - minimumCoinbase: '300000000000000000000000000000', - initialTarget: - 'bid:0000000100000000000000000000000000000000000000000000000000000000', - hardforkDevAddr: { - height: 1, - oldAddress: - 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', - newAddress: - 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', - }, - hardforkTax: { - height: 2, - }, - hardforkStorageProof: { - height: 5, - }, - hardforkOak: { - height: 10, - fixHeight: 12, - genesisTimestamp: '2023-01-13T03:53:20-05:00', - }, - hardforkASIC: { - height: 20, - oakTime: 10000000000000, - oakTarget: - 'bid:0000000100000000000000000000000000000000000000000000000000000000', - }, - hardforkFoundation: { - height: 30, - primaryAddress: - 'addr:053b2def3cbdd078c19d62ce2b4f0b1a3c5e0ffbeeff01280efb1f8969b2f5bb4fdc680f0807', - failsafeAddress: - 'addr:000000000000000000000000000000000000000000000000000000000000000089eb0d6a8a69', - }, - hardforkV2: { - allowHeight: 100000, - requireHeight: 102000, - }, - } +export function getMockAddresses({ + newWallet, + walletAddressesResponse, +}: { + newWallet: Wallet + walletAddressesResponse: WalletAddressesResponse +}) { + return transformAddressesResponse( + walletAddressesResponse, + newWallet.id, + () => null + ) } diff --git a/apps/walletd/pages/_app.tsx b/apps/walletd/pages/_app.tsx index 8d2a0a986..654a84a22 100644 --- a/apps/walletd/pages/_app.tsx +++ b/apps/walletd/pages/_app.tsx @@ -2,7 +2,6 @@ import '../config/style.css' import { NextAppCsr } from '@siafoundation/design-system' import { AppProps } from 'next/app' import { Providers } from '../config/providers' -import { useWasm } from '../hooks/useWasm' import { routes } from '../config/routes' import { rootFontClasses } from '@siafoundation/fonts' @@ -12,7 +11,6 @@ export default function App({ }: AppProps<{ fallback?: Record }>) { - useWasm() return ( ) { type TxPoolTransactionsResponse = { transactions: Transaction[] - v2Transactions: V2Transaction[] + v2transactions: V2Transaction[] } const txPoolTransactionsRoute = '/txpool/transactions' @@ -138,7 +138,7 @@ export function useTxPoolFee(args?: HookArgsSwr) { type TxPoolBroadcastPayload = { transactions: Transaction[] - v2Transactions: V2Transaction[] + v2transactions: V2Transaction[] } export function useTxPoolBroadcast( @@ -189,7 +189,7 @@ export function useWallets(args?: HookArgsSwr) { type WalletUpdatePayload = { name: string description: string - metadata: Metadata + metadata: WalletMetadata } export type WalletAddResponse = Wallet @@ -254,10 +254,10 @@ export function useWalletAddresses( }) } -export type WalletAddressAddResponse = WalletAddress +export type WalletAddressAddPayload = WalletAddress export function useWalletAddressAdd( - args?: HookArgsCallback<{ id: string }, WalletAddressAddResponse, void> + args?: HookArgsCallback<{ id: string }, WalletAddressAddPayload, void> ) { return usePutFunc( { diff --git a/libs/react-walletd/src/siaTypes.ts b/libs/react-walletd/src/siaTypes.ts index ecf82b185..02543377e 100644 --- a/libs/react-walletd/src/siaTypes.ts +++ b/libs/react-walletd/src/siaTypes.ts @@ -12,6 +12,7 @@ import { PublicKey, TransactionID, SpendPolicy, + UnlockConditions, } from '@siafoundation/types' export type GatewayPeer = { @@ -114,18 +115,33 @@ export type WalletEvent = export type Metadata = Record +export type WalletType = 'seed' | 'ledger' | 'watch' + +export type WalletMetadata = { + type: WalletType + mnemonicHash?: string + // ledger + publicKey0?: string + address0?: string +} + export type Wallet = { id: string name: string description: string dateCreated: string lastUpdated: string - metadata: Metadata + metadata: WalletMetadata +} + +export type WalletAddressMetadata = { + index?: number + unlockConditions?: UnlockConditions } export type WalletAddress = { address: string description: string spendPolicy?: SpendPolicy - metadata: Metadata + metadata: WalletAddressMetadata }