From 72270e20ea7625aea8c25cc695d99fdd5e3a5308 Mon Sep 17 00:00:00 2001 From: Eric Wang Date: Sat, 9 Dec 2023 07:06:18 -0800 Subject: [PATCH] fix(sdk-coin-ton): allow non bouncable address in transaction Ticket: EA-2457 TICKET: EA-2457 --- modules/sdk-coin-ton/src/lib/iface.ts | 1 + modules/sdk-coin-ton/src/lib/transaction.ts | 2 ++ modules/sdk-coin-ton/src/ton.ts | 8 ++++- modules/sdk-coin-ton/test/resources/ton.ts | 2 +- modules/sdk-coin-ton/test/unit/ton.ts | 28 +++++++++++++++ .../sdk-coin-ton/test/unit/transferBuilder.ts | 34 ++++++++++++++++++- 6 files changed, 72 insertions(+), 3 deletions(-) diff --git a/modules/sdk-coin-ton/src/lib/iface.ts b/modules/sdk-coin-ton/src/lib/iface.ts index 6d0a57cbfd..e4ee06647d 100644 --- a/modules/sdk-coin-ton/src/lib/iface.ts +++ b/modules/sdk-coin-ton/src/lib/iface.ts @@ -5,6 +5,7 @@ export interface TxData { id: string; sender: string; destination: string; + destinationAlias: string; amount: string; seqno: number; expirationTime: number; diff --git a/modules/sdk-coin-ton/src/lib/transaction.ts b/modules/sdk-coin-ton/src/lib/transaction.ts index b9bed0d750..02ae3843fd 100644 --- a/modules/sdk-coin-ton/src/lib/transaction.ts +++ b/modules/sdk-coin-ton/src/lib/transaction.ts @@ -38,10 +38,12 @@ export class Transaction extends BaseTransaction { } toJson(): TxData { + const non_bouncable = new TonWeb.Address(this.recipient.address).toString(true, true, false); return { id: this._id as string, sender: this.sender, destination: this.recipient.address, + destinationAlias: non_bouncable, amount: this.recipient.amount, seqno: this.seqno, expirationTime: this.expireTime, diff --git a/modules/sdk-coin-ton/src/ton.ts b/modules/sdk-coin-ton/src/ton.ts index 0501f17fd5..141b5a413f 100644 --- a/modules/sdk-coin-ton/src/ton.ts +++ b/modules/sdk-coin-ton/src/ton.ts @@ -18,6 +18,7 @@ import { KeyPair as TonKeyPair } from './lib/keyPair'; import BigNumber from 'bignumber.js'; import * as _ from 'lodash'; import { Transaction, TransactionBuilderFactory, Utils } from './lib'; +import TonWeb from 'tonweb'; export interface TonParseTransactionOptions extends ParseTransactionOptions { txHex: string; @@ -83,7 +84,12 @@ export class Ton extends BaseCoin { transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64')); const explainedTx = transaction.explainTransaction(); if (txParams.recipients !== undefined) { - const filteredRecipients = txParams.recipients?.map((recipient) => _.pick(recipient, ['address', 'amount'])); + const filteredRecipients = txParams.recipients?.map((recipient) => { + return { + address: new TonWeb.Address(recipient.address).toString(true, true, true), + amount: recipient.amount, + }; + }); const filteredOutputs = explainedTx.outputs.map((output) => _.pick(output, ['address', 'amount'])); if (!_.isEqual(filteredOutputs, filteredRecipients)) { diff --git a/modules/sdk-coin-ton/test/resources/ton.ts b/modules/sdk-coin-ton/test/resources/ton.ts index 6ca5279bf2..a2c566476c 100644 --- a/modules/sdk-coin-ton/test/resources/ton.ts +++ b/modules/sdk-coin-ton/test/resources/ton.ts @@ -36,7 +36,7 @@ export const recipients: Recipient[] = [ export const signedTransaction = { tx: 'te6cckEBAgEAsQAB4YgAQmy8N765HIcsCkExBYpKsfl7LUuXYW7MvV53d6EqWoADFgjyqp/3qccthFjfvskQ1XUeecHe8JVQ9DFGSmpwRovdPiRamZZ3PR2isMd1i5k+MdQCc9TCP/E5+rfkvMbYQU1NGLsruXHwAAAAgAAcAQB2QgB9Jzc15Nkj1GPyoSR0hIg9lzcoUjVC0MYRF1GRn2tVHpzEtAAAAAAAAAAAAAAAAAAAAAAAAHRlc3SUlVNc', - txId: '', + txId: '9gEyvIsHrA79g8pmEP1EBtO8GlpZ98QnUveSYmEx6fc=', signable: 'q28coBQ7sbBBAR/hG4eUdDayLDsPC9FKQtA/lSYYaSQ=', recipient: { address: 'EQD6Tm5rybJHqMflQkjpCRB7Lm5QpGqFoYwiLqMjPtaqPSdZ', diff --git a/modules/sdk-coin-ton/test/unit/ton.ts b/modules/sdk-coin-ton/test/unit/ton.ts index 0c9788704b..ff816a41d0 100644 --- a/modules/sdk-coin-ton/test/unit/ton.ts +++ b/modules/sdk-coin-ton/test/unit/ton.ts @@ -7,6 +7,7 @@ import * as testData from '../resources/ton'; import { TransactionExplanation } from '@bitgo/sdk-core'; import should from 'should'; import utils from '../../src/lib/utils'; +import Tonweb from 'tonweb'; describe('TON:', function () { const bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' }); @@ -62,6 +63,33 @@ describe('TON:', function () { .should.rejectedWith('Tx outputs does not match with expected txParams recipients'); }); + it('should succeed to verify transaction when recipients amount are numbers', async function () { + const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams)); + txParamsWithNumberAmounts.recipients[0].amount = '20000000'; + const verification = {}; + await basecoin + .verifyTransaction({ + txParams: txParamsWithNumberAmounts, + txPrebuild, + verification, + } as any) + .should.rejectedWith('Tx outputs does not match with expected txParams recipients'); + }); + + it('should succeed to verify transaction when recipient address are non bouncable', async function () { + const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams)); + txParamsWithNumberAmounts.recipients[0].address = new Tonweb.Address( + txParamsWithNumberAmounts.recipients[0].address + ).toString(true, true, false); + const verification = {}; + const isVerified = await basecoin.verifyTransaction({ + txParams: txParamsWithNumberAmounts, + txPrebuild, + verification, + } as any); + isVerified.should.equal(true); + }); + it('should fail to verify transaction with invalid param', async function () { const txPrebuild = {}; await basecoin diff --git a/modules/sdk-coin-ton/test/unit/transferBuilder.ts b/modules/sdk-coin-ton/test/unit/transferBuilder.ts index 8a687d937a..c35a49c131 100644 --- a/modules/sdk-coin-ton/test/unit/transferBuilder.ts +++ b/modules/sdk-coin-ton/test/unit/transferBuilder.ts @@ -44,7 +44,7 @@ describe('Ton Transfer Builder', () => { const jsonTx = builtTx.toJson(); should.equal(builtTx.type, TransactionType.Send); should.equal(builtTx.signablePayload.toString('base64'), testData.signedTransaction.signable); - // should.equal(builtTx.id, testData.signedTransaction.txId); + should.equal(builtTx.id, testData.signedTransaction.txId); const builder2 = factory.from(builtTx.toBroadcastFormat()); const builtTx2 = await builder2.build(); should.equal(builtTx2.type, TransactionType.Send); @@ -120,4 +120,36 @@ describe('Ton Transfer Builder', () => { should.equal(Buffer.from(signature).toString('hex'), Buffer.from(signature2).toString('hex')); should.equal(tx.toBroadcastFormat(), tx2.toBroadcastFormat()); }); + + it('should build transfer tx for non-bouncable address', async function () { + const txBuilder = factory.getTransferBuilder(); + txBuilder.sender(testData.sender.address); + txBuilder.sequenceNumber(0); + txBuilder.publicKey(testData.sender.publicKey); + txBuilder.expireTime(1234567890); + const address = 'UQAWzEKcdnykvXfUNouqdS62tvrp32bCxuKS6eQrS6ISgZ8t'; + const amount = '10000000'; + txBuilder.send({ address, amount }); + txBuilder.setMessage('test'); + const tx = await txBuilder.build(); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.sender.address, + value: amount, + coin: 'tton', + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address, + value: amount, + coin: 'tton', + }); + const txJson = tx.toJson(); + txJson.destination.should.equal(address); + const builder2 = factory.from(tx.toBroadcastFormat()); + const tx2 = await builder2.build(); + const txJson2 = tx2.toJson(); + txJson2.destinationAlias.should.equal(address); + }); });