Skip to content

Commit 78b18d4

Browse files
test: add memo based jetton transfer tests
TICKET: COIN-5626
1 parent ee1b696 commit 78b18d4

File tree

4 files changed

+297
-289
lines changed

4 files changed

+297
-289
lines changed

modules/sdk-coin-ton/test/resources/ton.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ export const signedTokenSendTransaction = {
9090
},
9191
};
9292

93+
export const signedTokenSendTransactionForMemoId = {
94+
tx: 'te6cckECGgEABB0AAuGIAVSGb+UGjjP3lvt+zFA8wouI3McEd6CKbO2TwcZ3OfLKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmpoxdJlgLSAAAAAAADgEXAgE0AhYBFP8A9KQT9LzyyAsDAgEgBBECAUgFCALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/pAMCD6RAHIygfL/8nQ7UTQgQFA1yH0BDBcgQEI9ApvoTGzkl8H4AXTP8glghBwbHVnupI4MOMNA4IQZHN0crqSXwbjDQYHAHgB+gD0BDD4J28iMFAKoSG+8uBQghBwbHVngx6xcIAYUATLBSbPFlj6Ahn0AMtpF8sfUmDLPyDJgED7AAYAilAEgQEI9Fkw7UTQgQFA1yDIAc8W9ADJ7VQBcrCOI4IQZHN0coMesXCAGFAFywVQA88WI/oCE8tqyx/LP8mAQPsAkl8D4gIBIAkQAgEgCg8CAVgLDAA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA0OABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AABG4yX7UTQ1wsfgAWb0kK29qJoQICga5D6AhhHDUCAhHpJN9KZEM5pA+n/mDeBKAG3gQFImHFZ8xhAT48oMI1xgg0x/TH9MfAvgju/Jk7UTQ0x/TH9P/9ATRUUO68qFRUbryogX5AVQQZPkQ8qP4ACSkyMsfUkDLH1Iwy/9SEPQAye1U+A8B0wchwACfbFGTINdKltMH1AL7AOgw4CHAAeMAIcAC4wABwAORMOMNA6TIyx8Syx/L/xITFBUAbtIH+gDU1CL5AAXIygcVy//J0Hd0gBjIywXLAiLPFlAF+gIUy2sSzMzJc/sAyEAUgQEI9FHypwIAcIEBCNcY+gDTP8hUIEeBAQj0UfKnghBub3RlcHSAGMjLBcsCUAbPFlAE+gIUy2oSyx/LP8lz+wACAGyBAQjXGPoA0z8wUiSBAQj0WfKnghBkc3RycHSAGMjLBcsCUAXPFlAD+gITy2rLHxLLP8lz+wAACvQAye1UAFEAAAAAKamjF9NTAQHUHhbX00VGZ3d2r8hbJxuz7PaxmuCOJ6kgckppQAFmQgABT9LR3Iqffskp0J9gWYO8Azlnb33BCMj8FqIUIGxGOZpiWgAAAAAAAAAAAAAAAAABGAGuD4p+pQAAAAAAAAAAQ7msoAgA/BGdBi/R01erquxJOvPgGKclBawUs3MAi0/IdctKQz8AKpDN/KDRxn7y32/ZigeYUXEbmOCO9BFNnbJ4OM7nPllGHoSBGQAkAAAAAGpldHRvbiB0ZXN0aW5nwHtw7A==',
95+
recipient: {
96+
address: 'UQB-CM6DF-jpq9XVdiSdefAMU5KC1gpZuYBFp-Q65aUhn0OP?memoId=1234',
97+
amount: '1000000000',
98+
},
99+
};
100+
93101
export const signedSendTransactionForMemoId = {
94102
tx: 'te6cckEBAgEAqQAB4YgBJAxo7vqHF++LJ4bC/kJ8A1uVRskrKlrKJZ8rIB0tF+gCadlSX+hPo2mmhZyi0p3zTVUYVRkcmrCm97cSUFSa2vzvCArM3APg+ww92r3IcklNjnzfKOgysJVQXiCvj9SAaU1NGLsotvRwAAAAMAAcAQBmQgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr5zEtAAAAAAAAAAAAAAAAAAAdfZO7w==',
95103
recipient: {

modules/sdk-coin-ton/test/unit/jettonToken.ts

Lines changed: 288 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import 'should';
2-
1+
import Tonweb from 'tonweb';
2+
import should from 'should';
3+
import * as sinon from 'sinon';
4+
import { TransactionExplanation } from '@bitgo/sdk-core';
35
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
46
import { BitGoAPI } from '@bitgo/sdk-api';
5-
import { JettonToken } from '../../src';
7+
import { JettonToken, Ton, TonParseTransactionOptions } from '../../src';
8+
import * as testData from '../resources/ton';
69

710
describe('Jetton Tokens', function () {
811
let bitgo: TestBitGoAPI;
@@ -11,6 +14,31 @@ describe('Jetton Tokens', function () {
1114
const testnetTokenName = 'tton:ukwny-us';
1215
const mainnetTokenName = 'ton:usdt';
1316

17+
const txPrebuildList = [
18+
{
19+
txHex: Buffer.from(testData.signedTokenSendTransaction.tx, 'base64').toString('hex'),
20+
txInfo: {},
21+
},
22+
];
23+
const txPrebuildBounceableList = [
24+
{
25+
txHex: Buffer.from(testData.signedTokenSendTransaction.txBounceable, 'base64').toString('hex'),
26+
txInfo: {},
27+
},
28+
];
29+
30+
const txParamsList = [
31+
{
32+
recipients: [testData.signedTokenSendTransaction.recipient],
33+
},
34+
];
35+
36+
const txParamsBounceableList = [
37+
{
38+
recipients: [testData.signedTokenSendTransaction.recipientBounceable],
39+
},
40+
];
41+
1442
before(function () {
1543
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
1644
JettonToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
@@ -46,4 +74,261 @@ describe('Jetton Tokens', function () {
4674
mainnetJettonToken.contractAddress.should.equal('EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs');
4775
mainnetJettonToken.decimalPlaces.should.equal(6);
4876
});
77+
78+
describe('Verify Jetton transaction: ', () => {
79+
txParamsList.forEach((_, index) => {
80+
const txParams = txParamsList[index];
81+
const txPrebuild = txPrebuildList[index];
82+
const txParamsBounceable = txParamsBounceableList[index];
83+
const txPrebuildBounceable = txPrebuildBounceableList[index];
84+
85+
it('should succeed to verify transaction', async function () {
86+
const verification = {};
87+
const isTransactionVerified = await testnetJettonToken.verifyTransaction({
88+
txParams,
89+
txPrebuild,
90+
verification,
91+
} as any);
92+
isTransactionVerified.should.equal(true);
93+
94+
const isBounceableTransactionVerified = await testnetJettonToken.verifyTransaction({
95+
txParams: txParamsBounceable,
96+
txPrebuild: txPrebuildBounceable,
97+
verification: {},
98+
} as any);
99+
isBounceableTransactionVerified.should.equal(true);
100+
});
101+
102+
it('should succeed to verify transaction when recipients amount are numbers', async function () {
103+
const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams));
104+
txParamsWithNumberAmounts.recipients[0].amount = 20000000;
105+
const verification = {};
106+
await testnetJettonToken
107+
.verifyTransaction({
108+
txParams: txParamsWithNumberAmounts,
109+
txPrebuild,
110+
verification,
111+
} as any)
112+
.should.rejectedWith('Tx outputs does not match with expected txParams recipients');
113+
});
114+
115+
it('should succeed to verify transaction when recipients amount are strings', async function () {
116+
const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams));
117+
txParamsWithNumberAmounts.recipients[0].amount = '20000000';
118+
const verification = {};
119+
await testnetJettonToken
120+
.verifyTransaction({
121+
txParams: txParamsWithNumberAmounts,
122+
txPrebuild,
123+
verification,
124+
} as any)
125+
.should.rejectedWith('Tx outputs does not match with expected txParams recipients');
126+
});
127+
128+
it('should succeed to verify transaction when recipients amounts are number and amount is same', async function () {
129+
const verification = {};
130+
await testnetJettonToken
131+
.verifyTransaction({
132+
txParams,
133+
txPrebuild,
134+
verification,
135+
} as any)
136+
.should.resolvedWith(true);
137+
});
138+
139+
it('should succeed to verify transaction when recipients amounts are string and amount is same', async function () {
140+
const verification = {};
141+
await testnetJettonToken
142+
.verifyTransaction({
143+
txParams,
144+
txPrebuild,
145+
verification,
146+
} as any)
147+
.should.resolvedWith(true);
148+
});
149+
150+
it('should succeed to verify transaction when recipient address are non bounceable', async function () {
151+
const txParamsWithNumberAmounts = JSON.parse(JSON.stringify(txParams));
152+
txParamsWithNumberAmounts.recipients[0].address = new Tonweb.Address(
153+
txParamsWithNumberAmounts.recipients[0].address
154+
).toString(true, true, false);
155+
const verification = {};
156+
const isVerified = await testnetJettonToken.verifyTransaction({
157+
txParams: txParamsWithNumberAmounts,
158+
txPrebuild,
159+
verification,
160+
} as any);
161+
isVerified.should.equal(true);
162+
});
163+
164+
it('should fail to verify transaction with invalid param', async function () {
165+
const txPrebuild = {};
166+
await testnetJettonToken
167+
.verifyTransaction({
168+
txParams,
169+
txPrebuild,
170+
} as any)
171+
.should.rejectedWith('missing required tx prebuild property txHex');
172+
});
173+
});
174+
175+
it('should succeed to verify transaction with recipient having memo', async function () {
176+
const txParams = {
177+
recipients: [testData.signedTokenSendTransactionForMemoId.recipient],
178+
};
179+
const txPrebuild = {
180+
txHex: Buffer.from(testData.signedTokenSendTransactionForMemoId.tx, 'base64').toString('hex'),
181+
txInfo: {},
182+
};
183+
const verification = {};
184+
const isTransactionVerified = await testnetJettonToken.verifyTransaction({
185+
txParams,
186+
txPrebuild,
187+
verification,
188+
} as any);
189+
isTransactionVerified.should.equal(true);
190+
});
191+
});
192+
193+
describe('Explain Jetton Transaction: ', () => {
194+
it('should explain a jetton transfer transaction', async function () {
195+
const explainedTransaction = (await testnetJettonToken.explainTransaction({
196+
txHex: Buffer.from(testData.signedTokenSendTransaction.tx, 'base64').toString('hex'),
197+
})) as TransactionExplanation;
198+
explainedTransaction.should.deepEqual({
199+
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'],
200+
id: testData.signedTokenSendTransaction.txId,
201+
outputs: [
202+
{
203+
address: testData.signedTokenSendTransaction.recipient.address,
204+
amount: testData.signedTokenSendTransaction.recipient.amount,
205+
},
206+
],
207+
outputAmount: testData.signedTokenSendTransaction.recipient.amount,
208+
changeOutputs: [],
209+
changeAmount: '0',
210+
fee: { fee: 'UNKNOWN' },
211+
withdrawAmount: undefined,
212+
});
213+
});
214+
215+
it('should explain a non-bounceable jetton transfer transaction', async function () {
216+
const explainedTransaction = (await testnetJettonToken.explainTransaction({
217+
txHex: Buffer.from(testData.signedTokenSendTransaction.txBounceable, 'base64').toString('hex'),
218+
toAddressBounceable: false,
219+
fromAddressBounceable: false,
220+
})) as TransactionExplanation;
221+
explainedTransaction.should.deepEqual({
222+
displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'withdrawAmount'],
223+
id: testData.signedTokenSendTransaction.txIdBounceable,
224+
outputs: [
225+
{
226+
address: testData.signedTokenSendTransaction.recipientBounceable.address,
227+
amount: testData.signedTokenSendTransaction.recipientBounceable.amount,
228+
},
229+
],
230+
outputAmount: testData.signedTokenSendTransaction.recipientBounceable.amount,
231+
changeOutputs: [],
232+
changeAmount: '0',
233+
fee: { fee: 'UNKNOWN' },
234+
withdrawAmount: undefined,
235+
});
236+
});
237+
238+
it('should fail to explain transaction with missing params', async function () {
239+
try {
240+
await testnetJettonToken.explainTransaction({});
241+
} catch (error) {
242+
should.equal(error.message, 'Invalid transaction');
243+
}
244+
});
245+
246+
it('should fail to explain transaction with invalid params', async function () {
247+
try {
248+
await testnetJettonToken.explainTransaction({ txHex: 'randomString' });
249+
} catch (error) {
250+
should.equal(error.message, 'Invalid transaction');
251+
}
252+
});
253+
});
254+
255+
describe('Parse Jetton Transactions: ', () => {
256+
const transactionsList = [testData.signedTokenSendTransaction.tx];
257+
const transactionInputsResponseList = [
258+
[
259+
{
260+
address: 'EQCqQzfyg0cZ-8t9v2YoHmFFxG5jgjvQRTZ2yeDjO5z5ZRy9',
261+
amount: testData.tokenRecipients[0].amount,
262+
},
263+
],
264+
];
265+
266+
const transactionInputsResponseBounceableList = [
267+
[
268+
{
269+
address: testData.tokenSender.address,
270+
amount: testData.tokenRecipients[0].amount,
271+
},
272+
],
273+
];
274+
275+
const transactionOutputsResponseList = [
276+
[
277+
{
278+
address: 'EQB-CM6DF-jpq9XVdiSdefAMU5KC1gpZuYBFp-Q65aUhnx5K',
279+
amount: testData.tokenRecipients[0].amount,
280+
},
281+
],
282+
];
283+
284+
const transactionOutputsResponseBounceableList = [
285+
[
286+
{
287+
address: testData.tokenRecipients[0].address,
288+
amount: testData.tokenRecipients[0].amount,
289+
},
290+
],
291+
];
292+
293+
transactionsList.forEach((_, index) => {
294+
const transaction = transactionsList[index];
295+
const transactionInputsResponse = transactionInputsResponseList[index];
296+
const transactionInputsResponseBounceable = transactionInputsResponseBounceableList[index];
297+
const transactionOutputsResponse = transactionOutputsResponseList[index];
298+
const transactionOutputsResponseBounceable = transactionOutputsResponseBounceableList[index];
299+
300+
it('should parse a TON transaction', async function () {
301+
const parsedTransaction = await testnetJettonToken.parseTransaction({
302+
txHex: Buffer.from(transaction, 'base64').toString('hex'),
303+
});
304+
305+
parsedTransaction.should.deepEqual({
306+
inputs: transactionInputsResponse,
307+
outputs: transactionOutputsResponse,
308+
});
309+
});
310+
311+
it('should parse a non-bounceable TON transaction', async function () {
312+
const parsedTransaction = await testnetJettonToken.parseTransaction({
313+
txHex: Buffer.from(transaction, 'base64').toString('hex'),
314+
toAddressBounceable: false,
315+
fromAddressBounceable: false,
316+
} as TonParseTransactionOptions);
317+
318+
parsedTransaction.should.deepEqual({
319+
inputs: transactionInputsResponseBounceable,
320+
outputs: transactionOutputsResponseBounceable,
321+
});
322+
});
323+
324+
it('should fail to parse a TON transaction when explainTransaction response is undefined', async function () {
325+
const stub = sinon.stub(Ton.prototype, 'explainTransaction');
326+
stub.resolves(undefined);
327+
await testnetJettonToken
328+
.parseTransaction({ txHex: transaction })
329+
.should.be.rejectedWith('invalid raw transaction');
330+
stub.restore();
331+
});
332+
});
333+
});
49334
});

0 commit comments

Comments
 (0)