diff --git a/package.json b/package.json index 0dfc0150f..05c4d7d42 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/helper-builder-react-jsx-experimental": "7.12.11", "@cosmjs/stargate": "0.24.1", "@crypto-org-chain/chain-jslib": "1.0.3", + "@ethersproject/rlp": "^5.5.0", "@ledgerhq/hw-app-eth": "^6.6.0", "@ledgerhq/hw-transport-node-hid": "^6.6.0", "@ledgerhq/hw-transport-webhid": "5.48.0", @@ -36,6 +37,7 @@ "eth-sig-util": "3.0.1", "ethereumjs-tx": "2.1.2", "ethers": "5.4.4", + "fast-check": "^2.20.0", "i18next": "20.3.2", "i18next-browser-languagedetector": "6.1.2", "isutf8": "4.0.0", diff --git a/src/service/signers/LedgerSigner.spec.ts b/src/service/signers/LedgerSigner.spec.ts new file mode 100644 index 000000000..6d59d6c96 --- /dev/null +++ b/src/service/signers/LedgerSigner.spec.ts @@ -0,0 +1,19 @@ +import 'mocha'; +import * as fc from 'fast-check'; +import { DefaultWalletConfigs } from '../../config/StaticConfig'; +import sdk from '@crypto-org-chain/chain-jslib'; +import { Units } from '../../utils/ChainJsLib'; +import { assert } from 'console'; + +test('should declare coin in ledger correctly', () => { + fc.assert( + // both inclusive + fc.property(fc.bigInt(BigInt('1'), BigInt('10000000000000000000')), coinAmount => { + const cro = sdk.CroSDK({ network: DefaultWalletConfigs.TestNetConfig.network }); + const fee = new cro.Coin(coinAmount.toString(), Units.BASE); + assert(fee.toString() === coinAmount.toString()); + return true; + }), + { verbose: true }, + ); +}); diff --git a/src/service/signers/TransactionSigner.spec.ts b/src/service/signers/TransactionSigner.spec.ts index f2003578d..018f59c53 100644 --- a/src/service/signers/TransactionSigner.spec.ts +++ b/src/service/signers/TransactionSigner.spec.ts @@ -1,10 +1,11 @@ import 'mocha'; import { expect } from 'chai'; +import * as fc from 'fast-check'; import { DefaultWalletConfigs, WalletConfig } from '../../config/StaticConfig'; import { TransactionSigner } from './TransactionSigner'; import { TransactionUnsigned } from './TransactionSupported'; import { evmTransactionSigner } from './EvmTransactionSigner'; - +import * as RLP from '@ethersproject/rlp'; const testNet = DefaultWalletConfigs.TestNetConfig; // Overridden testnet chainId const testNetConfig: WalletConfig = { @@ -120,3 +121,36 @@ describe('Testing TransactionSigner', () => { ); }); }); + +test('should transfer correctly', async () => { + const phrase = + 'team school reopen cave banner pass autumn march immune album hockey region baby critic insect armor pigeon owner number velvet romance flight blame tone'; + + fc.assert( + fc.asyncProperty( + fc.integer({ min: 1, max: 100000000000 }) /* gas limit range */, + fc.bigInt(BigInt('1'), BigInt('100000000000000000000000000000000')) /* amount range */, + async (gasLimit, amount) => { + const transferTxSignedHex = await evmTransactionSigner.signTransfer( + { + amount: amount.toString(), + fromAddress: '0xc2aFcEC3DAfAF1a4f47030eE35Fd1A1231C08256', + gasLimit: gasLimit, + gasPrice: 5_000_000, + nonce: 12, + toAddress: '0x8875bF87684f46111dbc27725332CEA9C0f12D39', + accountNumber: 0, + accountSequence: 0, + memo: '', + }, + phrase, + ); + const decodedTransaction = RLP.decode(transferTxSignedHex); + expect(transferTxSignedHex !== '').to.eq(true); + expect(BigInt(decodedTransaction[4]) === amount).to.eq(true); + return true; + }, + ), + { verbose: true }, + ); +}); diff --git a/src/utils/NumberUtils.spec.ts b/src/utils/NumberUtils.spec.ts index a5a50828d..6887a977a 100644 --- a/src/utils/NumberUtils.spec.ts +++ b/src/utils/NumberUtils.spec.ts @@ -1,5 +1,7 @@ import 'mocha'; import { expect } from 'chai'; +import * as fc from 'fast-check'; +import { Big } from 'big.js'; import { adjustedTransactionAmount, fromScientificNotation, @@ -97,3 +99,55 @@ describe('Testing Number utils', () => { expect(adjustedTransactionAmount('0.1223', asset, networkFee)).to.eq('0.1223'); }); }); + +test('should adjust transaction correctly', () => { + fc.assert( + fc.property( + fc.integer({ min: 4, max: 16 }), + fc.bigInt(BigInt('0'), BigInt('100000000000000000000000000000000')), + fc.bigInt(BigInt('0'), BigInt('100000000000000000000000000000000')), + + (decimals, formamount, balance) => { + const { networkFee } = DefaultWalletConfigs.TestNetConfig.fee; + const asset: UserAsset = { + decimals: decimals, + mainnetSymbol: '', + balance: balance.toString(), + description: 'The best asset', + icon_url: 'some url', + identifier: 'cbd4bab2cbfd2b3', + name: 'Best Asset', + symbol: 'BEST', + walletId: '', + stakedBalance: '0', + unbondingBalance: '0', + rewardsBalance: '0', + }; + + const adjustedAmount = adjustedTransactionAmount(formamount.toString(), asset, networkFee); + expect(Big(adjustedAmount).gte(Big('0'))).to.eq(true); + expect(Big(adjustedAmount).lte(Big(formamount.toString()))).to.eq(true); + return true; + }, + ), + { verbose: true }, + ); +}); + +test('should scale amount correctly', () => { + fc.assert( + fc.property( + fc.integer({ min: 4, max: 16 }), + fc.bigInt(BigInt('1'), BigInt('100000000000000000000000000000000')), + (decimals, baseAmount) => { + const decimalAmount = getUINormalScaleAmount(baseAmount.toString(), decimals, decimals); + const restoredBaseAmount = Big(10) + .pow(decimals) + .mul(Big(decimalAmount)); + expect(restoredBaseAmount.toFixed()).to.eq(Big(baseAmount.toString()).toFixed()); + return true; + }, + ), + { verbose: true }, + ); +}); diff --git a/src/utils/NumberUtils.ts b/src/utils/NumberUtils.ts index 295a4a149..2f47f1d90 100644 --- a/src/utils/NumberUtils.ts +++ b/src/utils/NumberUtils.ts @@ -76,18 +76,20 @@ export function getCurrentMinAssetAmount(userAsset: UserAsset) { // When user selects option to send max amount, // transaction fee gets deduced to the sent amount for the transaction to be successful export function adjustedTransactionAmount( - formAmount: string = '0', + formAmount: string = '0' /* decimal unit */, walletAsset: UserAsset, fee: string, ): string { // Handle case for existing users const currentFee = fee ?? FIXED_DEFAULT_FEE; - // const availableBalance = Big('10000000000000000000000000'); const availableBalance = Big(scaledBalance(walletAsset)); const fixedFee = getNormalScaleAmount(currentFee, walletAsset); const amountAndFee = Big(formAmount).add(fixedFee); if (amountAndFee.gt(availableBalance)) { - return availableBalance.minus(fixedFee).toFixed(); + if (availableBalance.gte(fixedFee)) { + return availableBalance.minus(fixedFee).toFixed(); + } + return '0'; } return formAmount.toString(); }