From 15fc2c5378a428a4903bfd6c44ab64c1848f6f56 Mon Sep 17 00:00:00 2001 From: Ravi Hegde Date: Tue, 10 Jun 2025 11:05:54 +0530 Subject: [PATCH] feat(sdk-coin-near): added fungible token transfer builder Ticket: COIN-4148 --- modules/bitgo/src/v2/coinFactory.ts | 6 +- modules/bitgo/test/browser/browser.spec.ts | 1 + modules/sdk-coin-near/package.json | 5 +- modules/sdk-coin-near/src/lib/constants.ts | 4 + .../src/lib/contractCallWrapper.ts | 12 +- .../src/lib/fungibleTokenTransferBuilder.ts | 134 ++++++ modules/sdk-coin-near/src/lib/iface.ts | 18 +- .../src/lib/stakingActivateBuilder.ts | 13 +- .../src/lib/stakingDeactivateBuilder.ts | 13 +- .../src/lib/stakingWithdrawBuilder.ts | 13 +- .../src/lib/storageDepositTransferBuilder.ts | 87 ++++ modules/sdk-coin-near/src/lib/transaction.ts | 450 ++++++++++++++---- .../src/lib/transactionBuilder.ts | 37 +- .../src/lib/transactionBuilderFactory.ts | 15 + .../sdk-coin-near/src/lib/transferBuilder.ts | 7 +- modules/sdk-coin-near/src/lib/utils.ts | 2 +- modules/sdk-coin-near/src/near.ts | 4 +- modules/sdk-coin-near/src/nep141Token.ts | 6 +- modules/sdk-coin-near/test/resources/near.ts | 12 + modules/sdk-coin-near/test/unit/keyPair.ts | 4 +- modules/sdk-coin-near/test/unit/near.ts | 18 +- .../fungibleTokenTransferBuilder.ts | 322 +++++++++++++ .../stakingActivateBuilder.ts | 12 +- .../stakingDeactivateBuilder.ts | 14 +- .../stakingWithdrawBuilder.ts | 14 +- .../storageDepositTransferBuilder.ts | 220 +++++++++ .../transactionBuilder/transactionBuilder.ts | 4 +- .../transactionBuilder/transferBuilder.ts | 12 +- .../sdk-core/src/account-lib/baseCoin/enum.ts | 1 + yarn.lock | 325 ++++++++++--- 30 files changed, 1545 insertions(+), 240 deletions(-) create mode 100644 modules/sdk-coin-near/src/lib/fungibleTokenTransferBuilder.ts create mode 100644 modules/sdk-coin-near/src/lib/storageDepositTransferBuilder.ts create mode 100644 modules/sdk-coin-near/test/unit/transactionBuilder/fungibleTokenTransferBuilder.ts create mode 100644 modules/sdk-coin-near/test/unit/transactionBuilder/storageDepositTransferBuilder.ts diff --git a/modules/bitgo/src/v2/coinFactory.ts b/modules/bitgo/src/v2/coinFactory.ts index ca544dab64..39aea038ad 100644 --- a/modules/bitgo/src/v2/coinFactory.ts +++ b/modules/bitgo/src/v2/coinFactory.ts @@ -5,7 +5,7 @@ import { AdaToken } from '@bitgo/sdk-coin-ada'; import { AlgoToken } from '@bitgo/sdk-coin-algo'; import { Bcha, Tbcha } from '@bitgo/sdk-coin-bcha'; import { HbarToken } from '@bitgo/sdk-coin-hbar'; -import { Near, TNear } from '@bitgo/sdk-coin-near'; +import { Near, TNear, Nep141Token } from '@bitgo/sdk-coin-near'; import { SolToken } from '@bitgo/sdk-coin-sol'; import { TrxToken } from '@bitgo/sdk-coin-trx'; import { CoinFactory } from '@bitgo/sdk-core'; @@ -455,6 +455,10 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin Sip10Token.createTokenConstructors([...tokens.bitcoin.stx.tokens, ...tokens.testnet.stx.tokens]).forEach( ({ name, coinConstructor }) => coinFactory.register(name, coinConstructor) ); + + Nep141Token.createTokenConstructors([...tokens.bitcoin.near.tokens, ...tokens.testnet.near.tokens]).forEach( + ({ name, coinConstructor }) => coinFactory.register(name, coinConstructor) + ); } export const GlobalCoinFactory: CoinFactory = new CoinFactory(); diff --git a/modules/bitgo/test/browser/browser.spec.ts b/modules/bitgo/test/browser/browser.spec.ts index e7835a4cae..3cbc6cba5e 100644 --- a/modules/bitgo/test/browser/browser.spec.ts +++ b/modules/bitgo/test/browser/browser.spec.ts @@ -43,6 +43,7 @@ describe('Coins', () => { Polyx: 1, Tpolyx: 1, CoredaoToken: 1, + Nep141Token: 1, }; Object.keys(BitGoJS.Coin) .filter((coinName) => !excludedKeys[coinName]) diff --git a/modules/sdk-coin-near/package.json b/modules/sdk-coin-near/package.json index 35e610ebda..10e574f945 100644 --- a/modules/sdk-coin-near/package.json +++ b/modules/sdk-coin-near/package.json @@ -43,13 +43,14 @@ "@bitgo/sdk-core": "^35.2.0", "@bitgo/sdk-lib-mpc": "^10.5.0", "@bitgo/statics": "^54.2.0", + "@near-js/crypto": "^2.0.1", + "@near-js/transactions": "^2.0.1", "@stablelib/hex": "^1.0.0", "bignumber.js": "^9.0.0", - "bn.js": "^5.2.1", "bs58": "^4.0.1", "js-sha256": "^0.9.0", "lodash": "^4.17.14", - "near-api-js": "^0.44.2", + "near-api-js": "^5.1.1", "superagent": "^9.0.1", "tweetnacl": "^1.0.3" }, diff --git a/modules/sdk-coin-near/src/lib/constants.ts b/modules/sdk-coin-near/src/lib/constants.ts index 33d9886b10..3ef8f57aba 100644 --- a/modules/sdk-coin-near/src/lib/constants.ts +++ b/modules/sdk-coin-near/src/lib/constants.ts @@ -9,4 +9,8 @@ export const StakingContractMethodNames = { Withdraw: 'withdraw', } as const; +export const FT_TRANSFER = 'ft_transfer'; +export const STORAGE_DEPOSIT = 'storage_deposit'; +export const FUNGIBLE_TOKEN_RELATED_METHODS = [FT_TRANSFER, STORAGE_DEPOSIT]; + export const HEX_REGEX = /^[0-9a-fA-F]+$/; diff --git a/modules/sdk-coin-near/src/lib/contractCallWrapper.ts b/modules/sdk-coin-near/src/lib/contractCallWrapper.ts index 8a4f068fa1..8fff35d0b7 100644 --- a/modules/sdk-coin-near/src/lib/contractCallWrapper.ts +++ b/modules/sdk-coin-near/src/lib/contractCallWrapper.ts @@ -7,7 +7,7 @@ import { FunctionCall } from './iface'; */ export class ContractCallWrapper { private _methodName: string; - private _args: Record; + private _args: Record = {}; private _gas: string; private _deposit: string; @@ -21,7 +21,7 @@ export class ContractCallWrapper { return this._methodName; } - /** Set gas, expresed on yocto */ + /** Set gas, expressed on yocto */ public set gas(gas: string) { if (!this.isValidAmount(new BigNumber(gas))) { throw new InvalidParameterValueError('Invalid gas value'); @@ -29,12 +29,12 @@ export class ContractCallWrapper { this._gas = gas; } - /** Get gas, expresed on yocto*/ + /** Get gas, expressed on yocto*/ public get gas(): string { return this._gas; } - /** Set deposit, expresed on yocto */ + /** Set deposit, expressed on yocto */ public set deposit(deposit: string) { if (!this.isValidAmount(new BigNumber(deposit))) { throw new InvalidParameterValueError('Invalid deposit value'); @@ -42,14 +42,14 @@ export class ContractCallWrapper { this._deposit = deposit; } - /** Get deposit, expresed on yocto */ + /** Get deposit, expressed on yocto */ public get deposit(): string { return this._deposit; } /** Get args, which are the parameters of a method */ public set args(args: Record) { - this._args = args; + this._args = { ...this._args, ...args }; } /** Set args, which are the parameters of a method */ diff --git a/modules/sdk-coin-near/src/lib/fungibleTokenTransferBuilder.ts b/modules/sdk-coin-near/src/lib/fungibleTokenTransferBuilder.ts new file mode 100644 index 0000000000..411b7c5b0e --- /dev/null +++ b/modules/sdk-coin-near/src/lib/fungibleTokenTransferBuilder.ts @@ -0,0 +1,134 @@ +import assert from 'assert'; + +import BigNumber from 'bignumber.js'; +import * as NearAPI from 'near-api-js'; + +import { BuildTransactionError, TransactionType } from '@bitgo/sdk-core'; +import { BaseCoin as CoinConfig } from '@bitgo/statics'; + +import { FT_TRANSFER, STORAGE_DEPOSIT } from './constants'; +import { ContractCallWrapper } from './contractCallWrapper'; +import { StorageDepositInput } from './iface'; +import { Transaction } from './transaction'; +import { TransactionBuilder } from './transactionBuilder'; +import utils from './utils'; + +export class FungibleTokenTransferBuilder extends TransactionBuilder { + private contractCallWrapper: ContractCallWrapper; + + constructor(_coinConfig: Readonly) { + super(_coinConfig); + this.contractCallWrapper = new ContractCallWrapper(); + this.contractCallWrapper.methodName = FT_TRANSFER; + } + + /** + * Check if a transaction is a fungible token transfer + * + * @param {NearAPI.transactions.Action[]} actions near transaction actions + * @returns {Boolean} true if more than 1 action present or first action method name is ft transfer + */ + public static isFungibleTokenTransferTransaction(actions: NearAPI.transactions.Action[]): boolean { + return actions.length > 1 || actions[0].functionCall?.methodName === FT_TRANSFER; + } + + /** + * Initialize the transaction builder fields using the decoded transaction data + * + * @param {Transaction} tx the transaction data + */ + initBuilder(tx: Transaction): void { + super.initBuilder(tx); + for (const action of tx.nearTransaction.actions) { + if (action.functionCall && action.functionCall.methodName === FT_TRANSFER) { + this.contractCallWrapper.deposit = action.functionCall.deposit.toString(); + this.contractCallWrapper.gas = action.functionCall.gas.toString(); + } + } + } + + /** + * Sets the gas of this transaction. + * + * @param {String} gas the gas of this transaction + * @returns {TransactionBuilder} This transaction builder + */ + public gas(gas: string): this { + this.validateValue(new BigNumber(gas)); + this.contractCallWrapper.gas = gas; + return this; + } + + /** + * Sets the deposit of at-least 1 yoctoNear + * + * @param {string} deposit the deposit in the minimum unit (1 Near = 1e24 yoctoNear) of this transaction + * @returns {TransactionBuilder} This transaction builder + */ + public deposit(deposit: string): this { + this.validateValue(new BigNumber(deposit)); + this.contractCallWrapper.deposit = deposit; + return this; + } + + /** + * Sets the actual receiver account id inside args + * + * @param accountId the receiver account id + */ + public ftReceiverId(accountId: string): this { + utils.isValidAddress(accountId); + this.contractCallWrapper.args = { receiver_id: accountId }; + return this; + } + + /** + * Sets the ft amount to be transferred + * + * @param amount the amount of fungible token to be transferred + */ + public amount(amount: string): this { + this.validateValue(new BigNumber(amount)); + this.contractCallWrapper.args = { amount }; + return this; + } + + /** + * Sets the optional memo for the transfer + * + * @param memo + */ + public memo(memo: string): this { + this.contractCallWrapper.args = { memo }; + return this; + } + + /** + * Sets the storage deposit action + * + * @param {StorageDepositInput} input contains the deposit value, gas and optional account id + * if account id is not provided then it is self transfer + */ + public addStorageDeposit(input: StorageDepositInput): void { + const methodName = STORAGE_DEPOSIT; + assert(input.deposit, new BuildTransactionError('deposit is required before building storage deposit transfer')); + assert(input.gas, new BuildTransactionError('gas is required before building fungible token transfer')); + const args = input.accountId ? { account_id: input.accountId } : {}; + const action = NearAPI.transactions.functionCall(methodName, args, input.gas, input.deposit); + super.action(action); + } + + /** @inheritdoc */ + protected async buildImplementation(): Promise { + const { methodName, args, gas, deposit } = this.contractCallWrapper.getParams(); + assert(gas, new BuildTransactionError('gas is required before building fungible token transfer')); + assert(deposit, new BuildTransactionError('deposit is required before building fungible token transfer')); + + if (!this._actions || this._actions.length === 0 || this._actions[0].functionCall?.methodName !== methodName) { + super.action(NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))); + } + const tx = await super.buildImplementation(); + tx.setTransactionType(TransactionType.Send); + return tx; + } +} diff --git a/modules/sdk-coin-near/src/lib/iface.ts b/modules/sdk-coin-near/src/lib/iface.ts index bdb94de513..ac5fcf1c12 100644 --- a/modules/sdk-coin-near/src/lib/iface.ts +++ b/modules/sdk-coin-near/src/lib/iface.ts @@ -1,21 +1,17 @@ -import BN from 'bn.js'; +import { KeyType } from '@near-js/crypto'; import { TransactionType, TransactionExplanation as BaseTransactionExplanation } from '@bitgo/sdk-core'; export interface TransactionExplanation extends BaseTransactionExplanation { type: TransactionType; } -export enum KeyType { - ED25519 = 0, -} - export interface Signature { keyType: KeyType; data: Uint8Array; } export interface Transfer { - deposit: BN; + deposit: bigint; } /** Interface with parameters needed to perform FunctionCall to a contract */ @@ -38,9 +34,15 @@ export interface Action { export interface TxData { id?: string; signerId: string; - publicKey: string; - nonce: number; + publicKey?: string; + nonce: bigint; receiverId: string; actions: Action[]; signature?: Signature; } + +export interface StorageDepositInput { + deposit: bigint; + gas: bigint; + accountId?: string; +} diff --git a/modules/sdk-coin-near/src/lib/stakingActivateBuilder.ts b/modules/sdk-coin-near/src/lib/stakingActivateBuilder.ts index 65617a0224..25fe86bbb8 100644 --- a/modules/sdk-coin-near/src/lib/stakingActivateBuilder.ts +++ b/modules/sdk-coin-near/src/lib/stakingActivateBuilder.ts @@ -4,7 +4,6 @@ import { Transaction } from './transaction'; import BigNumber from 'bignumber.js'; import * as NearAPI from 'near-api-js'; import assert from 'assert'; -import BN from 'bn.js'; import { ContractCallWrapper } from './contractCallWrapper'; import { TransactionBuilder } from './transactionBuilder'; @@ -32,14 +31,16 @@ export class StakingActivateBuilder extends TransactionBuilder { initBuilder(tx: Transaction): void { super.initBuilder(tx); const functionCall = tx.nearTransaction.actions[0].functionCall; - this.contractCallWrapper.deposit = functionCall.deposit.toString(); - this.contractCallWrapper.gas = functionCall.gas.toString(); + if (functionCall) { + this.contractCallWrapper.deposit = functionCall.deposit.toString(); + this.contractCallWrapper.gas = functionCall.gas.toString(); + } } /** * Sets the gas of this transaction. * - * @param {string} value the gas of this transaction + * @param {string} gas the gas of this transaction * @returns {TransactionBuilder} This transaction builder */ public gas(gas: string): this { @@ -51,7 +52,7 @@ export class StakingActivateBuilder extends TransactionBuilder { /** * Sets the amount of this transaction. * - * @param {string} value the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction + * @param {string} amount the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction * @returns {TransactionBuilder} This transaction builder */ public amount(amount: string): this { @@ -67,7 +68,7 @@ export class StakingActivateBuilder extends TransactionBuilder { assert(gas, new BuildTransactionError('gas is required before building staking activate')); assert(deposit, new BuildTransactionError('amount is required before building staking activate')); - super.actions([NearAPI.transactions.functionCall(methodName, args, new BN(gas), new BN(deposit, 10))]); + super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]); const tx = await super.buildImplementation(); tx.setTransactionType(TransactionType.StakingActivate); return tx; diff --git a/modules/sdk-coin-near/src/lib/stakingDeactivateBuilder.ts b/modules/sdk-coin-near/src/lib/stakingDeactivateBuilder.ts index d3aa33eb74..6ce5e3e970 100644 --- a/modules/sdk-coin-near/src/lib/stakingDeactivateBuilder.ts +++ b/modules/sdk-coin-near/src/lib/stakingDeactivateBuilder.ts @@ -4,7 +4,6 @@ import { Transaction } from './transaction'; import BigNumber from 'bignumber.js'; import * as NearAPI from 'near-api-js'; import assert from 'assert'; -import BN from 'bn.js'; import { ContractCallWrapper } from './contractCallWrapper'; import { TransactionBuilder } from './transactionBuilder'; @@ -32,14 +31,16 @@ export class StakingDeactivateBuilder extends TransactionBuilder { initBuilder(tx: Transaction): void { super.initBuilder(tx); const functionCall = tx.nearTransaction.actions[0].functionCall; - this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString()); - this.contractCallWrapper.gas = functionCall.gas.toString(); + if (functionCall) { + this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString()); + this.contractCallWrapper.gas = functionCall.gas.toString(); + } } /** * Sets the gas of this transaction. * - * @param {string} value the gas of this transaction + * @param {string} gas the gas of this transaction * @returns {TransactionBuilder} This transaction builder */ public gas(gas: string): this { @@ -51,7 +52,7 @@ export class StakingDeactivateBuilder extends TransactionBuilder { /** * Sets the amount of this transaction. * - * @param {string} value the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction + * @param {string} amount the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction * @returns {TransactionBuilder} This transaction builder */ public amount(amount: string): this { @@ -66,7 +67,7 @@ export class StakingDeactivateBuilder extends TransactionBuilder { assert(gas, new BuildTransactionError('gas is required before building staking deactivate')); assert(args?.amount, new BuildTransactionError('amount is required before building staking deactivate')); - super.actions([NearAPI.transactions.functionCall(methodName, args, new BN(gas), new BN(deposit))]); + super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]); const tx = await super.buildImplementation(); tx.setTransactionType(TransactionType.StakingDeactivate); return tx; diff --git a/modules/sdk-coin-near/src/lib/stakingWithdrawBuilder.ts b/modules/sdk-coin-near/src/lib/stakingWithdrawBuilder.ts index 96f1fac962..149c5b9f33 100644 --- a/modules/sdk-coin-near/src/lib/stakingWithdrawBuilder.ts +++ b/modules/sdk-coin-near/src/lib/stakingWithdrawBuilder.ts @@ -4,7 +4,6 @@ import { Transaction } from './transaction'; import BigNumber from 'bignumber.js'; import * as NearAPI from 'near-api-js'; import assert from 'assert'; -import BN from 'bn.js'; import { ContractCallWrapper } from './contractCallWrapper'; import { TransactionBuilder } from './transactionBuilder'; @@ -32,14 +31,16 @@ export class StakingWithdrawBuilder extends TransactionBuilder { initBuilder(tx: Transaction): void { super.initBuilder(tx); const functionCall = tx.nearTransaction.actions[0].functionCall; - this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString()); - this.contractCallWrapper.gas = functionCall.gas.toString(); + if (functionCall) { + this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString()); + this.contractCallWrapper.gas = functionCall.gas.toString(); + } } /** * Sets the gas of this transaction. * - * @param {string} value the gas of this transaction + * @param {string} gas the gas of this transaction * @returns {TransactionBuilder} This transaction builder */ public gas(gas: string): this { @@ -51,7 +52,7 @@ export class StakingWithdrawBuilder extends TransactionBuilder { /** * Sets the amount of this transaction. * - * @param {string} value the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction + * @param {string} amount the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction * @returns {TransactionBuilder} This transaction builder */ public amount(amount: string): this { @@ -66,7 +67,7 @@ export class StakingWithdrawBuilder extends TransactionBuilder { assert(gas, new BuildTransactionError('gas is required before building staking withdraw')); assert(args?.amount, new BuildTransactionError('amount is required before building staking withdraw')); - super.actions([NearAPI.transactions.functionCall(methodName, args, new BN(gas), new BN(deposit))]); + super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]); const tx = await super.buildImplementation(); tx.setTransactionType(TransactionType.StakingWithdraw); return tx; diff --git a/modules/sdk-coin-near/src/lib/storageDepositTransferBuilder.ts b/modules/sdk-coin-near/src/lib/storageDepositTransferBuilder.ts new file mode 100644 index 0000000000..37ec368b6c --- /dev/null +++ b/modules/sdk-coin-near/src/lib/storageDepositTransferBuilder.ts @@ -0,0 +1,87 @@ +import assert from 'assert'; +import BigNumber from 'bignumber.js'; +import * as NearAPI from 'near-api-js'; + +import { BuildTransactionError, TransactionType } from '@bitgo/sdk-core'; +import { BaseCoin as CoinConfig } from '@bitgo/statics'; + +import { ContractCallWrapper } from './contractCallWrapper'; +import { Transaction } from './transaction'; +import { TransactionBuilder } from './transactionBuilder'; +import { STORAGE_DEPOSIT } from './constants'; +import utils from './utils'; + +export class StorageDepositTransferBuilder extends TransactionBuilder { + private contractCallWrapper: ContractCallWrapper; + + constructor(_coinConfig: Readonly) { + super(_coinConfig); + this.contractCallWrapper = new ContractCallWrapper(); + this.contractCallWrapper.methodName = STORAGE_DEPOSIT; + } + + /** + * Initialize the transaction builder fields using the decoded transaction data + * + * @param {Transaction} tx the transaction data + */ + initBuilder(tx: Transaction): void { + super.initBuilder(tx); + for (const action of tx.nearTransaction.actions) { + if (action.functionCall && action.functionCall.methodName === STORAGE_DEPOSIT) { + this.contractCallWrapper.deposit = action.functionCall.deposit.toString(); + this.contractCallWrapper.gas = action.functionCall.gas.toString(); + break; + } + } + } + + /** + * Sets the gas of this transaction. + * + * @param {String} gas the gas of this transaction + * @returns {TransactionBuilder} This transaction builder + */ + public gas(gas: string): this { + this.validateValue(new BigNumber(gas)); + this.contractCallWrapper.gas = gas; + return this; + } + + /** + * Sets the deposit of at-least 1 yoctoNear + * + * @param {string} deposit the deposit in the minimum unit (1 Near = 1e24 yoctoNear) of this transaction + * @returns {TransactionBuilder} This transaction builder + */ + public deposit(deposit: string): this { + this.validateValue(new BigNumber(deposit)); + this.contractCallWrapper.deposit = deposit; + return this; + } + + /** + * Sets the actual receiver account id inside args + * + * @param accountId the receiver account id + */ + public beneficiaryId(accountId: string): this { + utils.isValidAddress(accountId); + this.contractCallWrapper.args = { account_id: accountId }; + return this; + } + + /** @inheritdoc */ + protected async buildImplementation(): Promise { + const { methodName, args, gas, deposit } = this.contractCallWrapper.getParams(); + assert(gas, new BuildTransactionError('gas is required before building fungible token transfer')); + assert(deposit, new BuildTransactionError('deposit is required before building fungible token transfer')); + + if (!this._actions || !this._actions.length) { + super.action(NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))); + } + const tx = await super.buildImplementation(); + tx.setTransactionType(TransactionType.StorageDeposit); + return tx; + } +} diff --git a/modules/sdk-coin-near/src/lib/transaction.ts b/modules/sdk-coin-near/src/lib/transaction.ts index de7f55e413..4c74e1bc4f 100644 --- a/modules/sdk-coin-near/src/lib/transaction.ts +++ b/modules/sdk-coin-near/src/lib/transaction.ts @@ -1,19 +1,31 @@ +import base58 from 'bs58'; +import { sha256 } from 'js-sha256'; +import * as nearAPI from 'near-api-js'; +import { functionCall, transfer } from 'near-api-js/lib/transaction'; +import { KeyType } from '@near-js/crypto'; +import { Action as TxAction, SignedTransaction, Transaction as UnsignedTransaction } from '@near-js/transactions'; + import { BaseKey, BaseTransaction, Entry, InvalidTransactionError, + ITransactionRecipient, TransactionRecipient, TransactionType, } from '@bitgo/sdk-core'; -import { BaseCoin as CoinConfig } from '@bitgo/statics'; -import { TransactionExplanation, TxData, Action } from './iface'; -import { HEX_REGEX, StakingContractMethodNames } from './constants'; -import utils from './utils'; +import { BaseCoin as CoinConfig, NetworkType } from '@bitgo/statics'; + +import { + FT_TRANSFER, + FUNGIBLE_TOKEN_RELATED_METHODS, + HEX_REGEX, + StakingContractMethodNames, + STORAGE_DEPOSIT, +} from './constants'; +import { Action, Signature, TransactionExplanation, TxData } from './iface'; import { KeyPair } from './keyPair'; -import * as nearAPI from 'near-api-js'; -import * as sha256 from 'js-sha256'; -import base58 from 'bs58'; +import utils from './utils'; export class Transaction extends BaseTransaction { private _nearTransaction: nearAPI.transactions.Transaction; @@ -59,29 +71,48 @@ export class Transaction extends BaseTransaction { throw new InvalidTransactionError('Empty transaction data'); } - let parsedAction: Action = {}; - if (this._nearTransaction.actions[0].enum === 'transfer') { - parsedAction = { transfer: this._nearTransaction.actions[0].transfer }; - } else if (this._nearTransaction.actions[0].enum === 'functionCall') { - const functionCallObject = this._nearTransaction.actions[0].functionCall; - parsedAction = { - functionCall: { - methodName: functionCallObject.methodName, - args: JSON.parse(Buffer.from(functionCallObject.args).toString()), - gas: functionCallObject.gas.toString(), - deposit: functionCallObject.deposit.toString(), - }, + const parsedActions: Action[] = []; + const actions = this._nearTransaction.actions; + actions.map((action) => { + let parsedAction: Action = {}; + if (action.enum === 'transfer' && action.transfer) { + parsedAction = { transfer: action.transfer }; + } else if (action.enum === 'functionCall' && action.functionCall) { + const functionCall = action.functionCall; + parsedAction = { + functionCall: { + methodName: functionCall.methodName, + args: JSON.parse(Buffer.from(functionCall.args).toString()), + gas: functionCall.gas.toString(), + deposit: functionCall.deposit.toString(), + }, + }; + } + parsedActions.push(parsedAction); + }); + + let signature: Signature | undefined; + if (this._nearSignedTransaction?.signature?.ed25519Signature) { + signature = { + keyType: KeyType.ED25519, + data: new Uint8Array(this._nearSignedTransaction.signature.ed25519Signature.data), }; } + let publicKey: string | undefined; + if (this._nearTransaction.publicKey?.ed25519Key) { + const rawBytes = new Uint8Array(this._nearTransaction.publicKey.ed25519Key.data); + const encoded = nearAPI.utils.serialize.base_encode(rawBytes); + publicKey = `ed25519:${encoded}`; + } return { id: this._id, signerId: this._nearTransaction.signerId, - publicKey: this._nearTransaction.publicKey.toString(), + publicKey: publicKey, nonce: this._nearTransaction.nonce, receiverId: this._nearTransaction.receiverId, - actions: [parsedAction], - signature: typeof this._nearSignedTransaction === 'undefined' ? undefined : this._nearSignedTransaction.signature, + actions: parsedActions, + signature: signature ? signature : undefined, }; } @@ -103,22 +134,64 @@ export class Transaction extends BaseTransaction { const bufferRawTransaction = HEX_REGEX.test(rawTx) ? Buffer.from(rawTx, 'hex') : Buffer.from(rawTx, 'base64'); try { const signedTx = nearAPI.utils.serialize.deserialize( - nearAPI.transactions.SCHEMA, - nearAPI.transactions.SignedTransaction, + nearAPI.transactions.SCHEMA.SignedTransaction, bufferRawTransaction - ); - signedTx.transaction.nonce = parseInt(signedTx.transaction.nonce.toString(), 10); + ) as SignedTransaction; + signedTx.transaction.actions = signedTx.transaction.actions.map((a) => { + const action = new TxAction(a); + switch (action.enum) { + case 'transfer': { + if (action.transfer?.deposit) { + return transfer(BigInt(action.transfer.deposit)); + } + break; + } + case 'functionCall': { + if (action.functionCall) { + return functionCall( + action.functionCall.methodName, + new Uint8Array(action.functionCall.args), + BigInt(action.functionCall.gas), + BigInt(action.functionCall.deposit) + ); + } + break; + } + } + return action; + }); this._nearSignedTransaction = signedTx; this._nearTransaction = signedTx.transaction; this._id = utils.base58Encode(this.getTransactionHash()); } catch (e) { try { const unsignedTx = nearAPI.utils.serialize.deserialize( - nearAPI.transactions.SCHEMA, - nearAPI.transactions.Transaction, + nearAPI.transactions.SCHEMA.Transaction, bufferRawTransaction - ); - unsignedTx.nonce = parseInt(unsignedTx.nonce.toString(), 10); + ) as UnsignedTransaction; + unsignedTx.actions = unsignedTx.actions.map((a) => { + const action = new TxAction(a); + switch (action.enum) { + case 'transfer': { + if (action.transfer?.deposit) { + return transfer(BigInt(action.transfer.deposit)); + } + break; + } + case 'functionCall': { + if (action.functionCall) { + return functionCall( + action.functionCall.methodName, + new Uint8Array(action.functionCall.args), + BigInt(action.functionCall.gas), + BigInt(action.functionCall.deposit) + ); + } + break; + } + } + return action; + }); this._nearTransaction = unsignedTx; this._id = utils.base58Encode(this.getTransactionHash()); } catch (e) { @@ -155,7 +228,7 @@ export class Transaction extends BaseTransaction { * set transaction type by staking contract method names. * @param methodName method name to match and set the transaction type */ - private setTypeByStakingMethod(methodName: string): void { + private setTypeByFunctionCall(methodName: string): void { switch (methodName) { case StakingContractMethodNames.DepositAndStake: this.setTransactionType(TransactionType.StakingActivate); @@ -166,87 +239,195 @@ export class Transaction extends BaseTransaction { case StakingContractMethodNames.Withdraw: this.setTransactionType(TransactionType.StakingWithdraw); break; + case FT_TRANSFER: + this.setTransactionType(TransactionType.Send); + break; + case STORAGE_DEPOSIT: + this.setTransactionType(TransactionType.StorageDeposit); + break; } } /** * Check if method is allowed on Near account-lib implementation. * This method should check on all contracts added to Near. - * @param methodName contract call method name to check if its allowed. + * @param methodName contract call method name to check if it's allowed. */ private validateMethodAllowed(methodName: string): void { - if (!Object.values(StakingContractMethodNames).some((item) => item === methodName)) { + const allowedMethods = [...Object.values(StakingContractMethodNames), ...FUNGIBLE_TOKEN_RELATED_METHODS]; + if (!allowedMethods.includes(methodName)) { throw new InvalidTransactionError('unsupported function call in raw transaction'); } } + /** + * Check if valid methods are present for batching of actions + * + * @param {TxAction[]} actions list of near transaction actions + * @returns {void} + */ + private validateBatchingMethods(actions: TxAction[]): void { + actions.every((action) => { + if (action.enum !== 'functionCall' || !action.functionCall) { + throw new InvalidTransactionError('invalid action for batching'); + } + }); + if ( + actions[0].functionCall?.methodName !== STORAGE_DEPOSIT || + actions[1].functionCall?.methodName !== FT_TRANSFER + ) { + throw new InvalidTransactionError('invalid action sequence: expected storage_deposit followed by ft_transfer'); + } + } + /** * Build input and output field for this transaction * */ loadInputsAndOutputs(): void { - if (this._nearTransaction.actions.length !== 1) { + if (!this._nearTransaction.actions || !this._nearTransaction.actions.length) { + throw new InvalidTransactionError('no actions in raw transaction'); + } + // TODO: modify this for send-many support + // currently only storage deposit + ft transfer are allowed for batching + if (this._nearTransaction.actions.length > 2) { throw new InvalidTransactionError('too many actions in raw transaction'); } + // check for correct sequence of actions + if (this._nearTransaction.actions.length === 2) { + this.validateBatchingMethods(this._nearTransaction.actions); + } const action = this._nearTransaction.actions[0]; + const actions = this._nearTransaction.actions; switch (action.enum) { case 'transfer': this.setTransactionType(TransactionType.Send); break; case 'functionCall': - const methodName = action.functionCall.methodName; - this.validateMethodAllowed(methodName); - this.setTypeByStakingMethod(methodName); + if (action.functionCall) { + const methodName = action.functionCall.methodName; + this.validateMethodAllowed(methodName); + this.setTypeByFunctionCall(methodName); + } break; default: throw new InvalidTransactionError('unsupported action in raw transaction'); } + // if there are 2 actions, we know for sure that it is storage deposit + ft transfer + if (actions.length === 2) { + this.setTransactionType(TransactionType.Send); + } const outputs: Entry[] = []; const inputs: Entry[] = []; - switch (this.type) { - case TransactionType.Send: - const amount = action.transfer.deposit.toString(); - inputs.push({ - address: this._nearTransaction.signerId, - value: amount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: this._nearTransaction.receiverId, - value: amount, - coin: this._coinConfig.name, - }); - break; - case TransactionType.StakingActivate: - const stakingAmount = action.functionCall.deposit.toString(); - inputs.push({ - address: this._nearTransaction.signerId, - value: stakingAmount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: this._nearTransaction.receiverId, - value: stakingAmount, - coin: this._coinConfig.name, - }); - break; - case TransactionType.StakingWithdraw: - const stakingWithdrawAmount = JSON.parse(Buffer.from(action.functionCall.args).toString()).amount; - inputs.push({ - address: this._nearTransaction.receiverId, - value: stakingWithdrawAmount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: this._nearTransaction.signerId, - value: stakingWithdrawAmount, - coin: this._coinConfig.name, - }); - break; - } + actions.map((action) => { + switch (this.type) { + case TransactionType.Send: + if (action.transfer) { + const amount = action.transfer.deposit.toString(); + inputs.push({ + address: this._nearTransaction.signerId, + value: amount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: this._nearTransaction.receiverId, + value: amount, + coin: this._coinConfig.name, + }); + } else if (action.functionCall) { + if (action.functionCall.methodName === 'ft_transfer') { + const parsedArgs = JSON.parse(Buffer.from(action.functionCall.args).toString()); + inputs.push({ + address: parsedArgs.receiver_id, + value: parsedArgs.amount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: this._nearTransaction.signerId, + value: parsedArgs.amount, + coin: this._coinConfig.name, + }); + } + if (action.functionCall.methodName === 'storage_deposit') { + const parsedArgs = JSON.parse(Buffer.from(action.functionCall.args).toString()); + const receiverId = parsedArgs.account_id ? parsedArgs.account_id : this._nearTransaction.signerId; + inputs.push({ + address: receiverId, + value: action.functionCall.deposit.toString(), + coin: + this._coinConfig.network.type === NetworkType.TESTNET + ? `t${this._coinConfig.family}` + : this._coinConfig.family, + }); + outputs.push({ + address: this._nearTransaction.signerId, + value: action.functionCall.deposit.toString(), + coin: + this._coinConfig.network.type === NetworkType.TESTNET + ? `t${this._coinConfig.family}` + : this._coinConfig.family, + }); + } + } + break; + case TransactionType.StakingActivate: + if (action.functionCall) { + const stakingAmount = action.functionCall.deposit.toString(); + inputs.push({ + address: this._nearTransaction.signerId, + value: stakingAmount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: this._nearTransaction.receiverId, + value: stakingAmount, + coin: this._coinConfig.name, + }); + } + break; + case TransactionType.StakingWithdraw: + if (action.functionCall) { + const stakingWithdrawAmount = JSON.parse(Buffer.from(action.functionCall.args).toString()).amount; + inputs.push({ + address: this._nearTransaction.receiverId, + value: stakingWithdrawAmount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: this._nearTransaction.signerId, + value: stakingWithdrawAmount, + coin: this._coinConfig.name, + }); + } + break; + case TransactionType.StorageDeposit: { + if (action.functionCall) { + const parsedArgs = JSON.parse(Buffer.from(action.functionCall.args).toString()); + const receiverId = parsedArgs.account_id ? parsedArgs.account_id : this._nearTransaction.signerId; + inputs.push({ + address: receiverId, + value: action.functionCall.deposit.toString(), + coin: + this._coinConfig.network.type === NetworkType.TESTNET + ? `t${this._coinConfig.family}` + : this._coinConfig.family, + }); + outputs.push({ + address: this._nearTransaction.signerId, + value: action.functionCall.deposit.toString(), + coin: + this._coinConfig.network.type === NetworkType.TESTNET + ? `t${this._coinConfig.family}` + : this._coinConfig.family, + }); + } + break; + } + } + }); this._outputs = outputs; this._inputs = inputs; } @@ -309,6 +490,103 @@ export class Transaction extends BaseTransaction { }; } + /** + * Calculates the total fungible token amount & total native near amount + * + * @param {Action[]} actions the list of delegate transaction actions + * @returns {String} either native near amount or fungible token amount + */ + calculateTotalOutputAmount(actions: Action[]): string { + let hasFtTransfer = false; + let hasStorageDeposit = false; + + let totalTokenAmount = BigInt(0); + let totalNearDeposit = BigInt(0); + + for (const action of actions) { + if (action.functionCall) { + const functionCall = action.functionCall; + const methodName = functionCall.methodName; + const args = functionCall.args; + const deposit = BigInt(functionCall.deposit); + if (methodName === FT_TRANSFER) { + hasFtTransfer = true; + const amountStr = args['amount'] as string; + if (args.amount) { + totalTokenAmount += BigInt(amountStr); + } + } + if (methodName === STORAGE_DEPOSIT) { + hasStorageDeposit = true; + totalNearDeposit += deposit; + } + } + } + if (hasFtTransfer && hasStorageDeposit) { + return totalTokenAmount.toString(); + } else if (!hasFtTransfer && hasStorageDeposit) { + return totalNearDeposit.toString(); + } + return ''; + } + + /** + * Returns a complete explanation for a token transfer transaction + * @param {TxData} json The transaction data in json format + * @param {TransactionExplanation} explanationResult The transaction explanation to be completed + * @returns {TransactionExplanation} + */ + explainTokenTransferTransaction(json: TxData, explanationResult: TransactionExplanation): TransactionExplanation { + const actions = json.actions; + const outputAmount = this.calculateTotalOutputAmount(actions); + const outputs: ITransactionRecipient[] = []; + actions.map((action) => { + if (action.functionCall) { + const functionCall = action.functionCall; + if (functionCall.methodName === FT_TRANSFER) { + const amountStr = functionCall.args['amount'] as string; + outputs.push({ + address: json.receiverId, + amount: amountStr, + }); + } + } + }); + return { + ...explanationResult, + outputAmount: outputAmount, + outputs: outputs, + }; + } + + /** + * Returns a complete explanation for a storage deposit transaction + * @param {TxData} json The transaction data in json format + * @param {TransactionExplanation} explanationResult The transaction explanation to be completed + * @returns {TransactionExplanation} + */ + explainStorageDepositTransaction(json: TxData, explanationResult: TransactionExplanation): TransactionExplanation { + const actions = json.actions; + const outputAmount = this.calculateTotalOutputAmount(actions); + const outputs: ITransactionRecipient[] = []; + actions.map((action) => { + if (action.functionCall) { + const functionCall = action.functionCall; + if (functionCall.methodName === STORAGE_DEPOSIT) { + outputs.push({ + address: json.receiverId, + amount: functionCall.deposit, + }); + } + } + }); + return { + ...explanationResult, + outputAmount: outputAmount, + outputs: outputs, + }; + } + /** @inheritdoc */ explainTransaction(): TransactionExplanation { const result = this.toJson(); @@ -327,6 +605,9 @@ export class Transaction extends BaseTransaction { }; switch (this.type) { case TransactionType.Send: + if (result.actions.length > 1 || result.actions[0].functionCall) { + return this.explainTokenTransferTransaction(result, explanationResult); + } return this.explainTransferTransaction(result, explanationResult); case TransactionType.StakingActivate: return this.explainStakingActivateTransaction(result, explanationResult); @@ -334,14 +615,19 @@ export class Transaction extends BaseTransaction { return explanationResult; case TransactionType.StakingWithdraw: return this.explainStakingWithdrawTransaction(result, explanationResult); + case TransactionType.StorageDeposit: + return this.explainStorageDepositTransaction(result, explanationResult); default: throw new InvalidTransactionError('Transaction type not supported'); } } private getTransactionHash(): Uint8Array { - const serializedTx = nearAPI.utils.serialize.serialize(nearAPI.transactions.SCHEMA, this._nearTransaction); - return new Uint8Array(sha256.sha256.array(serializedTx)); + const serializedTx = nearAPI.utils.serialize.serialize( + nearAPI.transactions.SCHEMA.Transaction, + this._nearTransaction + ); + return new Uint8Array(sha256.array(serializedTx)); } get signablePayload(): Buffer { @@ -373,8 +659,8 @@ export class Transaction extends BaseTransaction { get signature(): string[] { const signatures: string[] = []; - if (this._nearSignedTransaction) { - signatures.push(base58.encode(this._nearSignedTransaction.signature.data)); + if (this._nearSignedTransaction && this._nearSignedTransaction.signature.ed25519Signature) { + signatures.push(base58.encode(this._nearSignedTransaction.signature.ed25519Signature.data)); } return signatures; diff --git a/modules/sdk-coin-near/src/lib/transactionBuilder.ts b/modules/sdk-coin-near/src/lib/transactionBuilder.ts index 6ad7969d9f..eb01fee92a 100644 --- a/modules/sdk-coin-near/src/lib/transactionBuilder.ts +++ b/modules/sdk-coin-near/src/lib/transactionBuilder.ts @@ -20,8 +20,8 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { private _sender: string; private _publicKey: string; - private _receiverId: string; - private _nonce: number; + protected _receiverId: string; + private _nonce: bigint; private _recentBlockHash: string; private _signer: KeyPair; private _signatures: Signature[] = []; // only support single sig for now @@ -43,8 +43,10 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { this._sender = nearTransaction.signerId; this._nonce = nearTransaction.nonce; this._receiverId = nearTransaction.receiverId; - this._publicKey = hex.encode(nearTransaction.publicKey.data); - this._recentBlockHash = nearAPI.utils.serialize.base_encode(nearTransaction.blockHash); + if (nearTransaction.publicKey.ed25519Key?.data) { + this._publicKey = hex.encode(nearTransaction.publicKey.ed25519Key.data); + } + this._recentBlockHash = nearAPI.utils.serialize.base_encode(new Uint8Array(nearTransaction.blockHash)); this._actions = nearTransaction.actions; } @@ -107,18 +109,10 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { /** @inheritdoc */ validateRawTransaction(rawTransaction: any): void { try { - nearAPI.utils.serialize.deserialize( - nearAPI.transactions.SCHEMA, - nearAPI.transactions.SignedTransaction, - rawTransaction - ); + nearAPI.utils.serialize.deserialize(nearAPI.transactions.SCHEMA.SignedTransaction, rawTransaction); } catch { try { - nearAPI.utils.serialize.deserialize( - nearAPI.transactions.SCHEMA, - nearAPI.transactions.Transaction, - rawTransaction - ); + nearAPI.utils.serialize.deserialize(nearAPI.transactions.SCHEMA.Transaction, rawTransaction); } catch { throw new BuildTransactionError('invalid raw transaction'); } @@ -177,10 +171,10 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { /** * Set the nonce * - * @param {number} nonce - number that can be only used once + * @param {bigint} nonce - number that can be only used once * @returns {TransactionBuilder} This transaction builder */ - public nonce(nonce: number): this { + public nonce(nonce: bigint): this { if (nonce < 0) { throw new BuildTransactionError(`Invalid nonce: ${nonce}`); } @@ -212,6 +206,17 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { this._actions = value; return this; } + + /** + * Sets the action for this transaction/ + * + * @param {nearAPI.transactions.Action} value the delegate action + * @returns {TransactionBuilder} The delegate transaction builder + */ + protected action(value: nearAPI.transactions.Action): this { + this._actions ? this._actions.push(value) : (this._actions = [value]); + return this; + } /** * Builds the NEAR transaction. * diff --git a/modules/sdk-coin-near/src/lib/transactionBuilderFactory.ts b/modules/sdk-coin-near/src/lib/transactionBuilderFactory.ts index b8f7dff6c7..1a36642e41 100644 --- a/modules/sdk-coin-near/src/lib/transactionBuilderFactory.ts +++ b/modules/sdk-coin-near/src/lib/transactionBuilderFactory.ts @@ -7,6 +7,8 @@ import { Transaction } from './transaction'; import { StakingActivateBuilder } from './stakingActivateBuilder'; import { StakingDeactivateBuilder } from './stakingDeactivateBuilder'; import { StakingWithdrawBuilder } from './stakingWithdrawBuilder'; +import { FungibleTokenTransferBuilder } from './fungibleTokenTransferBuilder'; +import { StorageDepositTransferBuilder } from './storageDepositTransferBuilder'; export class TransactionBuilderFactory extends BaseTransactionBuilderFactory { constructor(_coinConfig: Readonly) { @@ -20,6 +22,9 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory { tx.fromRawTransaction(raw); switch (tx.type) { case TransactionType.Send: + if (FungibleTokenTransferBuilder.isFungibleTokenTransferTransaction(tx.nearTransaction.actions)) { + return this.getFungibleTokenTransferBuilder(tx); + } return this.getTransferBuilder(tx); case TransactionType.WalletInitialization: return this.getWalletInitializationBuilder(tx); @@ -29,6 +34,8 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory { return this.getStakingDeactivateBuilder(tx); case TransactionType.StakingWithdraw: return this.getStakingWithdrawBuilder(tx); + case TransactionType.StorageDeposit: + return this.getStorageDepositTransferBuilder(tx); default: throw new InvalidTransactionError('unsupported transaction'); } @@ -59,6 +66,14 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory { return TransactionBuilderFactory.initializeBuilder(tx, new StakingWithdrawBuilder(this._coinConfig)); } + getFungibleTokenTransferBuilder(tx?: Transaction): FungibleTokenTransferBuilder { + return TransactionBuilderFactory.initializeBuilder(tx, new FungibleTokenTransferBuilder(this._coinConfig)); + } + + getStorageDepositTransferBuilder(tx?: Transaction): StorageDepositTransferBuilder { + return TransactionBuilderFactory.initializeBuilder(tx, new StorageDepositTransferBuilder(this._coinConfig)); + } + /** * Initialize the builder with the given transaction * diff --git a/modules/sdk-coin-near/src/lib/transferBuilder.ts b/modules/sdk-coin-near/src/lib/transferBuilder.ts index 24a04bf413..24220ae621 100644 --- a/modules/sdk-coin-near/src/lib/transferBuilder.ts +++ b/modules/sdk-coin-near/src/lib/transferBuilder.ts @@ -4,7 +4,6 @@ import { TransactionBuilder } from './transactionBuilder'; import { Transaction } from './transaction'; import * as NearAPI from 'near-api-js'; import assert from 'assert'; -import BN from 'bn.js'; import BigNumber from 'bignumber.js'; export class TransferBuilder extends TransactionBuilder { @@ -21,13 +20,15 @@ export class TransferBuilder extends TransactionBuilder { */ initBuilder(tx: Transaction): void { super.initBuilder(tx); - this._amount = tx.nearTransaction.actions[0].transfer.deposit.toString(); + if (tx.nearTransaction.actions[0].transfer) { + this._amount = tx.nearTransaction.actions[0].transfer.deposit.toString(); + } } /** @inheritdoc */ protected async buildImplementation(): Promise { assert(this._amount, new BuildTransactionError('amount is required before building transfer')); - super.actions([NearAPI.transactions.transfer(new BN(this._amount))]); + super.actions([NearAPI.transactions.transfer(BigInt(this._amount))]); const tx = await super.buildImplementation(); tx.setTransactionType(TransactionType.Send); return tx; diff --git a/modules/sdk-coin-near/src/lib/utils.ts b/modules/sdk-coin-near/src/lib/utils.ts index ca371799f0..0ff2914737 100644 --- a/modules/sdk-coin-near/src/lib/utils.ts +++ b/modules/sdk-coin-near/src/lib/utils.ts @@ -73,7 +73,7 @@ export class Utils implements BaseUtils { * * @param {string} value - string to be checked * @param {number} length - expected decoded length - * @return {boolean} if the string can decoded as base58 and match the expected length + * @return {boolean} if the string can be decoded as base58 and match the expected length */ isBase58(value: string, length: number): boolean { try { diff --git a/modules/sdk-coin-near/src/near.ts b/modules/sdk-coin-near/src/near.ts index c7dde5da05..7d0874ccd8 100644 --- a/modules/sdk-coin-near/src/near.ts +++ b/modules/sdk-coin-near/src/near.ts @@ -52,7 +52,7 @@ export interface TransactionPrebuild { txHex: string; key: string; blockHash: string; - nonce: number; + nonce: bigint; } export interface ExplainTransactionOptions { @@ -94,7 +94,7 @@ interface RecoveryOptions { } interface NearTxBuilderParamsFromNode { - nonce: number; + nonce: bigint; blockHash: string; } diff --git a/modules/sdk-coin-near/src/nep141Token.ts b/modules/sdk-coin-near/src/nep141Token.ts index 438aa9c7c2..1d86ec828a 100644 --- a/modules/sdk-coin-near/src/nep141Token.ts +++ b/modules/sdk-coin-near/src/nep141Token.ts @@ -16,9 +16,11 @@ export class Nep141Token extends Near { return (bitgo: BitGoBase) => new Nep141Token(bitgo, config); } - static createTokenConstructors(): NamedCoinConstructor[] { + static createTokenConstructors( + tokenConfig: Nep141TokenConfig[] = [...tokens.bitcoin.near.tokens, ...tokens.testnet.near.tokens] + ): NamedCoinConstructor[] { const tokensCtors: NamedCoinConstructor[] = []; - for (const token of [...tokens.bitcoin.near.tokens, ...tokens.testnet.near.tokens]) { + for (const token of tokenConfig) { const tokenConstructor = Nep141Token.createTokenConstructor(token); tokensCtors.push({ name: token.type, coinConstructor: tokenConstructor }); } diff --git a/modules/sdk-coin-near/test/resources/near.ts b/modules/sdk-coin-near/test/resources/near.ts index a787dc8701..72ec76c707 100644 --- a/modules/sdk-coin-near/test/resources/near.ts +++ b/modules/sdk-coin-near/test/resources/near.ts @@ -92,6 +92,18 @@ export const rawTx = { unsigned: 'QAAAADYxYjE4YzZkYzAyZGRjYWJkZWFjNTZjYjRmMjFhOTcxY2M0MWNjOTc2NDBmNmY4NWIwNzM0ODAwMDhjNTNhMGQAYbGMbcAt3KverFbLTyGpccxBzJdkD2+FsHNIAAjFOg0BAAAAAAAAABwAAABsYXZlbmRlcmZpdmUucG9vbC5mODYzOTczLm0wppNL00/j8LLRb+dQg6da599fp9XXZsr3QyxL4aKNJmABAAAAAggAAAB3aXRoZHJhdxQAAAB7ImFtb3VudCI6IjEwMDAwMDAifQDQmNSvcQAAAAAAAAAAAAAAAAAAAAAAAA==', }, + fungibleTokenTransfer: { + signed: + 'QAAAADYxYjE4YzZkYzAyZGRjYWJkZWFjNTZjYjRmMjFhOTcxY2M0MWNjOTc2NDBmNmY4NWIwNzM0ODAwMDhjNTNhMGQAYbGMbcAt3KverFbLTyGpccxBzJdkD2+FsHNIAAjFOg0BAAAAAAAAABMAAABmdC10bmVwMjRkcC50ZXN0bmV0ppNL00/j8LLRb+dQg6da599fp9XXZsr3QyxL4aKNJmABAAAAAgsAAABmdF90cmFuc2Zlcm8AAAB7InJlY2VpdmVyX2lkIjoiOWY3YjA2NzVkYjU5ZDE5YjRiZDljOGM3MmVhYWJiYTc1YTk4NjNkMDJiMzAxMTViOGIzYzNjYTVjMjBmMDI1NCIsImFtb3VudCI6IjEwMCIsIm1lbW8iOiJ0ZXN0In0A0JjUr3EAAAEAAAAAAAAAAAAAAAAAAAAAgaGrAZpNgVyjRUgas5GcjmEJyC/SROgXQk3AHW1mpO6yjzV3ElsM2pcxFfflvTi3oDQSFFh1ScVbkmya4QEaDA==', + unsigned: + 'QAAAADYxYjE4YzZkYzAyZGRjYWJkZWFjNTZjYjRmMjFhOTcxY2M0MWNjOTc2NDBmNmY4NWIwNzM0ODAwMDhjNTNhMGQAYbGMbcAt3KverFbLTyGpccxBzJdkD2+FsHNIAAjFOg0BAAAAAAAAABMAAABmdC10bmVwMjRkcC50ZXN0bmV0ppNL00/j8LLRb+dQg6da599fp9XXZsr3QyxL4aKNJmABAAAAAgsAAABmdF90cmFuc2Zlcm8AAAB7InJlY2VpdmVyX2lkIjoiOWY3YjA2NzVkYjU5ZDE5YjRiZDljOGM3MmVhYWJiYTc1YTk4NjNkMDJiMzAxMTViOGIzYzNjYTVjMjBmMDI1NCIsImFtb3VudCI6IjEwMCIsIm1lbW8iOiJ0ZXN0In0A0JjUr3EAAAEAAAAAAAAAAAAAAAAAAAA=', + }, + storageDeposit: { + signed: + 'QAAAADYxYjE4YzZkYzAyZGRjYWJkZWFjNTZjYjRmMjFhOTcxY2M0MWNjOTc2NDBmNmY4NWIwNzM0ODAwMDhjNTNhMGQAYbGMbcAt3KverFbLTyGpccxBzJdkD2+FsHNIAAjFOg0BAAAAAAAAABMAAABmdC10bmVwMjRkcC50ZXN0bmV0ppNL00/j8LLRb+dQg6da599fp9XXZsr3QyxL4aKNJmABAAAAAg8AAABzdG9yYWdlX2RlcG9zaXQCAAAAe30A0JjUr3EAAAAASFY3GTzDQwAAAAAAAAAAqH6xLHXPvsld56HLmtj16VGVHtxSY3TSUyVKpKOy2IBgRr4A2c7eyo8l/v6/KfXS3bOIFrIPX/zWw4qqaL7mAw==', + unsigned: + 'QAAAADYxYjE4YzZkYzAyZGRjYWJkZWFjNTZjYjRmMjFhOTcxY2M0MWNjOTc2NDBmNmY4NWIwNzM0ODAwMDhjNTNhMGQAYbGMbcAt3KverFbLTyGpccxBzJdkD2+FsHNIAAjFOg0BAAAAAAAAABMAAABmdC10bmVwMjRkcC50ZXN0bmV0ppNL00/j8LLRb+dQg6da599fp9XXZsr3QyxL4aKNJmABAAAAAg8AAABzdG9yYWdlX2RlcG9zaXQCAAAAe30A0JjUr3EAAAAASFY3GTzDQwAAAAAAAAA=', + }, }; export const AMOUNT = '1000000000000000000000000'; diff --git a/modules/sdk-coin-near/test/unit/keyPair.ts b/modules/sdk-coin-near/test/unit/keyPair.ts index f643ccc4f5..2f11acafa0 100644 --- a/modules/sdk-coin-near/test/unit/keyPair.ts +++ b/modules/sdk-coin-near/test/unit/keyPair.ts @@ -52,14 +52,14 @@ describe('NEAR KeyPair', () => { const source = { pub: '01D63D', }; - assert.throws(() => new KeyPair(source), /Non-base58 character/); + assert.throws(() => new KeyPair(source), /Unknown letter/); }); it('should fail to create from an invalid private key', () => { const source = { prv: '82A34', }; - assert.throws(() => new KeyPair(source), /Non-base58 character/); + assert.throws(() => new KeyPair(source), /Unknown letter/); }); }); diff --git a/modules/sdk-coin-near/test/unit/near.ts b/modules/sdk-coin-near/test/unit/near.ts index 83bd505223..48692cc140 100644 --- a/modules/sdk-coin-near/test/unit/near.ts +++ b/modules/sdk-coin-near/test/unit/near.ts @@ -273,7 +273,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -300,7 +300,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -322,7 +322,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -404,7 +404,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -440,7 +440,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -471,7 +471,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -600,7 +600,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -624,7 +624,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); @@ -648,7 +648,7 @@ describe('NEAR:', function () { .sender(accounts.account1.address, accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: accounts.account1.secretKey }); const tx = await txBuilder.build(); const txToBroadcastFormat = tx.toBroadcastFormat(); diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/fungibleTokenTransferBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/fungibleTokenTransferBuilder.ts new file mode 100644 index 0000000000..6cc5b03414 --- /dev/null +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/fungibleTokenTransferBuilder.ts @@ -0,0 +1,322 @@ +import should from 'should'; +import * as base58 from 'bs58'; + +import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; +import { BitGoAPI } from '@bitgo/sdk-api'; +import { Eddsa, TransactionType } from '@bitgo/sdk-core'; +import { coins } from '@bitgo/statics'; + +import * as testData from '../../resources/near'; +import { getBuilderFactory } from '../getBuilderFactory'; +import { KeyPair, Nep141Token, TransactionBuilderFactory } from '../../../src'; +import { StorageDepositInput } from '../../../src/lib/iface'; + +describe('Near: Fungible Token Transfer Builder', () => { + const coinName = 'near:usdc'; + const coinNameTest = 'tnear:tnep24dp'; + let nep141Token: Nep141Token; + let bitgo: TestBitGoAPI; + + before(function () { + bitgo = TestBitGo.decorate(BitGoAPI, { + env: 'mock', + }); + bitgo.initializeTestVars(); + Nep141Token.createTokenConstructors().forEach(({ name, coinConstructor }) => { + bitgo.safeRegister(name, coinConstructor); + }); + nep141Token = bitgo.coin(coinNameTest) as Nep141Token; + }); + + describe('Nep141 fungible token transfer builder', function () { + const factory = new TransactionBuilderFactory(coins.get(coinNameTest)); + const factoryProd = new TransactionBuilderFactory(coins.get(coinName)); + const gas = '125000000000000'; + const deposit = '1'; + + const initTxBuilder = () => { + const txBuilder = factory.getFungibleTokenTransferBuilder(); + txBuilder.gas(gas); + txBuilder.deposit(deposit); + txBuilder.ftReceiverId(testData.accounts.account2.address); + txBuilder.amount('100'); + txBuilder.memo('test'); + txBuilder.nonce(BigInt(1)); + txBuilder.receiverId(nep141Token.contractAddress); + txBuilder.recentBlockHash(testData.blockHash.block1); + return txBuilder; + }; + + describe('fungible token builder environment', function () { + it('should select the right network', function () { + should.equal(factory.getFungibleTokenTransferBuilder().coinName(), coinNameTest); + should.equal(factoryProd.getFungibleTokenTransferBuilder().coinName(), coinName); + // used type any to access protected properties + const txBuilder: any = factory.getFungibleTokenTransferBuilder(); + const txBuilderProd: any = factoryProd.getFungibleTokenTransferBuilder(); + + txBuilder._coinConfig.name.should.deepEqual(coinNameTest); + txBuilderProd._coinConfig.name.should.deepEqual(coinName); + }); + }); + + describe('should build', function () { + it('an unsigned fungible token transfer transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account2.address, + value: '100', + coin: coinNameTest, + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '100', + coin: coinNameTest, + }); + const rawTx = tx.toBroadcastFormat(); + rawTx.should.deepEqual(testData.rawTx.fungibleTokenTransfer.unsigned); + }); + + it('a signed fungible token transfer transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + builder.sign({ key: testData.accounts.account1.secretKey }); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account2.address, + value: '100', + coin: coinNameTest, + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '100', + coin: coinNameTest, + }); + const rawTx = tx.toBroadcastFormat(); + rawTx.should.deepEqual(testData.rawTx.fungibleTokenTransfer.signed); + }); + + it('an unsigned fungible token transfer with self storage deposit transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const storageDepositInput: StorageDepositInput = { + deposit: BigInt('1250000000000000000000'), + gas: BigInt(125000000000000), + }; + builder.addStorageDeposit(storageDepositInput); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(2); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.inputs[1].should.deepEqual({ + address: testData.accounts.account2.address, + value: '100', + coin: coinNameTest, + }); + tx.outputs.length.should.equal(2); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs[1].should.deepEqual({ + address: testData.accounts.account1.address, + value: '100', + coin: coinNameTest, + }); + }); + + it('a signed fungible token transfer with self storage deposit transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const storageDepositInput: StorageDepositInput = { + deposit: BigInt('1250000000000000000000'), + gas: BigInt(125000000000000), + }; + builder.addStorageDeposit(storageDepositInput); + builder.sign({ key: testData.accounts.account1.secretKey }); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(2); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.inputs[1].should.deepEqual({ + address: testData.accounts.account2.address, + value: '100', + coin: coinNameTest, + }); + tx.outputs.length.should.equal(2); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs[1].should.deepEqual({ + address: testData.accounts.account1.address, + value: '100', + coin: coinNameTest, + }); + }); + + it('an unsigned fungible token transfer with storage deposit transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const storageDepositInput: StorageDepositInput = { + deposit: BigInt('1250000000000000000000'), + gas: BigInt(125000000000000), + accountId: testData.accounts.account2.address, + }; + builder.addStorageDeposit(storageDepositInput); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(2); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account2.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.inputs[1].should.deepEqual({ + address: testData.accounts.account2.address, + value: '100', + coin: coinNameTest, + }); + tx.outputs.length.should.equal(2); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs[1].should.deepEqual({ + address: testData.accounts.account1.address, + value: '100', + coin: coinNameTest, + }); + }); + + it('a signed fungible token transfer with storage deposit transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const storageDepositInput: StorageDepositInput = { + deposit: BigInt('1250000000000000000000'), + gas: BigInt(125000000000000), + accountId: testData.accounts.account2.address, + }; + builder.addStorageDeposit(storageDepositInput); + builder.sign({ key: testData.accounts.account1.secretKey }); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(2); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account2.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.inputs[1].should.deepEqual({ + address: testData.accounts.account2.address, + value: '100', + coin: coinNameTest, + }); + tx.outputs.length.should.equal(2); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs[1].should.deepEqual({ + address: testData.accounts.account1.address, + value: '100', + coin: coinNameTest, + }); + }); + }); + + describe('add TSS signature', function () { + let MPC: Eddsa; + + before('initialize mpc module', async () => { + MPC = await Eddsa.initialize(); + }); + it('should add TSS signature', async () => { + const factory = getBuilderFactory(coinNameTest); + const A = MPC.keyShare(1, 2, 3); + const B = MPC.keyShare(2, 2, 3); + const C = MPC.keyShare(3, 2, 3); + + const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]); + const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]); + const C_combine = MPC.keyCombine(C.uShare, [A.yShares[3], B.yShares[3]]); + + const commonPub = A_combine.pShare.y; + const nearKeyPair = new KeyPair({ pub: commonPub }); + const sender = nearKeyPair.getAddress(); + + let txBuilder = initTxBuilder(); + txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const unsignedTransaction = await txBuilder.build(); + const signablePayload = unsignedTransaction.signablePayload; + + // signing with A and B + let A_sign_share = MPC.signShare(signablePayload, A_combine.pShare, [A_combine.jShares[2]]); + let B_sign_share = MPC.signShare(signablePayload, B_combine.pShare, [B_combine.jShares[1]]); + let A_sign = MPC.sign(signablePayload, A_sign_share.xShare, [B_sign_share.rShares[1]], [C.yShares[1]]); + let B_sign = MPC.sign(signablePayload, B_sign_share.xShare, [A_sign_share.rShares[2]], [C.yShares[2]]); + // sign the message_buffer (unsigned txHex) + let signature = MPC.signCombine([A_sign, B_sign]); + let rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); + txBuilder = initTxBuilder(); + txBuilder.sender(sender, commonPub); + txBuilder.addSignature({ pub: nearKeyPair.getKeys().pub }, rawSignature); + let signedTransaction = await txBuilder.build(); + signedTransaction.signature.length.should.equal(1); + signedTransaction.signature[0].should.equal(base58.encode(rawSignature)); + + // signing with A and C + A_sign_share = MPC.signShare(signablePayload, A_combine.pShare, [A_combine.jShares[3]]); + let C_sign_share = MPC.signShare(signablePayload, C_combine.pShare, [C_combine.jShares[1]]); + A_sign = MPC.sign(signablePayload, A_sign_share.xShare, [C_sign_share.rShares[1]], [B.yShares[1]]); + let C_sign = MPC.sign(signablePayload, C_sign_share.xShare, [A_sign_share.rShares[3]], [B.yShares[3]]); + signature = MPC.signCombine([A_sign, C_sign]); + rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); + txBuilder = initTxBuilder(); + txBuilder.sender(sender, commonPub); + txBuilder.addSignature({ pub: nearKeyPair.getKeys().pub }, rawSignature); + signedTransaction = await txBuilder.build(); + signedTransaction.signature.length.should.equal(1); + signedTransaction.signature[0].should.equal(base58.encode(rawSignature)); + + // signing with B and C + B_sign_share = MPC.signShare(signablePayload, B_combine.pShare, [B_combine.jShares[3]]); + C_sign_share = MPC.signShare(signablePayload, C_combine.pShare, [C_combine.jShares[2]]); + B_sign = MPC.sign(signablePayload, B_sign_share.xShare, [C_sign_share.rShares[2]], [A.yShares[2]]); + C_sign = MPC.sign(signablePayload, C_sign_share.xShare, [B_sign_share.rShares[3]], [A.yShares[3]]); + signature = MPC.signCombine([B_sign, C_sign]); + rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); + txBuilder = initTxBuilder(); + txBuilder.sender(sender, commonPub); + txBuilder.addSignature({ pub: nearKeyPair.getKeys().pub }, rawSignature); + signedTransaction = await txBuilder.build(); + signedTransaction.signature.length.should.equal(1); + signedTransaction.signature[0].should.equal(base58.encode(rawSignature)); + + const rebuiltTransaction = await factory.from(signedTransaction.toBroadcastFormat()).build(); + + rebuiltTransaction.signature[0].should.equal(base58.encode(rawSignature)); + }); + }); + }); +}); diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/stakingActivateBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/stakingActivateBuilder.ts index 4c6c8918e8..2657a42306 100644 --- a/modules/sdk-coin-near/test/unit/transactionBuilder/stakingActivateBuilder.ts +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/stakingActivateBuilder.ts @@ -19,7 +19,7 @@ describe('Near Staking Activate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: testData.accounts.account1.secretKey }); const tx = await txBuilder.build(); tx.inputs.length.should.equal(1); @@ -43,7 +43,7 @@ describe('Near Staking Activate Builder', () => { txJson.id.should.equal('GpiLLaGs2Fk2bd7SQvhkJaZjj74UnPPdF7cUa9pw15je'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { @@ -65,7 +65,7 @@ describe('Near Staking Activate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); const tx = await txBuilder.build(); tx.inputs.length.should.equal(1); tx.inputs[0].should.deepEqual({ @@ -92,7 +92,7 @@ describe('Near Staking Activate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); await txBuilder.build().should.be.rejectedWith('gas is required before building staking activate'); }); @@ -103,7 +103,7 @@ describe('Near Staking Activate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); await txBuilder.build().should.be.rejectedWith('amount is required before building staking activate'); }); @@ -146,7 +146,7 @@ describe('Near Staking Activate Builder', () => { txJson.id.should.equal('GpiLLaGs2Fk2bd7SQvhkJaZjj74UnPPdF7cUa9pw15je'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/stakingDeactivateBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/stakingDeactivateBuilder.ts index 544416ad35..ed0db9d633 100644 --- a/modules/sdk-coin-near/test/unit/transactionBuilder/stakingDeactivateBuilder.ts +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/stakingDeactivateBuilder.ts @@ -18,7 +18,7 @@ describe('Near Staking Deactivate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: testData.accounts.account1.secretKey }); const tx = await txBuilder.build(); tx.inputs.length.should.equal(0); @@ -30,7 +30,7 @@ describe('Near Staking Deactivate Builder', () => { txJson.id.should.equal('CDxPRP3DgHN8gYmRDagk5TRuX7fsCRYHcuqoNULyQPUW'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { @@ -52,7 +52,7 @@ describe('Near Staking Deactivate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); const tx = await txBuilder.build(); tx.inputs.length.should.equal(0); should.equal(tx.type, TransactionType.StakingDeactivate); @@ -67,7 +67,7 @@ describe('Near Staking Deactivate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); await txBuilder.build().should.be.rejectedWith('gas is required before building staking deactivate'); }); @@ -78,7 +78,7 @@ describe('Near Staking Deactivate Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); await txBuilder.build().should.be.rejectedWith('amount is required before building staking deactivate'); }); @@ -90,7 +90,7 @@ describe('Near Staking Deactivate Builder', () => { txJson.id.should.equal('CDxPRP3DgHN8gYmRDagk5TRuX7fsCRYHcuqoNULyQPUW'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { @@ -112,7 +112,7 @@ describe('Near Staking Deactivate Builder', () => { txJson.id.should.equal('CDxPRP3DgHN8gYmRDagk5TRuX7fsCRYHcuqoNULyQPUW'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/stakingWithdrawBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/stakingWithdrawBuilder.ts index 6209f6d365..af6b7cc55b 100644 --- a/modules/sdk-coin-near/test/unit/transactionBuilder/stakingWithdrawBuilder.ts +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/stakingWithdrawBuilder.ts @@ -18,7 +18,7 @@ describe('Near Staking withdraw Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); txBuilder.sign({ key: testData.accounts.account1.secretKey }); const tx = await txBuilder.build(); tx.inputs.length.should.equal(1); @@ -30,7 +30,7 @@ describe('Near Staking withdraw Builder', () => { txJson.id.should.equal('52ZX8MUwmYc6WQ67riUBpmntkcSxxT5aKkJYt5CtCZub'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { @@ -52,7 +52,7 @@ describe('Near Staking withdraw Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); const tx = await txBuilder.build(); tx.inputs.length.should.equal(1); should.equal(tx.type, TransactionType.StakingWithdraw); @@ -72,7 +72,7 @@ describe('Near Staking withdraw Builder', () => { txJson.id.should.equal('52ZX8MUwmYc6WQ67riUBpmntkcSxxT5aKkJYt5CtCZub'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { @@ -94,7 +94,7 @@ describe('Near Staking withdraw Builder', () => { txJson.id.should.equal('52ZX8MUwmYc6WQ67riUBpmntkcSxxT5aKkJYt5CtCZub'); txJson.signerId.should.equal(testData.accounts.account1.address); txJson.publicKey.should.equal(testData.accounts.account1.publicKeyBase58); - txJson.nonce.should.equal(1); + txJson.nonce.should.equal(BigInt(1)); txJson.receiverId.should.equal('lavenderfive.pool.f863973.m0'); txJson.actions.should.deepEqual([ { @@ -117,7 +117,7 @@ describe('Near Staking withdraw Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); await txBuilder.build().should.be.rejectedWith('gas is required before building staking withdraw'); }); @@ -128,7 +128,7 @@ describe('Near Staking withdraw Builder', () => { .sender(testData.accounts.account1.address, testData.accounts.account1.publicKey) .receiverId(validatorContractAddress) .recentBlockHash(testData.blockHash.block1) - .nonce(1); + .nonce(BigInt(1)); await txBuilder.build().should.be.rejectedWith('amount is required before building staking withdraw'); }); }); diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/storageDepositTransferBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/storageDepositTransferBuilder.ts new file mode 100644 index 0000000000..cbdbae0d3d --- /dev/null +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/storageDepositTransferBuilder.ts @@ -0,0 +1,220 @@ +import should from 'should'; +import * as base58 from 'bs58'; + +import { BitGoAPI } from '@bitgo/sdk-api'; +import { Eddsa, TransactionType } from '@bitgo/sdk-core'; +import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; +import { coins } from '@bitgo/statics'; + +import * as testData from '../../resources/near'; +import { getBuilderFactory } from '../getBuilderFactory'; + +import { KeyPair, Nep141Token, TransactionBuilderFactory } from '../../../src'; + +describe('Near: Storage Deposit Transfer Builder', () => { + const coinName = 'near:usdc'; + const coinNameTest = 'tnear:tnep24dp'; + let nep141Token: Nep141Token; + let bitgo: TestBitGoAPI; + + before(function () { + bitgo = TestBitGo.decorate(BitGoAPI, { + env: 'mock', + }); + bitgo.initializeTestVars(); + Nep141Token.createTokenConstructors().forEach(({ name, coinConstructor }) => { + bitgo.safeRegister(name, coinConstructor); + }); + nep141Token = bitgo.coin(coinNameTest) as Nep141Token; + }); + + describe('Near storage deposit transfer builder', function () { + const factory = new TransactionBuilderFactory(coins.get(coinNameTest)); + const factoryProd = new TransactionBuilderFactory(coins.get(coinName)); + const gas = '125000000000000'; + const deposit = '1250000000000000000000'; + + const initTxBuilder = () => { + const txBuilder = factory.getStorageDepositTransferBuilder(); + txBuilder.gas(gas); + txBuilder.deposit(deposit); + txBuilder.nonce(BigInt(1)); + txBuilder.receiverId(nep141Token.contractAddress); + txBuilder.recentBlockHash(testData.blockHash.block1); + return txBuilder; + }; + + describe('fungible token builder environment', function () { + it('should select the right network', function () { + should.equal(factory.getStorageDepositTransferBuilder().coinName(), coinNameTest); + should.equal(factoryProd.getStorageDepositTransferBuilder().coinName(), coinName); + // used type any to access protected properties + const txBuilder: any = factory.getStorageDepositTransferBuilder(); + const txBuilderProd: any = factoryProd.getStorageDepositTransferBuilder(); + + txBuilder._coinConfig.name.should.deepEqual(coinNameTest); + txBuilderProd._coinConfig.name.should.deepEqual(coinName); + }); + }); + + describe('should build', function () { + it('an unsigned storage deposit self transfer transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.StorageDeposit); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + const rawTx = tx.toBroadcastFormat(); + rawTx.should.deepEqual(testData.rawTx.storageDeposit.unsigned); + }); + + it('a signed storage deposit self transfer transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + builder.sign({ key: testData.accounts.account1.secretKey }); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.StorageDeposit); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + const rawTx = tx.toBroadcastFormat(); + rawTx.should.deepEqual(testData.rawTx.storageDeposit.signed); + }); + }); + + it('an unsigned storage deposit transfer transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + builder.beneficiaryId(testData.accounts.account2.address); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.StorageDeposit); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account2.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + }); + + it('a signed fungible token transfer with storage deposit transaction', async () => { + const builder = initTxBuilder(); + builder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + builder.beneficiaryId(testData.accounts.account2.address); + builder.sign({ key: testData.accounts.account1.secretKey }); + const tx = await builder.build(); + should.equal(tx.type, TransactionType.StorageDeposit); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.accounts.account2.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.accounts.account1.address, + value: '1250000000000000000000', + coin: `t${nep141Token.getFamily()}`, + }); + }); + + describe('add TSS signature', function () { + let MPC: Eddsa; + + before('initialize mpc module', async () => { + MPC = await Eddsa.initialize(); + }); + it('should add TSS signature', async () => { + const factory = getBuilderFactory(coinNameTest); + const A = MPC.keyShare(1, 2, 3); + const B = MPC.keyShare(2, 2, 3); + const C = MPC.keyShare(3, 2, 3); + + const A_combine = MPC.keyCombine(A.uShare, [B.yShares[1], C.yShares[1]]); + const B_combine = MPC.keyCombine(B.uShare, [A.yShares[2], C.yShares[2]]); + const C_combine = MPC.keyCombine(C.uShare, [A.yShares[3], B.yShares[3]]); + + const commonPub = A_combine.pShare.y; + const nearKeyPair = new KeyPair({ pub: commonPub }); + const sender = nearKeyPair.getAddress(); + + let txBuilder = initTxBuilder(); + txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); + const unsignedTransaction = await txBuilder.build(); + const signablePayload = unsignedTransaction.signablePayload; + + // signing with A and B + let A_sign_share = MPC.signShare(signablePayload, A_combine.pShare, [A_combine.jShares[2]]); + let B_sign_share = MPC.signShare(signablePayload, B_combine.pShare, [B_combine.jShares[1]]); + let A_sign = MPC.sign(signablePayload, A_sign_share.xShare, [B_sign_share.rShares[1]], [C.yShares[1]]); + let B_sign = MPC.sign(signablePayload, B_sign_share.xShare, [A_sign_share.rShares[2]], [C.yShares[2]]); + // sign the message_buffer (unsigned txHex) + let signature = MPC.signCombine([A_sign, B_sign]); + let rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); + txBuilder = initTxBuilder(); + txBuilder.sender(sender, commonPub); + txBuilder.addSignature({ pub: nearKeyPair.getKeys().pub }, rawSignature); + let signedTransaction = await txBuilder.build(); + signedTransaction.signature.length.should.equal(1); + signedTransaction.signature[0].should.equal(base58.encode(rawSignature)); + + // signing with A and C + A_sign_share = MPC.signShare(signablePayload, A_combine.pShare, [A_combine.jShares[3]]); + let C_sign_share = MPC.signShare(signablePayload, C_combine.pShare, [C_combine.jShares[1]]); + A_sign = MPC.sign(signablePayload, A_sign_share.xShare, [C_sign_share.rShares[1]], [B.yShares[1]]); + let C_sign = MPC.sign(signablePayload, C_sign_share.xShare, [A_sign_share.rShares[3]], [B.yShares[3]]); + signature = MPC.signCombine([A_sign, C_sign]); + rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); + txBuilder = initTxBuilder(); + txBuilder.sender(sender, commonPub); + txBuilder.addSignature({ pub: nearKeyPair.getKeys().pub }, rawSignature); + signedTransaction = await txBuilder.build(); + signedTransaction.signature.length.should.equal(1); + signedTransaction.signature[0].should.equal(base58.encode(rawSignature)); + + // signing with B and C + B_sign_share = MPC.signShare(signablePayload, B_combine.pShare, [B_combine.jShares[3]]); + C_sign_share = MPC.signShare(signablePayload, C_combine.pShare, [C_combine.jShares[2]]); + B_sign = MPC.sign(signablePayload, B_sign_share.xShare, [C_sign_share.rShares[2]], [A.yShares[2]]); + C_sign = MPC.sign(signablePayload, C_sign_share.xShare, [B_sign_share.rShares[3]], [A.yShares[3]]); + signature = MPC.signCombine([B_sign, C_sign]); + rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); + txBuilder = initTxBuilder(); + txBuilder.sender(sender, commonPub); + txBuilder.addSignature({ pub: nearKeyPair.getKeys().pub }, rawSignature); + signedTransaction = await txBuilder.build(); + signedTransaction.signature.length.should.equal(1); + signedTransaction.signature[0].should.equal(base58.encode(rawSignature)); + + const rebuiltTransaction = await factory.from(signedTransaction.toBroadcastFormat()).build(); + + rebuiltTransaction.signature[0].should.equal(base58.encode(rawSignature)); + }); + }); + }); +}); diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/transactionBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/transactionBuilder.ts index 4344a4b195..8a68345d3c 100644 --- a/modules/sdk-coin-near/test/unit/transactionBuilder/transactionBuilder.ts +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/transactionBuilder.ts @@ -17,7 +17,7 @@ describe('NEAR Transaction Builder', async () => { it('start and build an empty a transfer tx', async () => { const txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); @@ -31,7 +31,7 @@ describe('NEAR Transaction Builder', async () => { it('build and sign a transfer tx', async () => { const txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); diff --git a/modules/sdk-coin-near/test/unit/transactionBuilder/transferBuilder.ts b/modules/sdk-coin-near/test/unit/transactionBuilder/transferBuilder.ts index d48c391ad3..93b87b8fed 100644 --- a/modules/sdk-coin-near/test/unit/transactionBuilder/transferBuilder.ts +++ b/modules/sdk-coin-near/test/unit/transactionBuilder/transferBuilder.ts @@ -12,7 +12,7 @@ describe('Near Transfer Builder', () => { it('build a transfer tx unsigned', async () => { const txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); @@ -38,7 +38,7 @@ describe('Near Transfer Builder', () => { it('build a transfer tx signed', async () => { const txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); @@ -85,7 +85,7 @@ describe('Near Transfer Builder', () => { let txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); @@ -102,7 +102,7 @@ describe('Near Transfer Builder', () => { let rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); txBuilder = factory.getTransferBuilder(); txBuilder.sender(sender, commonPub); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); @@ -120,7 +120,7 @@ describe('Near Transfer Builder', () => { rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); txBuilder = factory.getTransferBuilder(); txBuilder.sender(sender, commonPub); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); @@ -138,7 +138,7 @@ describe('Near Transfer Builder', () => { rawSignature = Buffer.concat([Buffer.from(signature.R, 'hex'), Buffer.from(signature.sigma, 'hex')]); txBuilder = factory.getTransferBuilder(); txBuilder.sender(sender, commonPub); - txBuilder.nonce(1); + txBuilder.nonce(BigInt(1)); txBuilder.receiverId(testData.accounts.account2.address); txBuilder.recentBlockHash(testData.blockHash.block1); txBuilder.amount(testData.AMOUNT); diff --git a/modules/sdk-core/src/account-lib/baseCoin/enum.ts b/modules/sdk-core/src/account-lib/baseCoin/enum.ts index 61835d430b..40aca4de3f 100644 --- a/modules/sdk-core/src/account-lib/baseCoin/enum.ts +++ b/modules/sdk-core/src/account-lib/baseCoin/enum.ts @@ -82,6 +82,7 @@ export enum TransactionType { DelegateResource, // Un-delegate resource (e.g. TRX) UnDelegateResource, + StorageDeposit, } /** diff --git a/yarn.lock b/yarn.lock index f8b17e314f..07ee0503e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3471,6 +3471,162 @@ dependencies: bs58 "^5.0.0" +"@near-js/accounts@1.4.1": + version "1.4.1" + resolved "https://registry.npmjs.org/@near-js/accounts/-/accounts-1.4.1.tgz#729692916862c21a2bcd36cb0c312b46540ac33c" + integrity sha512-ni3QT9H3NdrbVVKyx56yvz93r89Dvpc/vgVtiIK2OdXjkK6jcj+UKMDRQ6F7rd9qJOInLkHZbVBtcR6j1CXLjw== + dependencies: + "@near-js/crypto" "1.4.2" + "@near-js/providers" "1.0.3" + "@near-js/signers" "0.2.2" + "@near-js/transactions" "1.3.3" + "@near-js/types" "0.3.1" + "@near-js/utils" "1.1.0" + "@noble/hashes" "1.7.1" + borsh "1.0.0" + depd "2.0.0" + is-my-json-valid "^2.20.6" + lru_map "0.4.1" + near-abi "0.2.0" + +"@near-js/crypto@1.4.2": + version "1.4.2" + resolved "https://registry.npmjs.org/@near-js/crypto/-/crypto-1.4.2.tgz#b903b1de954a06d6e34457f78184c43086df1917" + integrity sha512-GRfchsyfWvSAPA1gI9hYhw5FH94Ac1BUo+Cmp5rSJt/V0K3xVzCWgOQxvv4R3kDnWjaXJEuAmpEEnr4Bp3FWrA== + dependencies: + "@near-js/types" "0.3.1" + "@near-js/utils" "1.1.0" + "@noble/curves" "1.8.1" + borsh "1.0.0" + randombytes "2.1.0" + secp256k1 "5.0.1" + +"@near-js/crypto@2.0.2", "@near-js/crypto@^2.0.1": + version "2.0.2" + resolved "https://registry.npmjs.org/@near-js/crypto/-/crypto-2.0.2.tgz#f22d2c94df4994351df03398a8c35d54e5f3199d" + integrity sha512-qmljQFYSYaEeeOA+yz2uDnWyeggyOjQ/7jMty8JVgoY1WdqOZphOmJfIJxl8WZxc7jlc0TU4pDnt7cfd0y5MuA== + dependencies: + "@near-js/types" "2.0.2" + "@near-js/utils" "2.0.2" + "@noble/curves" "1.8.1" + borsh "1.0.0" + randombytes "2.1.0" + secp256k1 "5.0.1" + +"@near-js/keystores-browser@0.2.2": + version "0.2.2" + resolved "https://registry.npmjs.org/@near-js/keystores-browser/-/keystores-browser-0.2.2.tgz#9df046c9dcca91fc743d33e798ba4c205d5f673b" + integrity sha512-Pxqm7WGtUu6zj32vGCy9JcEDpZDSB5CCaLQDTQdF3GQyL0flyRv2I/guLAgU5FLoYxU7dJAX9mslJhPW7P2Bfw== + dependencies: + "@near-js/crypto" "1.4.2" + "@near-js/keystores" "0.2.2" + +"@near-js/keystores-node@0.1.2": + version "0.1.2" + resolved "https://registry.npmjs.org/@near-js/keystores-node/-/keystores-node-0.1.2.tgz#e8391cbda7de4123f293d51dfb55c060f537c5cd" + integrity sha512-MWLvTszZOVziiasqIT/LYNhUyWqOJjDGlsthOsY6dTL4ZcXjjmhmzrbFydIIeQr+CcEl5wukTo68ORI9JrHl6g== + dependencies: + "@near-js/crypto" "1.4.2" + "@near-js/keystores" "0.2.2" + +"@near-js/keystores@0.2.2": + version "0.2.2" + resolved "https://registry.npmjs.org/@near-js/keystores/-/keystores-0.2.2.tgz#c1ec215c39e8d279f226333364bc424b57842527" + integrity sha512-DLhi/3a4qJUY+wgphw2Jl4S+L0AKsUYm1mtU0WxKYV5OBwjOXvbGrXNfdkheYkfh3nHwrQgtjvtszX6LrRXLLw== + dependencies: + "@near-js/crypto" "1.4.2" + "@near-js/types" "0.3.1" + +"@near-js/providers@1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@near-js/providers/-/providers-1.0.3.tgz#918c5ffb8f9bb92f0d28198e9396a08c61b3754a" + integrity sha512-VJMboL14R/+MGKnlhhE3UPXCGYvMd1PpvF9OqZ9yBbulV7QVSIdTMfY4U1NnDfmUC2S3/rhAEr+3rMrIcNS7Fg== + dependencies: + "@near-js/transactions" "1.3.3" + "@near-js/types" "0.3.1" + "@near-js/utils" "1.1.0" + borsh "1.0.0" + exponential-backoff "^3.1.2" + optionalDependencies: + node-fetch "2.6.7" + +"@near-js/signers@0.2.2": + version "0.2.2" + resolved "https://registry.npmjs.org/@near-js/signers/-/signers-0.2.2.tgz#ae521e1ea72c9b49ad1dfc63f2d90d847207fea7" + integrity sha512-M6ib+af9zXAPRCjH2RyIS0+RhCmd9gxzCeIkQ+I2A3zjgGiEDkBZbYso9aKj8Zh2lPKKSH7h+u8JGymMOSwgyw== + dependencies: + "@near-js/crypto" "1.4.2" + "@near-js/keystores" "0.2.2" + "@noble/hashes" "1.3.3" + +"@near-js/transactions@1.3.3": + version "1.3.3" + resolved "https://registry.npmjs.org/@near-js/transactions/-/transactions-1.3.3.tgz#3b0c03861fc4b29322cedb03875cf80f52871dd2" + integrity sha512-1AXD+HuxlxYQmRTLQlkVmH+RAmV3HwkAT8dyZDu+I2fK/Ec9BQHXakOJUnOBws3ihF+akQhamIBS5T0EXX/Ylw== + dependencies: + "@near-js/crypto" "1.4.2" + "@near-js/signers" "0.2.2" + "@near-js/types" "0.3.1" + "@near-js/utils" "1.1.0" + "@noble/hashes" "1.7.1" + borsh "1.0.0" + +"@near-js/transactions@^2.0.1": + version "2.0.2" + resolved "https://registry.npmjs.org/@near-js/transactions/-/transactions-2.0.2.tgz#cc56e658e1de589a6ee5e79d9ae666942372b5d4" + integrity sha512-QEPY9lHJR+W7fKYLpDXEi4JhyN26embvCx/YkG9KdfRmc+oHy6jxYGiL4eovrat1DcJgTcmiz4oyHznW35Ztvw== + dependencies: + "@near-js/crypto" "2.0.2" + "@near-js/types" "2.0.2" + "@near-js/utils" "2.0.2" + "@noble/hashes" "1.7.1" + borsh "1.0.0" + +"@near-js/types@0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@near-js/types/-/types-0.3.1.tgz#35f2649f85881d72fc231a16142e1f0e3b0e8377" + integrity sha512-8qIA7ynAEAuVFNAQc0cqz2xRbfyJH3PaAG5J2MgPPhD18lu/tCGd6pzYg45hjhtiJJRFDRjh/FUWKS+ZiIIxUw== + +"@near-js/types@2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@near-js/types/-/types-2.0.2.tgz#d472bff3c0097ba74f548a13dfb46090bb55ea25" + integrity sha512-NihtDMLOc/iKLbmE9yvY77HMXBA724Yj2X30b2glhFXxqBbBs7QRj5QpWT/O4ay4yovmnmlGKukQABeDNQI+qw== + +"@near-js/utils@1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@near-js/utils/-/utils-1.1.0.tgz#fcdd0b7d9badb11d61f399755bead804b2a47d47" + integrity sha512-5XWRq7xpu8Wud9pRXe2U347KXyi0mXofedUY2DQ9TaqiZUcMIaN9xj7DbCs2v6dws3pJyYrT1KWxeNp5fSaY3w== + dependencies: + "@near-js/types" "0.3.1" + "@scure/base" "^1.2.0" + depd "2.0.0" + mustache "4.0.0" + +"@near-js/utils@2.0.2": + version "2.0.2" + resolved "https://registry.npmjs.org/@near-js/utils/-/utils-2.0.2.tgz#5eb4ca94bf4a84528da3c69e1fc4a01cc17c5555" + integrity sha512-B1Ds7PdtAfxaUkYdu5i7OwihcUACAyKd9w4b/SJ9XXJSCCARSRvMnU8McffAlq7sGfgOE0V2SK85S0U0RGZQtg== + dependencies: + "@near-js/types" "2.0.2" + "@scure/base" "^1.2.4" + depd "2.0.0" + mustache "4.0.0" + +"@near-js/wallet-account@1.3.3": + version "1.3.3" + resolved "https://registry.npmjs.org/@near-js/wallet-account/-/wallet-account-1.3.3.tgz#ac23509e0c1c124945a8539d3cd2f654aed727e8" + integrity sha512-GDzg/Kz0GBYF7tQfyQQQZ3vviwV8yD+8F2lYDzsWJiqIln7R1ov0zaXN4Tii86TeS21KPn2hHAsVu3Y4txa8OQ== + dependencies: + "@near-js/accounts" "1.4.1" + "@near-js/crypto" "1.4.2" + "@near-js/keystores" "0.2.2" + "@near-js/providers" "1.0.3" + "@near-js/signers" "0.2.2" + "@near-js/transactions" "1.3.3" + "@near-js/types" "0.3.1" + "@near-js/utils" "1.1.0" + borsh "1.0.0" + "@noble/ciphers@^1.1.1", "@noble/ciphers@^1.3.0": version "1.3.0" resolved "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz#f64b8ff886c240e644e5573c097f86e5b43676dc" @@ -4737,7 +4893,7 @@ resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9" integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ== -"@scure/base@^1.2.4", "@scure/base@~1.2.5": +"@scure/base@^1.2.0", "@scure/base@^1.2.4", "@scure/base@~1.2.5": version "1.2.6" resolved "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" integrity sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg== @@ -5564,7 +5720,7 @@ resolved "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.10.18.tgz#d442369fd2d06d5b2c607fae51c257b2f9caa94e" integrity sha512-jOk52a1Kz+1oU5fNWwAcNe64/GsE7r/Q6ronwDox0D3ETo/cr4ICMQyeXrj7G6FPW1n8YjRoAZA2F0XBr6GicQ== -"@types/json-schema@*", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.11", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -7531,14 +7687,10 @@ borc@^2.1.1: json-text-sequence "~0.1.0" readable-stream "^3.6.0" -borsh@^0.6.0: - version "0.6.0" - resolved "https://registry.npmjs.org/borsh/-/borsh-0.6.0.tgz#a7c9eeca6a31ca9e0607cb49f329cb659eb791e1" - integrity sha512-sl5k89ViqsThXQpYa9XDtz1sBl3l1lI313cFUY1HKr+wvMILnb+58xpkqTNrYbelh99dY7K8usxoCusQmqix9Q== - dependencies: - bn.js "^5.2.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" +borsh@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz#b564c8cc8f7a91e3772b9aef9e07f62b84213c1f" + integrity sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ== borsh@^0.7.0: version "0.7.0" @@ -8047,11 +8199,6 @@ canvg@4.0.3, canvg@^3.0.11: stackblur-canvas "^2.0.0" svg-pathdata "^6.0.3" -capability@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/capability/-/capability-0.2.5.tgz#51ad87353f1936ffd77f2f21c74633a4dea88801" - integrity sha512-rsJZYVCgXd08sPqwmaIqjAd5SUTfonV0z/gDJ8D6cN8wQphky1kkAYEqQ+hmDxTw7UihvBfjUVUSY+DBEe44jg== - caseless@~0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -9552,7 +9699,7 @@ depcheck@^1.4.3: semver "^7.5.4" yargs "^16.2.0" -depd@2.0.0, depd@^2.0.0, depd@~2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -10144,15 +10291,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error-polyfill@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/error-polyfill/-/error-polyfill-0.1.3.tgz#df848b61ad8834f7a5db69a70b9913df86721d15" - integrity sha512-XHJk60ufE+TG/ydwp4lilOog549iiQF2OAPhkk9DdiYWMrltz5yhDz/xnKuenNwP7gy3dsibssO5QpVhkrSzzg== - dependencies: - capability "^0.2.5" - o3 "^1.0.3" - u3 "^0.1.1" - es-abstract@^1.23.2, es-abstract@^1.23.5, es-abstract@^1.23.9: version "1.23.9" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz#5b45994b7de78dada5c1bebf1379646b32b9d606" @@ -10982,7 +11120,7 @@ expo-random@^12.1.2: dependencies: base64-js "^1.3.0" -exponential-backoff@^3.1.1: +exponential-backoff@^3.1.1, exponential-backoff@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz#a8f26adb96bf78e8cd8ad1037928d5e5c0679d91" integrity sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA== @@ -11665,6 +11803,20 @@ gauge@^4.0.3: strip-ansi "^6.0.1" wide-align "^1.1.5" +generate-function@^2.0.0: + version "2.3.1" + resolved "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + integrity sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ== + dependencies: + is-property "^1.0.0" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -12401,7 +12553,18 @@ http-deceiver@^1.2.7: resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== -http-errors@1.8.1, http-errors@^1.7.2: +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.8.1: version "1.8.1" resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== @@ -13013,6 +13176,22 @@ is-map@^2.0.3: resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== +is-my-ip-valid@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz#f7220d1146257c98672e6fba097a9f3f2d348442" + integrity sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg== + +is-my-json-valid@^2.20.6: + version "2.20.6" + resolved "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.20.6.tgz#a9d89e56a36493c77bda1440d69ae0dc46a08387" + integrity sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw== + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^5.0.0" + xtend "^4.0.0" + is-nan@^1.2.1, is-nan@^1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" @@ -13100,6 +13279,11 @@ is-promise@^2.2.2: resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== +is-property@^1.0.0, is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + is-regex@^1.1.4, is-regex@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -13658,6 +13842,11 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== +jsonpointer@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + jspdf@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz#d81e1964f354f60412516eb2449ea2cccd4d2a3b" @@ -14360,6 +14549,11 @@ lru-queue@^0.1.0: dependencies: es5-ext "~0.10.2" +lru_map@0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz#f7b4046283c79fb7370c36f8fca6aee4324b0a98" + integrity sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg== + lz-string@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" @@ -15077,10 +15271,10 @@ multimatch@^5.0.0: arrify "^2.0.1" minimatch "^3.0.4" -mustache@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" - integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== +mustache@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mustache/-/mustache-4.0.0.tgz#7f02465dbb5b435859d154831c032acdfbbefb31" + integrity sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA== mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" @@ -15122,22 +15316,35 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -near-api-js@^0.44.2: - version "0.44.2" - resolved "https://registry.npmjs.org/near-api-js/-/near-api-js-0.44.2.tgz#e451f68f2c56bd885c7b918db5818a3e6e9423d0" - integrity sha512-eMnc4V+geggapEUa3nU2p8HSHn/njtloI4P2mceHQWO8vDE1NGpnAw8FuTBrLmXSgIv9m6oocgFc9t3VNf5zwg== +near-abi@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/near-abi/-/near-abi-0.2.0.tgz#745b569d69d172f46cd2ecc06271974a6cd2fa48" + integrity sha512-kCwSf/3fraPU2zENK18sh+kKG4uKbEUEQdyWQkmW8ZofmLarObIz2+zAYjA1teDZLeMvEQew3UysnPDXgjneaA== dependencies: - bn.js "5.2.0" - borsh "^0.6.0" - bs58 "^4.0.0" - depd "^2.0.0" - error-polyfill "^0.1.3" - http-errors "^1.7.2" - js-sha256 "^0.9.0" - mustache "^4.0.0" - node-fetch "^2.6.1" - text-encoding-utf-8 "^1.0.2" - tweetnacl "^1.0.1" + "@types/json-schema" "^7.0.11" + +near-api-js@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/near-api-js/-/near-api-js-5.1.1.tgz#64905f74d0ad9ce0b4f5ad6ec4aaa8403b8e5968" + integrity sha512-h23BGSKxNv8ph+zU6snicstsVK1/CTXsQz4LuGGwoRE24Hj424nSe4+/1tzoiC285Ljf60kPAqRCmsfv9etF2g== + dependencies: + "@near-js/accounts" "1.4.1" + "@near-js/crypto" "1.4.2" + "@near-js/keystores" "0.2.2" + "@near-js/keystores-browser" "0.2.2" + "@near-js/keystores-node" "0.1.2" + "@near-js/providers" "1.0.3" + "@near-js/signers" "0.2.2" + "@near-js/transactions" "1.3.3" + "@near-js/types" "0.3.1" + "@near-js/utils" "1.1.0" + "@near-js/wallet-account" "1.3.3" + "@noble/curves" "1.8.1" + borsh "1.0.0" + depd "2.0.0" + http-errors "1.7.2" + near-abi "0.2.0" + node-fetch "2.6.7" negotiator@0.6.3: version "0.6.3" @@ -15644,13 +15851,6 @@ nyc@^15.0.0, nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" -o3@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/o3/-/o3-1.0.3.tgz#192ce877a882dfa6751f0412a865fafb2da1dac0" - integrity sha512-f+4n+vC6s4ysy7YO7O2gslWZBUu8Qj2i2OUJOvjRxQva7jVjYjB29jrr9NCjmxZQR0gzrOcv1RnqoYOeMs5VRQ== - dependencies: - capability "^0.2.5" - oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -17259,7 +17459,7 @@ randombytes@2.0.5: dependencies: safe-buffer "^5.1.0" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: +randombytes@2.1.0, randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -18308,6 +18508,11 @@ setprototypeof@1.1.0: resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -19622,6 +19827,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -19802,7 +20012,7 @@ tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== -tweetnacl@1.0.3, tweetnacl@^1.0.0, tweetnacl@^1.0.1, tweetnacl@^1.0.3: +tweetnacl@1.0.3, tweetnacl@^1.0.0, tweetnacl@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== @@ -19977,11 +20187,6 @@ typescript@>=5.0.2: resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -u3@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/u3/-/u3-0.1.1.tgz#5f52044f42ee76cd8de33148829e14528494b73b" - integrity sha512-+J5D5ir763y+Am/QY6hXNRlwljIeRMZMGs0cT6qqZVVzzT3X3nFPXVyPOFRMOR4kupB0T8JnCdpWdp6Q/iXn3w== - ua-parser-js@0.7.22, "ua-parser-js@>0.7.30 <0.8.0", ua-parser-js@^1.0.35: version "0.7.40" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz#c87d83b7bb25822ecfa6397a0da5903934ea1562"