]> git.codecow.com Git - libnemo.git/commitdiff
Extract three more wallet methods to separate function modules.
authorChris Duncan <chris@zoso.dev>
Tue, 19 Aug 2025 04:37:42 +0000 (21:37 -0700)
committerChris Duncan <chris@zoso.dev>
Tue, 19 Aug 2025 04:37:42 +0000 (21:37 -0700)
src/lib/wallet/accounts.ts [new file with mode: 0644]
src/lib/wallet/index.ts
src/lib/wallet/unopened.ts [new file with mode: 0644]
src/lib/wallet/verify.ts [new file with mode: 0644]

diff --git a/src/lib/wallet/accounts.ts b/src/lib/wallet/accounts.ts
new file mode 100644 (file)
index 0000000..7e0f77f
--- /dev/null
@@ -0,0 +1,43 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { Account, AccountList } from '../account'
+import { Vault } from '../vault'
+import { KeyPair } from '#types'
+
+export async function _accounts (accounts: AccountList, vault: Vault, from: number, to: number): Promise<AccountList>
+export async function _accounts (accounts: AccountList, vault: Vault, from: unknown, to: unknown): Promise<AccountList> {
+       if (typeof from !== 'number' || typeof to !== 'number') {
+               throw new TypeError('Invalid account range', { cause: `${to}-${from}` })
+       }
+       if (from > to) [from as number, to as number] = [to, from]
+       const output = new AccountList()
+       const indexes: number[] = []
+       for (let i = from; i <= to; i++) {
+               if (accounts[i] == null) {
+                       indexes.push(i)
+               } else {
+                       output[i] = accounts[i]
+               }
+       }
+       if (indexes.length > 0) {
+               const promises = []
+               for (const index of indexes) {
+                       promises.push(vault.request<ArrayBuffer>({
+                               action: 'derive',
+                               index
+                       }))
+               }
+               const publicKeys: KeyPair[] = await Promise.all(promises)
+               if (publicKeys.length > 0) {
+                       const publicAccounts = Account.load(publicKeys)
+                       for (const a of publicAccounts) {
+                               if (a.index == null) {
+                                       throw new RangeError('Index missing for Account')
+                               }
+                               output[a.index] = accounts[a.index] = a
+                       }
+               }
+       }
+       return output
+}
index 8569b5bda48be764bed48e95e0eb8d83ebba34d2..3b8c45820b780ca3ba541821fe89b0b338026b2e 100644 (file)
@@ -17,6 +17,9 @@ import { Rpc } from '../rpc'
 import { _sign } from './sign'\r
 import { _unlock } from './unlock'\r
 import { Vault } from '../vault'\r
+import { _accounts } from './accounts'\r
+import { _verify } from './verify'\r
+import { _unopened } from './unopened'\r
 \r
 /**\r
 * Represents a wallet containing numerous Nano accounts derived from a single\r
@@ -122,7 +125,7 @@ export class Wallet {
        get id (): string { return this.#id }\r
 \r
        /**\r
-       * Method used to create wallet and derive accounts.\r
+       * Algorithm or device used to create wallet and derive accounts.\r
        */\r
        get type (): WalletType { return this.#type }\r
 \r
@@ -216,41 +219,12 @@ export class Wallet {
        * ```\r
        *\r
        * If `from` is greater than `to`, their values will be swapped.\r
-       * @param {number} from - Start index of accounts. Default: 0\r
-       * @param {number} to - End index of accounts. Default: `from`\r
+       * @param {number} [from=0] - Start index of accounts. Default: 0\r
+       * @param {number} [to=from] - End index of accounts. Default: `from`\r
        * @returns {AccountList} Promise for a list of Accounts at the specified indexes\r
        */\r
        async accounts (from: number = 0, to: number = from): Promise<AccountList> {\r
-               if (from > to) [from, to] = [to, from]\r
-               const output = new AccountList()\r
-               const indexes: number[] = []\r
-               for (let i = from; i <= to; i++) {\r
-                       if (this.#accounts[i] == null) {\r
-                               indexes.push(i)\r
-                       } else {\r
-                               output[i] = this.#accounts[i]\r
-                       }\r
-               }\r
-               if (indexes.length > 0) {\r
-                       const promises = []\r
-                       for (const index of indexes) {\r
-                               promises.push(this.#vault.request<ArrayBuffer>({\r
-                                       action: 'derive',\r
-                                       index\r
-                               }))\r
-                       }\r
-                       const publicKeys: KeyPair[] = await Promise.all(promises)\r
-                       if (publicKeys.length > 0) {\r
-                               const publicAccounts = Account.load(publicKeys)\r
-                               for (const a of publicAccounts) {\r
-                                       if (a.index == null) {\r
-                                               throw new RangeError('Index missing for Account')\r
-                                       }\r
-                                       output[a.index] = this.#accounts[a.index] = a\r
-                               }\r
-                       }\r
-               }\r
-               return output\r
+               return await _accounts(this.#accounts, this.#vault, from, to)\r
        }\r
 \r
        /**\r
@@ -335,25 +309,7 @@ export class Wallet {
        * @returns {Promise<Account>} The lowest-indexed unopened account belonging to the wallet\r
        */\r
        async unopened (rpc: Rpc, batchSize: number = ADDRESS_GAP, from: number = 0): Promise<Account> {\r
-               if (!Number.isSafeInteger(batchSize) || batchSize < 1) {\r
-                       throw new RangeError(`Invalid batch size ${batchSize}`)\r
-               }\r
-               const accounts = await this.accounts(from, from + batchSize - 1)\r
-               const addresses = []\r
-               for (const a in accounts) {\r
-                       addresses.push(accounts[a].address)\r
-               }\r
-               const data = {\r
-                       "accounts": addresses\r
-               }\r
-               const { errors } = await rpc.call('accounts_frontiers', data)\r
-               for (const key of Object.keys(errors ?? {})) {\r
-                       const value = errors[key]\r
-                       if (value === 'Account not found') {\r
-                               return Account.load(key)\r
-                       }\r
-               }\r
-               return await this.unopened(rpc, batchSize, from + batchSize)\r
+               return await _unopened(this, rpc, batchSize, from + batchSize)\r
        }\r
 \r
        /**\r
@@ -374,23 +330,7 @@ export class Wallet {
        */\r
        async verify (mnemonic: string): Promise<boolean>\r
        async verify (secret: string): Promise<boolean> {\r
-               try {\r
-                       const data: NamedData = {\r
-                               action: 'verify'\r
-                       }\r
-                       if (/^(?:[A-F0-9]{64}){1,2}$/i.test(secret)) {\r
-                               data.seed = hex.toBuffer(secret)\r
-                       } else if (/^([a-z]{3,8} ){11,23}[a-z]{3,8}$/i.test(secret)) {\r
-                               data.mnemonicPhrase = secret.toLowerCase()\r
-                       } else {\r
-                               throw new TypeError('Invalid format')\r
-                       }\r
-                       const result = await this.#vault.request<boolean>(data)\r
-                       const { isVerified } = result\r
-                       return isVerified\r
-               } catch (err) {\r
-                       throw new Error('Failed to verify wallet', { cause: err })\r
-               }\r
+               return await _verify(this.#vault, secret)\r
        }\r
 \r
        static #isInternal: boolean = false\r
diff --git a/src/lib/wallet/unopened.ts b/src/lib/wallet/unopened.ts
new file mode 100644 (file)
index 0000000..0b31a3f
--- /dev/null
@@ -0,0 +1,38 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { Account } from '../account'
+import { hex } from '../convert'
+import { Rpc } from '../rpc'
+import { Vault } from '../vault'
+import { Wallet } from '#wallet'
+import { NamedData } from '#types'
+
+export async function _unopened (wallet: Wallet, rpc: Rpc, batchSize: number, from: number): Promise<Account>
+export async function _unopened (wallet: Wallet, rpc: unknown, batchSize: unknown, from: unknown): Promise<Account> {
+       if (!(rpc instanceof Rpc)) {
+               throw new TypeError('Invalid RPC endpoint', { cause: rpc })
+       }
+       if (typeof batchSize !== 'number' || !Number.isSafeInteger(batchSize) || batchSize < 1) {
+               throw new TypeError('Invalid batch size', { cause: batchSize })
+       }
+       if (typeof from !== 'number' || !Number.isSafeInteger(batchSize) || batchSize < 0) {
+               throw new TypeError('Invalid starting account index', { cause: from })
+       }
+       const accounts = await wallet.accounts(from, from + batchSize - 1)
+       const addresses = []
+       for (const a in accounts) {
+               addresses.push(accounts[a].address)
+       }
+       const data = {
+               "accounts": addresses
+       }
+       const { errors } = await rpc.call('accounts_frontiers', data)
+       for (const key of Object.keys(errors ?? {})) {
+               const value = errors[key]
+               if (value === 'Account not found') {
+                       return Account.load(key)
+               }
+       }
+       return await _unopened(wallet, rpc, batchSize, from + batchSize)
+}
diff --git a/src/lib/wallet/verify.ts b/src/lib/wallet/verify.ts
new file mode 100644 (file)
index 0000000..01526b7
--- /dev/null
@@ -0,0 +1,30 @@
+//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { hex } from '../convert'
+import { Vault } from '../vault'
+import { NamedData } from '#types'
+
+export async function _verify (vault: Vault, secret: string): Promise<boolean>
+export async function _verify (vault: Vault, secret: unknown): Promise<boolean> {
+       try {
+               if (typeof secret !== 'string') {
+                       throw new TypeError('Wallet secret must be a string', { cause: typeof secret })
+               }
+               const data: NamedData = {
+                       action: 'verify'
+               }
+               if (/^(?:[A-F0-9]{64}){1,2}$/i.test(secret)) {
+                       data.seed = hex.toBuffer(secret)
+               } else if (/^([a-z]{3,8} ){11,23}[a-z]{3,8}$/i.test(secret)) {
+                       data.mnemonicPhrase = secret.toLowerCase()
+               } else {
+                       throw new TypeError('Invalid format')
+               }
+               const result = await vault.request<boolean>(data)
+               const { isVerified } = result
+               return isVerified
+       } catch (err) {
+               throw new Error('Failed to verify wallet', { cause: err })
+       }
+}