From 7f37e74080dc53fd394fc2e9009756834f2c7f7a Mon Sep 17 00:00:00 2001 From: Denis Davidyuk Date: Thu, 26 May 2022 19:49:31 +0300 Subject: [PATCH] refactor(NodeApi)!: return BigInt instead of string | number BREAKING CHANGE: Node API returns BigInts for coin fields instead of string or number --- src/chain.ts | 10 +++++----- src/nodeApi.ts | 13 +++++-------- src/tx/builder/index.js | 4 ++-- src/utils/amount-formatter.ts | 4 ++-- src/utils/bignumber.ts | 13 +++++++------ test/integration/accounts.js | 16 +++++----------- test/integration/contract-aci.js | 6 +++--- test/integration/oracle.js | 8 ++++---- tsconfig.json | 4 ++-- 9 files changed, 35 insertions(+), 43 deletions(-) diff --git a/src/chain.ts b/src/chain.ts index ceb875e229..8fc3a9171e 100644 --- a/src/chain.ts +++ b/src/chain.ts @@ -170,7 +170,7 @@ export async function waitForTxConfirm ( export async function getAccount ( address: EncodedData<'ak'>, { height, hash, onNode }: - { height?: number, hash?: EncodedData<'kh'> | EncodedData<'mh'>, onNode: Node } + { height?: number, hash?: EncodedData<'kh' | 'mh'>, onNode: Node } ): Promise> { if (height != null) return await onNode.api.getAccountByPubkeyAndHeight(address, height) if (hash != null) return await onNode.api.getAccountByPubkeyAndHash(address, hash) @@ -189,10 +189,10 @@ export async function getBalance ( address: EncodedData<'ak'>, { format = AE_AMOUNT_FORMATS.AETTOS, ...options }: { format: AeAmountFormats } & Parameters[1] -): Promise { - const { balance } = await getAccount(address, options).catch(() => ({ balance: 0 })) +): Promise { + const { balance } = await getAccount(address, options).catch(() => ({ balance: 0n })) - return formatAmount(balance, { targetDenomination: format }).toString() + return formatAmount(balance, { targetDenomination: format }) } /** @@ -440,7 +440,7 @@ export async function resolveName ( key: string, { verify = true, resolveByNode, onNode }: { verify: boolean, resolveByNode: boolean, onNode: Node } -): Promise | EncodedData<'nm'>> { +): Promise> { try { decode(nameOrId) return nameOrId as EncodedData<'ak'> diff --git a/src/nodeApi.ts b/src/nodeApi.ts index b238f9cd62..f0fafbe358 100644 --- a/src/nodeApi.ts +++ b/src/nodeApi.ts @@ -8,13 +8,13 @@ import { mapObject } from './utils/other' import { EncodedData } from './utils/encoder' const bigIntPropertyNames = [ - 'balance', 'queryFee', 'fee', 'amount', 'nameSalt', 'nameFee', 'channelAmount', + 'balance', 'queryFee', 'fee', 'amount', 'nameFee', 'channelAmount', 'initiatorAmount', 'responderAmount', 'channelReserve', 'initiatorAmountFinal', - 'responderAmountFinal', 'gasPrice', 'gas', 'gasUsed', 'deposit' + 'responderAmountFinal', 'gasPrice', 'deposit' ] as const const numberPropertyNames = [ - 'time', + 'time', 'gas', 'gasUsed', 'nameSalt', 'nonce', 'nextNonce', 'height', 'blockHeight', 'top', 'topBlockHeight', 'ttl', 'inbound', 'outbound', 'peerCount', 'pendingTransactionsCount', 'effectiveAtHeight', 'version', 'solutions' @@ -35,7 +35,7 @@ export type TransformNodeType = ? { [Property in keyof Type]: Property extends BigIntPropertyNames - ? PreserveOptional + ? PreserveOptional : Property extends NumberPropertyNames ? PreserveOptional : Property extends 'txHash' @@ -103,10 +103,7 @@ export default class extends (Node as unknown as TransformedNode) { #decodeRes (data: any): any { return this.#mapData(data, { - bigInt: value => { - const bn = new BigNumber(value) - return bn.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER) ? +bn : bn.toFixed() - }, + bigInt: value => BigInt(value), number: value => +value }) } diff --git a/src/tx/builder/index.js b/src/tx/builder/index.js index 8b084fcaef..8b65f72ed7 100644 --- a/src/tx/builder/index.js +++ b/src/tx/builder/index.js @@ -156,8 +156,8 @@ function validateField (value, type, prefix) { switch (type) { case FIELD_TYPES.amount: case FIELD_TYPES.int: { - if (isNaN(value) && !BigNumber.isBigNumber(value)) { - return `${value} is not of type Number or BigNumber` + if (typeof value !== 'bigint' && isNaN(value) && !BigNumber.isBigNumber(value)) { + return `${value} is not of type BigInt, Number or BigNumber` } if (new BigNumber(value).lt(0)) return `${value} must be >= 0` return diff --git a/src/utils/amount-formatter.ts b/src/utils/amount-formatter.ts index 26cdb9f1af..5ed85a3b7d 100644 --- a/src/utils/amount-formatter.ts +++ b/src/utils/amount-formatter.ts @@ -83,13 +83,13 @@ export const toAettos = ( * @return {String} */ export const formatAmount = ( - value: string | number | BigNumber, + value: string | number | bigint | BigNumber, { denomination = AE_AMOUNT_FORMATS.AETTOS, targetDenomination = AE_AMOUNT_FORMATS.AETTOS }: { denomination?: AeAmountFormats, targetDenomination?: AeAmountFormats } ): string => { if (!isBigNumber(value)) throw new ArgumentError('value', 'a number', value) - return new BigNumber(value) + return new BigNumber(typeof value === 'bigint' ? value.toString() : value) .shiftedBy(DENOMINATION_MAGNITUDE[denomination] - DENOMINATION_MAGNITUDE[targetDenomination]) .toFixed() } diff --git a/src/utils/bignumber.ts b/src/utils/bignumber.ts index 475dc56f1f..cd308336a6 100644 --- a/src/utils/bignumber.ts +++ b/src/utils/bignumber.ts @@ -6,13 +6,14 @@ import BigNumber from 'bignumber.js' /** - * Check if value is BigNumber, Number or number string representation - * @param {String|Number|BigNumber} number number to convert - * @return {Boolean} + * Check if value is BigNumber, Number, BigInt or number string representation + * @param number number to check */ -export const isBigNumber = (number: string | number | BigNumber): boolean => - ['number', 'object', 'string', 'bigint'].includes(typeof number) && - (!isNaN(number as number) || Number.isInteger(number) || BigNumber.isBigNumber(number)) +export const isBigNumber = (number: string | number | bigint | BigNumber): boolean => { + if (typeof number === 'bigint') return true + return ['number', 'object', 'string'].includes(typeof number) && + (!isNaN(number as number) || Number.isInteger(number) || BigNumber.isBigNumber(number)) +} /** * BigNumber ceil operation diff --git a/test/integration/accounts.js b/test/integration/accounts.js index 9ad8453227..3115d01234 100644 --- a/test/integration/accounts.js +++ b/test/integration/accounts.js @@ -108,31 +108,25 @@ describe('Accounts', function () { it('spends coins', async () => { const ret = await aeSdk.spend(1, receiver) ret.should.have.property('tx') - ret.tx.should.include({ - amount: 1, recipientId: receiver - }) + ret.tx.should.include({ amount: 1n, recipientId: receiver }) }) it('spends coins in AE format', async () => { const ret = await aeSdk.spend(1, receiver, { denomination: AE_AMOUNT_FORMATS.AE }) ret.should.have.property('tx') - ret.tx.should.include({ - amount: `${1e18}`, recipientId: receiver - }) + ret.tx.should.include({ amount: 10n ** 18n, recipientId: receiver }) }) it('spends big amount of coins', async () => { - const bigAmount = '10000000000000100000000000000000' + const bigAmount = 10n ** 31n + 10n ** 17n const genesis = await BaseAe({ networkId }) const receiverWallet = generateKeyPair() const ret = await genesis.spend(bigAmount, receiverWallet.publicKey) const balanceAfter = await aeSdk.getBalance(receiverWallet.publicKey) - balanceAfter.should.be.equal(bigAmount) + balanceAfter.should.be.equal(bigAmount.toString()) ret.should.have.property('tx') - ret.tx.should.include({ - amount: bigAmount, recipientId: receiverWallet.publicKey - }) + ret.tx.should.include({ amount: bigAmount, recipientId: receiverWallet.publicKey }) }) it('Get Account by block height/hash', async () => { diff --git a/test/integration/contract-aci.js b/test/integration/contract-aci.js index e554651696..17154298ab 100644 --- a/test/integration/contract-aci.js +++ b/test/integration/contract-aci.js @@ -193,10 +193,10 @@ describe('Contract instance', function () { }) expect(deployInfo.address).to.satisfy(b => b.startsWith('ct_')) expect(deployInfo.txData.tx.gas).to.be.equal(16000) - expect(deployInfo.txData.tx.amount).to.be.equal(42) + expect(deployInfo.txData.tx.amount).to.be.equal(42n) expect(deployInfo.txData.gasUsed).to.be.equal(209) - expect(deployInfo.txData.gasPrice).to.be.equal(1000000000) - expect(deployInfo.txData.tx.deposit).to.be.equal(0) + expect(deployInfo.txData.gasPrice).to.be.equal(1000000000n) + expect(deployInfo.txData.tx.deposit).to.be.equal(0n) expect(testContract.bytecode).to.satisfy(b => b.startsWith('cb_')) testContractAddress = deployInfo.address }) diff --git a/test/integration/oracle.js b/test/integration/oracle.js index 19c27f8f94..d619532473 100644 --- a/test/integration/oracle.js +++ b/test/integration/oracle.js @@ -87,7 +87,7 @@ describe('Oracle', function () { describe('Oracle query fee settings', async () => { let oracleWithFee - const queryFee = 24000 + const queryFee = 24000n const account = generateKeyPair() before(async function () { @@ -98,7 +98,7 @@ describe('Oracle', function () { it('Post Oracle Query with default query fee', async () => { query = await oracle.postQuery("{'city': 'Berlin'}") - query.tx.queryFee.should.be.equal(QUERY_FEE) + query.tx.queryFee.should.be.equal(BigInt(QUERY_FEE)) }) it('Post Oracle Query with registered query fee', async () => { @@ -107,8 +107,8 @@ describe('Oracle', function () { }) it('Post Oracle Query with custom query fee', async () => { - query = await oracleWithFee.postQuery("{'city': 'Berlin'}", { queryFee: queryFee + 2000 }) - query.tx.queryFee.should.be.equal(queryFee + 2000) + query = await oracleWithFee.postQuery("{'city': 'Berlin'}", { queryFee: queryFee + 2000n }) + query.tx.queryFee.should.be.equal(queryFee + 2000n) }) }) }) diff --git a/tsconfig.json b/tsconfig.json index 9653420c15..852ea212c9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,8 +12,8 @@ "outDir": "./es", "noImplicitAny": true, "module": "es6", - "target": "es6", - "lib": ["es2019", "dom"], + "target": "es2020", + "lib": ["es2020", "dom"], "moduleResolution": "node", "preserveConstEnums": true, "declaration": true,