From cff74da6d633a52acee41f0535baf9770b0f0911 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Tue, 8 Oct 2024 02:02:56 -0700 Subject: [PATCH] Return bigint directly from account properties to let consumers do math on the raw amounts since they can call toString() if necessary. Allow bigint to be passed to denomination converter and explicitly check its amount type. --- src/lib/account.ts | 6 +- src/lib/tools.ts | 13 ++- test/refresh-accounts.test.mjs | 200 ++++++++++++++++----------------- 3 files changed, 111 insertions(+), 108 deletions(-) diff --git a/src/lib/account.ts b/src/lib/account.ts index 80c358c..356d1ab 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -31,10 +31,10 @@ export class Account { get privateKey () { return this.#prv } get index () { return this.#i } get frontier () { return this.#f } - get balance () { return this.#b?.toString() } - get receivable () { return this.#r?.toString() } + get balance () { return this.#b } + get receivable () { return this.#r } get representative () { return this.#rep } - get weight () { return this.#w?.toString() } + get weight () { return this.#w } set frontier (v) { this.#f = v } set balance (v) { this.#b = v ? BigInt(v) : undefined } diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 13b79b7..fb5c13d 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -14,12 +14,15 @@ import { SendBlock } from './block.js' /** * Converts a decimal amount from one unit divider to another. * -* @param {string} amount - Decimal amount to convert +* @param {bigint|string} amount - Decimal amount to convert * @param {string} inputUnit - Current denomination * @param {string} outputUnit - Desired denomination */ -export async function convert (amount: string, inputUnit: string, outputUnit: string): Promise { - if (!amount || !/^[0-9]*\.?[0-9]*$/.test(amount)) { +export async function convert (amount: bigint | string, inputUnit: string, outputUnit: string): Promise { + if (typeof amount === 'bigint') { + amount = amount.toString() + } + if (typeof amount !== 'string' || amount === '' || !/^[0-9]*\.?[0-9]*$/.test(amount)) { throw new Error('Invalid amount') } let [i = '', f = ''] = amount.toString().split('.') @@ -131,9 +134,9 @@ export async function sweep (node: Node | string | URL, wallet: Blake2bWallet | if (account.representative?.address && account.frontier) { const block = new SendBlock( account, - account.balance ?? '0', + account.balance?.toString() ?? '0', recipientAccount.address, - account.balance ?? '0', + account.balance?.toString() ?? '0', account.representative.address, account.frontier ) diff --git a/test/refresh-accounts.test.mjs b/test/refresh-accounts.test.mjs index 7767b4c..c1427d3 100644 --- a/test/refresh-accounts.test.mjs +++ b/test/refresh-accounts.test.mjs @@ -18,110 +18,110 @@ const node = new Node(process.env.NODE_URL, process.env.API_KEY_NAME, process.en const skip = true describe('refreshing account info', { skip }, async () => { - it('should fetch balance, frontier, and representative', async () => { - const accounts = await wallet.accounts() - const account = accounts[0] - await account.refresh(node) - - assert.equal(typeof account.balance, 'string') - assert.notEqual(account.balance, undefined) - assert.notEqual(account.balance, null) - assert.notEqual(account.balance, '') - assert.equal(isNaN(parseInt(account.balance)), false) - assert.equal(parseInt(account.balance) < 0, false) - - assert.equal(typeof account.frontier, 'string') - assert.notEqual(account.frontier, undefined) - assert.notEqual(account.frontier, null) - assert.notEqual(account.frontier, '') - assert.match(account.frontier, /^[0-9A-F]{64}$/i) - - assert.equal(account.representative.constructor, Account) - assert.notEqual(account.representative, undefined) - assert.notEqual(account.representative, null) - assert.notEqual(account.representative, '') - assert.notEqual(account.representative.address, undefined) - assert.notEqual(account.representative.address, null) - assert.notEqual(account.representative.address, '') - }) - - it('should throw when refreshing unopened account', async () => { - const accounts = await wallet.accounts(0x7fffffff) - const account = accounts[0] - await assert.rejects(account.refresh(node), - { message: 'Account not found' }) - }) - - it('should throw when referencing invalid account index', async () => { - await assert.rejects(wallet.accounts(0x80000000), - { message: 'Invalid child key index 0x80000000' }) - }) - - it('should throw with invalid node', async () => { - const invalidNode = new Node('http://invalid.com') - const accounts = await wallet.accounts() - const account = accounts[0] - await assert.rejects(account.refresh(invalidNode), - { message: 'Account not found' }) - }) + it('should fetch balance, frontier, and representative', async () => { + const accounts = await wallet.accounts() + const account = accounts[0] + await account.refresh(node) + + assert.equal(typeof account.balance, 'string') + assert.notEqual(account.balance, undefined) + assert.notEqual(account.balance, null) + assert.notEqual(account.balance, '') + assert.equal(isNaN(parseInt(account.balance)), false) + assert.equal(parseInt(account.balance) < 0, false) + + assert.equal(typeof account.frontier, 'string') + assert.notEqual(account.frontier, undefined) + assert.notEqual(account.frontier, null) + assert.notEqual(account.frontier, '') + assert.match(account.frontier, /^[0-9A-F]{64}$/i) + + assert.equal(account.representative.constructor, Account) + assert.notEqual(account.representative, undefined) + assert.notEqual(account.representative, null) + assert.notEqual(account.representative, '') + assert.notEqual(account.representative.address, undefined) + assert.notEqual(account.representative.address, null) + assert.notEqual(account.representative.address, '') + }) + + it('should throw when refreshing unopened account', async () => { + const accounts = await wallet.accounts(0x7fffffff) + const account = accounts[0] + await assert.rejects(account.refresh(node), + { message: 'Account not found' }) + }) + + it('should throw when referencing invalid account index', async () => { + await assert.rejects(wallet.accounts(0x80000000), + { message: 'Invalid child key index 0x80000000' }) + }) + + it('should throw with invalid node', async () => { + const invalidNode = new Node('http://invalid.com') + const accounts = await wallet.accounts() + const account = accounts[0] + await assert.rejects(account.refresh(invalidNode), + { message: 'Account not found' }) + }) }) describe('finding next unopened account', { skip }, async () => { - it('should return correct account from test vector', async () => { - const account = await wallet.getNextNewAccount(node) - assert.ok(account) - assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) - }) - - it('should return successfully for small batch size', async () => { - const account = await wallet.getNextNewAccount(node, 1) - assert.ok(account) - assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) - }) - - it('should return successfully for large batch size', async () => { - const account = await wallet.getNextNewAccount(node, 100) - assert.ok(account) - assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) - assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) - }) - - it('should throw on invalid node URL', async () => { - await assert.rejects(wallet.getNextNewAccount()) - await assert.rejects(wallet.getNextNewAccount(null)) - await assert.rejects(wallet.getNextNewAccount(1)) - await assert.rejects(wallet.getNextNewAccount('')) - await assert.rejects(wallet.getNextNewAccount('foo')) - }) - - it('should throw on invalid batch size', async () => { - await assert.rejects(wallet.getNextNewAccount(node, null)) - await assert.rejects(wallet.getNextNewAccount(node, -1)) - await assert.rejects(wallet.getNextNewAccount(node, '')) - await assert.rejects(wallet.getNextNewAccount(node, 'foo')) - await assert.rejects(wallet.getNextNewAccount(node, { 'foo': 'bar' })) - }) + it('should return correct account from test vector', async () => { + const account = await wallet.getNextNewAccount(node) + assert.ok(account) + assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) + }) + + it('should return successfully for small batch size', async () => { + const account = await wallet.getNextNewAccount(node, 1) + assert.ok(account) + assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) + }) + + it('should return successfully for large batch size', async () => { + const account = await wallet.getNextNewAccount(node, 100) + assert.ok(account) + assert.equal(account.address, NANO_TEST_VECTORS.ADDRESS_1) + assert.equal(account.publicKey, NANO_TEST_VECTORS.PUBLIC_1) + }) + + it('should throw on invalid node URL', async () => { + await assert.rejects(wallet.getNextNewAccount()) + await assert.rejects(wallet.getNextNewAccount(null)) + await assert.rejects(wallet.getNextNewAccount(1)) + await assert.rejects(wallet.getNextNewAccount('')) + await assert.rejects(wallet.getNextNewAccount('foo')) + }) + + it('should throw on invalid batch size', async () => { + await assert.rejects(wallet.getNextNewAccount(node, null)) + await assert.rejects(wallet.getNextNewAccount(node, -1)) + await assert.rejects(wallet.getNextNewAccount(node, '')) + await assert.rejects(wallet.getNextNewAccount(node, 'foo')) + await assert.rejects(wallet.getNextNewAccount(node, { 'foo': 'bar' })) + }) }) describe('refreshing wallet accounts', { skip }, async () => { - it('should get balance, frontier, and representative for one account', async () => { - const accounts = await wallet.refresh(node) - assert.ok(accounts[0] instanceof Account) - assert.equal(typeof accounts[0].balance, 'string') - assert.notEqual(accounts[0].frontier, undefined) - assert.notEqual(accounts[0].frontier, null) - assert.equal(typeof accounts[0].frontier, 'string') - }) - - it('should get balance, frontier, and representative for multiple accounts', async () => { - const accounts = await wallet.refresh(node, 0, 2) - assert.equal(accounts.length, 1) - assert.ok(accounts[0] instanceof Account) - }) - - it('should handle failure gracefully', async () => { - await assert.doesNotReject(wallet.refresh(node, 0, 20)) - }) + it('should get balance, frontier, and representative for one account', async () => { + const accounts = await wallet.refresh(node) + assert.ok(accounts[0] instanceof Account) + assert.equal(typeof accounts[0].balance, 'bigint') + assert.notEqual(accounts[0].frontier, undefined) + assert.notEqual(accounts[0].frontier, null) + assert.equal(typeof accounts[0].frontier, 'string') + }) + + it('should get balance, frontier, and representative for multiple accounts', async () => { + const accounts = await wallet.refresh(node, 0, 2) + assert.equal(accounts.length, 1) + assert.ok(accounts[0] instanceof Account) + }) + + it('should handle failure gracefully', async () => { + await assert.doesNotReject(wallet.refresh(node, 0, 20)) + }) }) -- 2.34.1