Skip to content

Commit 6270567

Browse files
committed
feat(sdk-coin-near): added fungible token transfer builder
Ticket: COIN-4148
1 parent 9365736 commit 6270567

27 files changed

+1222
-308
lines changed

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AdaToken } from '@bitgo/sdk-coin-ada';
55
import { AlgoToken } from '@bitgo/sdk-coin-algo';
66
import { Bcha, Tbcha } from '@bitgo/sdk-coin-bcha';
77
import { HbarToken } from '@bitgo/sdk-coin-hbar';
8-
import { Near, TNear } from '@bitgo/sdk-coin-near';
8+
import { Near, TNear, Nep141Token } from '@bitgo/sdk-coin-near';
99
import { SolToken } from '@bitgo/sdk-coin-sol';
1010
import { TrxToken } from '@bitgo/sdk-coin-trx';
1111
import { CoinFactory } from '@bitgo/sdk-core';
@@ -451,6 +451,10 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
451451
Sip10Token.createTokenConstructors([...tokens.bitcoin.stx.tokens, ...tokens.testnet.stx.tokens]).forEach(
452452
({ name, coinConstructor }) => coinFactory.register(name, coinConstructor)
453453
);
454+
455+
Nep141Token.createTokenConstructors([...tokens.bitcoin.near.tokens, ...tokens.testnet.near.tokens]).forEach(
456+
({ name, coinConstructor }) => coinFactory.register(name, coinConstructor)
457+
);
454458
}
455459

456460
export const GlobalCoinFactory: CoinFactory = new CoinFactory();

modules/bitgo/test/browser/browser.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ describe('Coins', () => {
4343
Polyx: 1,
4444
Tpolyx: 1,
4545
CoredaoToken: 1,
46+
Nep141Token: 1,
4647
};
4748
Object.keys(BitGoJS.Coin)
4849
.filter((coinName) => !excludedKeys[coinName])

modules/sdk-coin-near/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@
4242
"dependencies": {
4343
"@bitgo/sdk-core": "^35.0.0",
4444
"@bitgo/statics": "^54.0.0",
45+
"@near-js/crypto": "^2.0.1",
46+
"@near-js/transactions": "^2.0.1",
4547
"@stablelib/hex": "^1.0.0",
4648
"bignumber.js": "^9.0.0",
47-
"bn.js": "^5.2.1",
4849
"bs58": "^4.0.1",
4950
"js-sha256": "^0.9.0",
5051
"lodash": "^4.17.14",
51-
"near-api-js": "^0.44.2",
52+
"near-api-js": "^5.1.1",
5253
"superagent": "^9.0.1",
5354
"tweetnacl": "^1.0.3"
5455
},

modules/sdk-coin-near/src/lib/constants.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ export const StakingContractMethodNames = {
99
Withdraw: 'withdraw',
1010
} as const;
1111

12+
export const AdditionalAllowedMethods = ['ft_transfer', 'storage_deposit'];
13+
14+
export const FT_TRANSFER = 'ft_transfer';
15+
1216
export const HEX_REGEX = /^[0-9a-fA-F]+$/;

modules/sdk-coin-near/src/lib/contractCallWrapper.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { FunctionCall } from './iface';
77
*/
88
export class ContractCallWrapper {
99
private _methodName: string;
10-
private _args: Record<string, unknown>;
10+
private _args: Record<string, unknown> = {};
1111
private _gas: string;
1212
private _deposit: string;
1313

@@ -21,35 +21,35 @@ export class ContractCallWrapper {
2121
return this._methodName;
2222
}
2323

24-
/** Set gas, expresed on yocto */
24+
/** Set gas, expressed on yocto */
2525
public set gas(gas: string) {
2626
if (!this.isValidAmount(new BigNumber(gas))) {
2727
throw new InvalidParameterValueError('Invalid gas value');
2828
}
2929
this._gas = gas;
3030
}
3131

32-
/** Get gas, expresed on yocto*/
32+
/** Get gas, expressed on yocto*/
3333
public get gas(): string {
3434
return this._gas;
3535
}
3636

37-
/** Set deposit, expresed on yocto */
37+
/** Set deposit, expressed on yocto */
3838
public set deposit(deposit: string) {
3939
if (!this.isValidAmount(new BigNumber(deposit))) {
4040
throw new InvalidParameterValueError('Invalid deposit value');
4141
}
4242
this._deposit = deposit;
4343
}
4444

45-
/** Get deposit, expresed on yocto */
45+
/** Get deposit, expressed on yocto */
4646
public get deposit(): string {
4747
return this._deposit;
4848
}
4949

5050
/** Get args, which are the parameters of a method */
5151
public set args(args: Record<string, unknown>) {
52-
this._args = args;
52+
this._args = { ...this._args, ...args };
5353
}
5454

5555
/** Set args, which are the parameters of a method */
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import assert from 'assert';
2+
import BigNumber from 'bignumber.js';
3+
import * as NearAPI from 'near-api-js';
4+
5+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
6+
import { BuildTransactionError, TransactionType } from '@bitgo/sdk-core';
7+
8+
import { FT_TRANSFER } from './constants';
9+
import { ContractCallWrapper } from './contractCallWrapper';
10+
import { Transaction } from './transaction';
11+
import { TransactionBuilder } from './transactionBuilder';
12+
import utils from './utils';
13+
14+
export class FungibleTokenTransferBuilder extends TransactionBuilder {
15+
private contractCallWrapper: ContractCallWrapper;
16+
17+
constructor(_coinConfig: Readonly<CoinConfig>) {
18+
super(_coinConfig);
19+
this.contractCallWrapper = new ContractCallWrapper();
20+
this.contractCallWrapper.methodName = FT_TRANSFER;
21+
}
22+
23+
/**
24+
* Initialize the transaction builder fields using the decoded transaction data
25+
*
26+
* @param {Transaction} tx the transaction data
27+
*/
28+
initBuilder(tx: Transaction): void {
29+
super.initBuilder(tx);
30+
const functionCall = tx.nearTransaction.actions[0].functionCall;
31+
if (functionCall) {
32+
this.contractCallWrapper.deposit = functionCall.deposit.toString();
33+
this.contractCallWrapper.gas = functionCall.gas.toString();
34+
}
35+
}
36+
37+
/**
38+
* Sets the gas of this transaction.
39+
*
40+
* @param {String} gas the gas of this transaction
41+
* @returns {TransactionBuilder} This transaction builder
42+
*/
43+
public gas(gas: string): this {
44+
this.validateValue(new BigNumber(gas));
45+
this.contractCallWrapper.gas = gas;
46+
return this;
47+
}
48+
49+
/**
50+
* Sets the deposit of at-least 1 yoctoNear
51+
*
52+
* @param {string} deposit the deposit in the minimum unit (1 Near = 1e24 yoctoNear) of this transaction
53+
* @returns {TransactionBuilder} This transaction builder
54+
*/
55+
public deposit(deposit: string): this {
56+
this.validateValue(new BigNumber(deposit));
57+
this.contractCallWrapper.deposit = deposit;
58+
return this;
59+
}
60+
61+
/**
62+
* @inheritdoc
63+
*
64+
* We need to override this because for contract call the receiver id is the contract address
65+
* And we need to pass the actual receiver id in the args
66+
*
67+
* @param accountId the contract address
68+
*/
69+
public receiverId(accountId: string): this {
70+
utils.isValidAddress(accountId);
71+
this._receiverId = accountId;
72+
return this;
73+
}
74+
75+
/**
76+
* Sets the actual receiver account id inside args
77+
*
78+
* @param accountId the receiver account id
79+
*/
80+
public ftReceiverId(accountId: string): this {
81+
utils.isValidAddress(accountId);
82+
this.contractCallWrapper.args = { receiver_id: accountId };
83+
return this;
84+
}
85+
86+
/**
87+
* Sets the ft amount to be transferred
88+
*
89+
* @param amount the amount of fungible token to be transferred
90+
*/
91+
public amount(amount: string): this {
92+
this.validateValue(new BigNumber(amount));
93+
this.contractCallWrapper.args = { amount };
94+
return this;
95+
}
96+
97+
/**
98+
* Sets the optional memo for the transfer
99+
*
100+
* @param memo
101+
*/
102+
public memo(memo: string): this {
103+
this.contractCallWrapper.args = { memo };
104+
return this;
105+
}
106+
107+
/** @inheritdoc */
108+
protected async buildImplementation(): Promise<Transaction> {
109+
const { methodName, args, gas, deposit } = this.contractCallWrapper.getParams();
110+
assert(gas, new BuildTransactionError('gas is required before building fungible token transfer'));
111+
assert(deposit, new BuildTransactionError('deposit is required before building fungible token transfer'));
112+
113+
super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]);
114+
const tx = await super.buildImplementation();
115+
tx.setTransactionType(TransactionType.Send);
116+
return tx;
117+
}
118+
}

modules/sdk-coin-near/src/lib/iface.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
import BN from 'bn.js';
1+
import { KeyType } from '@near-js/crypto';
22
import { TransactionType, TransactionExplanation as BaseTransactionExplanation } from '@bitgo/sdk-core';
33

44
export interface TransactionExplanation extends BaseTransactionExplanation {
55
type: TransactionType;
66
}
77

8-
export enum KeyType {
9-
ED25519 = 0,
10-
}
11-
128
export interface Signature {
139
keyType: KeyType;
1410
data: Uint8Array;
1511
}
1612

1713
export interface Transfer {
18-
deposit: BN;
14+
deposit: bigint;
1915
}
2016

2117
/** Interface with parameters needed to perform FunctionCall to a contract */
@@ -38,8 +34,8 @@ export interface Action {
3834
export interface TxData {
3935
id?: string;
4036
signerId: string;
41-
publicKey: string;
42-
nonce: number;
37+
publicKey?: string;
38+
nonce: bigint;
4339
receiverId: string;
4440
actions: Action[];
4541
signature?: Signature;

modules/sdk-coin-near/src/lib/stakingActivateBuilder.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Transaction } from './transaction';
44
import BigNumber from 'bignumber.js';
55
import * as NearAPI from 'near-api-js';
66
import assert from 'assert';
7-
import BN from 'bn.js';
87

98
import { ContractCallWrapper } from './contractCallWrapper';
109
import { TransactionBuilder } from './transactionBuilder';
@@ -32,14 +31,16 @@ export class StakingActivateBuilder extends TransactionBuilder {
3231
initBuilder(tx: Transaction): void {
3332
super.initBuilder(tx);
3433
const functionCall = tx.nearTransaction.actions[0].functionCall;
35-
this.contractCallWrapper.deposit = functionCall.deposit.toString();
36-
this.contractCallWrapper.gas = functionCall.gas.toString();
34+
if (functionCall) {
35+
this.contractCallWrapper.deposit = functionCall.deposit.toString();
36+
this.contractCallWrapper.gas = functionCall.gas.toString();
37+
}
3738
}
3839

3940
/**
4041
* Sets the gas of this transaction.
4142
*
42-
* @param {string} value the gas of this transaction
43+
* @param {string} gas the gas of this transaction
4344
* @returns {TransactionBuilder} This transaction builder
4445
*/
4546
public gas(gas: string): this {
@@ -51,7 +52,7 @@ export class StakingActivateBuilder extends TransactionBuilder {
5152
/**
5253
* Sets the amount of this transaction.
5354
*
54-
* @param {string} value the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction
55+
* @param {string} amount the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction
5556
* @returns {TransactionBuilder} This transaction builder
5657
*/
5758
public amount(amount: string): this {
@@ -67,7 +68,7 @@ export class StakingActivateBuilder extends TransactionBuilder {
6768
assert(gas, new BuildTransactionError('gas is required before building staking activate'));
6869
assert(deposit, new BuildTransactionError('amount is required before building staking activate'));
6970

70-
super.actions([NearAPI.transactions.functionCall(methodName, args, new BN(gas), new BN(deposit, 10))]);
71+
super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]);
7172
const tx = await super.buildImplementation();
7273
tx.setTransactionType(TransactionType.StakingActivate);
7374
return tx;

modules/sdk-coin-near/src/lib/stakingDeactivateBuilder.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Transaction } from './transaction';
44
import BigNumber from 'bignumber.js';
55
import * as NearAPI from 'near-api-js';
66
import assert from 'assert';
7-
import BN from 'bn.js';
87

98
import { ContractCallWrapper } from './contractCallWrapper';
109
import { TransactionBuilder } from './transactionBuilder';
@@ -32,14 +31,16 @@ export class StakingDeactivateBuilder extends TransactionBuilder {
3231
initBuilder(tx: Transaction): void {
3332
super.initBuilder(tx);
3433
const functionCall = tx.nearTransaction.actions[0].functionCall;
35-
this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString());
36-
this.contractCallWrapper.gas = functionCall.gas.toString();
34+
if (functionCall) {
35+
this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString());
36+
this.contractCallWrapper.gas = functionCall.gas.toString();
37+
}
3738
}
3839

3940
/**
4041
* Sets the gas of this transaction.
4142
*
42-
* @param {string} value the gas of this transaction
43+
* @param {string} gas the gas of this transaction
4344
* @returns {TransactionBuilder} This transaction builder
4445
*/
4546
public gas(gas: string): this {
@@ -51,7 +52,7 @@ export class StakingDeactivateBuilder extends TransactionBuilder {
5152
/**
5253
* Sets the amount of this transaction.
5354
*
54-
* @param {string} value the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction
55+
* @param {string} amount the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction
5556
* @returns {TransactionBuilder} This transaction builder
5657
*/
5758
public amount(amount: string): this {
@@ -66,7 +67,7 @@ export class StakingDeactivateBuilder extends TransactionBuilder {
6667
assert(gas, new BuildTransactionError('gas is required before building staking deactivate'));
6768
assert(args?.amount, new BuildTransactionError('amount is required before building staking deactivate'));
6869

69-
super.actions([NearAPI.transactions.functionCall(methodName, args, new BN(gas), new BN(deposit))]);
70+
super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]);
7071
const tx = await super.buildImplementation();
7172
tx.setTransactionType(TransactionType.StakingDeactivate);
7273
return tx;

modules/sdk-coin-near/src/lib/stakingWithdrawBuilder.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Transaction } from './transaction';
44
import BigNumber from 'bignumber.js';
55
import * as NearAPI from 'near-api-js';
66
import assert from 'assert';
7-
import BN from 'bn.js';
87

98
import { ContractCallWrapper } from './contractCallWrapper';
109
import { TransactionBuilder } from './transactionBuilder';
@@ -32,14 +31,16 @@ export class StakingWithdrawBuilder extends TransactionBuilder {
3231
initBuilder(tx: Transaction): void {
3332
super.initBuilder(tx);
3433
const functionCall = tx.nearTransaction.actions[0].functionCall;
35-
this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString());
36-
this.contractCallWrapper.gas = functionCall.gas.toString();
34+
if (functionCall) {
35+
this.contractCallWrapper.args = JSON.parse(Buffer.from(functionCall.args).toString());
36+
this.contractCallWrapper.gas = functionCall.gas.toString();
37+
}
3738
}
3839

3940
/**
4041
* Sets the gas of this transaction.
4142
*
42-
* @param {string} value the gas of this transaction
43+
* @param {string} gas the gas of this transaction
4344
* @returns {TransactionBuilder} This transaction builder
4445
*/
4546
public gas(gas: string): this {
@@ -51,7 +52,7 @@ export class StakingWithdrawBuilder extends TransactionBuilder {
5152
/**
5253
* Sets the amount of this transaction.
5354
*
54-
* @param {string} value the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction
55+
* @param {string} amount the amount in the minimum unit (1 Near = 1e24 yoctos) of this transaction
5556
* @returns {TransactionBuilder} This transaction builder
5657
*/
5758
public amount(amount: string): this {
@@ -66,7 +67,7 @@ export class StakingWithdrawBuilder extends TransactionBuilder {
6667
assert(gas, new BuildTransactionError('gas is required before building staking withdraw'));
6768
assert(args?.amount, new BuildTransactionError('amount is required before building staking withdraw'));
6869

69-
super.actions([NearAPI.transactions.functionCall(methodName, args, new BN(gas), new BN(deposit))]);
70+
super.actions([NearAPI.transactions.functionCall(methodName, args, BigInt(gas), BigInt(deposit))]);
7071
const tx = await super.buildImplementation();
7172
tx.setTransactionType(TransactionType.StakingWithdraw);
7273
return tx;

0 commit comments

Comments
 (0)