Skip to content

Commit f88d569

Browse files
committed
feat(isdk-coin-ton): add ton whales deposit tx building
TICKET: SC-4503
1 parent edc099f commit f88d569

6 files changed

Lines changed: 204 additions & 1 deletion

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export const JETTON_TRANSFER_OPCODE = 0x0f8a7ea5;
44
export const WITHDRAW_OPCODE = '00001000';
55
export const VESTING_CONTRACT_CODE_B64 =
66
'te6cckECHAEAA/sAART/APSkE/S88sgLAQIBIAISAgFIAwUDrNBsIiDXScFgkVvgAdDTAwFxsJFb4PpAMNs8AdMf0z/4S1JAxwUjghCnczrNurCOpGwS2zyCEPdzOs0BcIAYyMsFUATPFiP6AhPLassfyz/JgED7AOMOExQEAc74SlJAxwUDghByWKabuhOwjtGOLAH6QH/IygAC+kQByMoHy//J0PhEECOBAQj0QfhkINdKwgAglQHUMNAB3rMS5oIQ8limmzJwgBjIywVQBM8WI/oCE8tqyx/LP8mAQPsA2zySXwPiGwIBIAYPAgEgBwoCAW4ICQAZrc52omhAIGuQ64X/wAAZrx32omhAEGuQ64WPwAIBYgsMAUutNG2eNvwiRw1AgIR6STfSmRDOaQPp/5g3gSgBt4EBSJhxWfMYQBMCAWoNDgAPol+1E0NcLH4BL6LHbPPpEAcjKB8v/ydD4RIEBCPQKb6ExhMCASAQEQEpukYts8+EX4RvhH+Ej4SfhK+Ev4RIEwINuYRts82zyBMVA7jygwjXGCDTH9Mf0x8C+CO78mTtRNDTH9Mf0/8wWrryoVAzuvKiAvkBQDP5EPKj+ADbPCDXSsABjpntRO1F7UeRW+1n7WXtZI6C2zztQe3xAfL/kTDi+EGk+GHbPBMUGwB+7UTQ0x8B+GHTHwH4YtP/Afhj9AQB+GTUAdDTPwH4ZdMfAfhm0x8B+GfTHwH4aPoAAfhp+kAB+Gr6QAH4a9HRAlzTB9TR+CPbPCDCAI6bIsAD8uBkIdDTA/pAMfpA+EpSIMcFs5JfBOMNkTDiAfsAFRYAYPhF+EagUhC8kjBw4PhF+EigUhC5kzD4SeD4SfhJ+EUTofhHqQT4RvhHqQQQI6mEoQP6IfpEAcjKB8v/ydD4RIEBCPQKb6Exj18zAXKwwALy4GUB+gAxcdch+gAx+gAx0z8x0x8x0wABwADy4GbTAAGT1DDQ3iFx2zyOKjHTHzAgghBOc3RLuiGCEEdldCS6sSGCEFZ0Q3C6sQGCEFZvdGW6sfLgZ+MOcJJfA+IgwgAYFxoC6gFw2zyObSDXScIAjmPTHyHAACKDC7qxIoEQAbqxIoIQR9VDkbqxIoIQWV8HvLqxIoIQafswbLqxIoIQVm90ZbqxIoIQVnRDcLqx8uBnAcAAIddJwgCwjhXTBzAgwGQhwHexIcBEsQHAV7Hy4GiRMOKRMOLjDRgZAEQB+kQBw/+SW3DgAfgzIG6SW3Dg0CDXSYMHuZJbcODXC/+6ABrTHzCCEFZvdGW68uBnAA6TcvsCkTDiAGb4SPhH+Eb4RcjLP8sfyx/LH/hJ+gL4Ss8W+EvPFsn4RPhD+EL4QcjLH8sfy//0AMzJ7VSo1+S9';
7+
export const TON_WHALES_DEPOSIT_OPCODE = '2077040623';
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
2+
import { Recipient, TransactionType } from '@bitgo/sdk-core';
3+
import { TransactionBuilder } from './transactionBuilder';
4+
import { Transaction } from './transaction';
5+
import { TON_WHALES_DEPOSIT_OPCODE } from './constants';
6+
7+
export class TonWhalesDepositBuilder extends TransactionBuilder {
8+
constructor(_coinConfig: Readonly<CoinConfig>) {
9+
super(_coinConfig);
10+
this._transaction = new Transaction(_coinConfig);
11+
}
12+
13+
protected get transactionType(): TransactionType {
14+
return TransactionType.TonWhalesDeposit;
15+
}
16+
17+
setDepositMessage(): TonWhalesDepositBuilder {
18+
// Deposit payload is just OpCode + QueryId.
19+
// The Amount is in the transaction value (recipient.amount)
20+
// The Gas Limit is hardcoded in Transaction.ts build()
21+
const queryId = '0000000000000000';
22+
this.transaction.message = TON_WHALES_DEPOSIT_OPCODE + queryId;
23+
return this;
24+
}
25+
26+
send(recipient: Recipient): TonWhalesDepositBuilder {
27+
this.transaction.recipient = recipient;
28+
return this;
29+
}
30+
31+
setMessage(msg: string): TonWhalesDepositBuilder {
32+
throw new Error('Method not implemented.');
33+
}
34+
}

modules/sdk-coin-ton/src/lib/transaction.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import { Cell } from 'tonweb/dist/types/boc/cell';
55
import { BaseKey, BaseTransaction, Entry, Recipient, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
66
import { BaseCoin as CoinConfig } from '@bitgo/statics';
77
import { TransactionExplanation, TxData } from './iface';
8-
import { WITHDRAW_OPCODE, WALLET_ID, JETTON_TRANSFER_OPCODE, VESTING_CONTRACT_WALLET_ID } from './constants';
8+
import {
9+
WITHDRAW_OPCODE,
10+
WALLET_ID,
11+
JETTON_TRANSFER_OPCODE,
12+
VESTING_CONTRACT_WALLET_ID,
13+
TON_WHALES_DEPOSIT_OPCODE,
14+
} from './constants';
915

1016
export class Transaction extends BaseTransaction {
1117
public recipient: Recipient;
@@ -127,6 +133,11 @@ export class Transaction extends BaseTransaction {
127133
payloadCell.bits.writeUint(parseInt(WITHDRAW_OPCODE, 16), 32);
128134
payloadCell.bits.writeUint(parseInt(queryId, 16), 64);
129135
payloadCell.bits.writeCoins(new BN(withdrawAmount));
136+
} else if (payload.length >= 24 && payload.substring(0, 8) === TON_WHALES_DEPOSIT_OPCODE) {
137+
const queryId = payload.substring(8, 24);
138+
payloadCell.bits.writeUint(parseInt(TON_WHALES_DEPOSIT_OPCODE, 16), 32);
139+
payloadCell.bits.writeUint(parseInt(queryId, 16), 64);
140+
payloadCell.bits.writeCoins(TonWeb.utils.toNano('20'));
130141
} else {
131142
payloadCell.bits.writeUint(0, 32);
132143
payloadCell.bits.writeString(payload);
@@ -369,6 +380,10 @@ export class Transaction extends BaseTransaction {
369380
forwardTonAmount: forwardTonAmount.toString(),
370381
message: message,
371382
};
383+
} else if (opcode === parseInt(TON_WHALES_DEPOSIT_OPCODE, 16)) {
384+
this.transactionType = TransactionType.TonWhalesDeposit;
385+
const queryId = order.loadUint(64).toNumber();
386+
payload = TON_WHALES_DEPOSIT_OPCODE + queryId.toString(16).padStart(16, '0');
372387
} else {
373388
payload = '';
374389
}

modules/sdk-coin-ton/src/lib/transactionBuilderFactory.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { SingleNominatorWithdrawBuilder } from './singleNominatorWithdrawBuilder
66
import { Transaction } from './transaction';
77
import { TokenTransferBuilder } from './tokenTransferBuilder';
88
import { TokenTransaction } from './tokenTransaction';
9+
import { TonWhalesDepositBuilder } from './tonWhalesDepositBuilder';
910

1011
export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
1112
constructor(_coinConfig: Readonly<CoinConfig>) {
@@ -37,6 +38,9 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
3738
case TransactionType.SendToken:
3839
builder = this.getTokenTransferBuilder();
3940
break;
41+
case TransactionType.TonWhalesDeposit:
42+
builder = this.getTonWhalesDepositBuilder();
43+
break;
4044
default:
4145
throw new InvalidTransactionError('unsupported transaction');
4246
}
@@ -70,4 +74,8 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
7074
getWalletInitializationBuilder(): void {
7175
throw new Error('Method not implemented.');
7276
}
77+
78+
getTonWhalesDepositBuilder(): TonWhalesDepositBuilder {
79+
return new TonWhalesDepositBuilder(this._coinConfig);
80+
}
7381
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import should from 'should';
2+
import { TransactionType } from '@bitgo/sdk-core';
3+
import { TransactionBuilderFactory } from '../../src/lib/transactionBuilderFactory';
4+
import { coins } from '@bitgo/statics';
5+
import * as testData from '../resources/ton';
6+
import { KeyPair } from '../../src/lib/keyPair';
7+
import * as utils from '../../src/lib/utils';
8+
import { TON_WHALES_DEPOSIT_OPCODE } from '../../src/lib/constants';
9+
10+
describe('Ton Whales Deposit Builder', () => {
11+
const factory = new TransactionBuilderFactory(coins.get('tton'));
12+
13+
// Use a valid pool address (TonWhales Testnet Pool)
14+
const poolAddress = 'kQDr9Sq482A6ikIUh5mUUjJaBUUJBrye13CJiDB-R31_l7mg';
15+
const depositAmount = '10000000000'; // 10 TON
16+
17+
it('should build a unsigned deposit tx', async function () {
18+
const txBuilder = factory.getTonWhalesDepositBuilder();
19+
20+
// 1. Setup Sender using fixture data
21+
txBuilder.sender(testData.sender.address);
22+
txBuilder.sequenceNumber(0);
23+
txBuilder.publicKey(testData.sender.publicKey);
24+
txBuilder.expireTime(1234567890);
25+
26+
// 2. Setup Deposit
27+
txBuilder.send({ address: poolAddress, amount: depositAmount });
28+
txBuilder.setDepositMessage(); // Sets the OpCode
29+
30+
const tx = await txBuilder.build();
31+
const json = tx.toJson();
32+
33+
// 3. Verify Type and Fields
34+
should.equal(tx.type, TransactionType.TonWhalesDeposit);
35+
36+
// Check Outputs
37+
tx.outputs.length.should.equal(1);
38+
tx.outputs[0].should.deepEqual({
39+
address: poolAddress,
40+
value: depositAmount,
41+
coin: 'tton',
42+
});
43+
44+
// Check Payload contains OpCode + QueryID (0x7BCD136F + 0...0)
45+
const expectedPayload = TON_WHALES_DEPOSIT_OPCODE + '0000000000000000';
46+
should.equal(tx['message'], expectedPayload);
47+
48+
// Check JSON
49+
should.equal(json.bounceable, false);
50+
should.equal(json.amount, depositAmount);
51+
should.equal(json.destination, poolAddress);
52+
});
53+
54+
it('should parse a raw deposit transaction (Round Trip)', async function () {
55+
// 1. Build the transaction
56+
const buildStep = factory.getTonWhalesDepositBuilder();
57+
buildStep.sender(testData.sender.address);
58+
buildStep.sequenceNumber(123);
59+
buildStep.publicKey(testData.sender.publicKey);
60+
buildStep.expireTime(1700000000);
61+
buildStep.send({ address: poolAddress, amount: depositAmount });
62+
buildStep.setDepositMessage();
63+
64+
const builtTx = await buildStep.build();
65+
const rawBoc = builtTx.toBroadcastFormat();
66+
67+
// 2. Parse it back (Simulate receiving from chain/API)
68+
const parseStep = factory.from(rawBoc);
69+
const parsedTx = await parseStep.build();
70+
const parsedJson = parsedTx.toJson();
71+
72+
// 3. Verify Parsing Logic correctly identified it as TonWhalesDeposit
73+
should.equal(parsedTx.type, TransactionType.TonWhalesDeposit);
74+
75+
// Verify fields persisted
76+
parsedJson.seqno.should.equal(123);
77+
parsedJson.expirationTime.should.equal(1700000000);
78+
parsedJson.amount.should.equal(depositAmount);
79+
parsedJson.destination.should.equal(poolAddress);
80+
parsedJson.sender.should.equal(testData.sender.address);
81+
});
82+
83+
it('should build a signed deposit tx using add signature', async function () {
84+
// Use private key from fixtures
85+
const keyPair = new KeyPair({ prv: testData.privateKeys.prvKey1 });
86+
const publicKey = keyPair.getKeys().pub;
87+
const address = await utils.default.getAddressFromPublicKey(publicKey);
88+
89+
const txBuilder = factory.getTonWhalesDepositBuilder();
90+
txBuilder.sender(address);
91+
txBuilder.sequenceNumber(0);
92+
txBuilder.publicKey(publicKey);
93+
const expireAt = Math.floor(Date.now() / 1e3) + 60 * 60 * 24 * 7;
94+
txBuilder.expireTime(expireAt);
95+
96+
txBuilder.send({ address: poolAddress, amount: depositAmount });
97+
txBuilder.setDepositMessage();
98+
99+
const tx = await txBuilder.build();
100+
101+
// Sign
102+
const signable = tx.signablePayload;
103+
const signature = keyPair.signMessageinUint8Array(signable);
104+
105+
// Add Signature
106+
txBuilder.addSignature(keyPair.getKeys(), Buffer.from(signature));
107+
108+
const signedTx = await txBuilder.build();
109+
110+
// Re-parse to verify signature attachment
111+
const builder2 = factory.from(signedTx.toBroadcastFormat());
112+
const tx2 = await builder2.build();
113+
114+
should.equal(tx.toBroadcastFormat(), tx2.toBroadcastFormat());
115+
should.equal(tx2.type, TransactionType.TonWhalesDeposit);
116+
should.exist(tx2.toJson().signature);
117+
});
118+
119+
it('should build deposit tx with bounceable flag', async function () {
120+
const txBuilder = factory.getTonWhalesDepositBuilder();
121+
txBuilder.sender(testData.sender.address);
122+
txBuilder.sequenceNumber(0);
123+
txBuilder.publicKey(testData.sender.publicKey);
124+
txBuilder.expireTime(1234567890);
125+
126+
txBuilder.send({ address: poolAddress, amount: depositAmount });
127+
txBuilder.setDepositMessage();
128+
129+
// Set Bounceable
130+
txBuilder.bounceable(true);
131+
132+
const tx = await txBuilder.build();
133+
should.equal(tx.type, TransactionType.TonWhalesDeposit);
134+
should.equal(tx.toJson().bounceable, true);
135+
136+
// Round trip verification
137+
const builder2 = factory.from(tx.toBroadcastFormat());
138+
const tx2 = await builder2.build();
139+
should.equal(tx2.toJson().bounceable, true);
140+
});
141+
});

modules/sdk-core/src/account-lib/baseCoin/enum.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ export enum TransactionType {
119119

120120
// flrp
121121
ImportToC,
122+
123+
// ton whales
124+
TonWhalesDeposit,
125+
TonWhalesWithdraw,
122126
}
123127

124128
/**

0 commit comments

Comments
 (0)