Skip to content

Commit 8ddf8ae

Browse files
Support Botbitgobot
authored andcommitted
fix(sdk-coin-ton): expose memo from TON transfer transactions
Include the on-chain text comment (used as routing memoId) in the parsed transaction output for TON transfers. - Add optional `memoId` field to `TxData` interface in `iface.ts` - Populate `memoId` in `Transaction.toJson()` from `this.message` when transaction type is a plain `Send` transfer - Propagate `memo` into `inputs`/`outputs` entries in the `parseTransaction()` method in `ton.ts` - Extract `action.memo` from WASM-parsed send actions in `explainTransactionWasm.ts` and include it in output objects Without this fix, TON deposit transfers with a text comment (e.g. a customer's routing memoId like 572826509) returned `memoId=null` in the BitGo transfer response, making it impossible for Bullish and other integrators to credit the depositing user. Ticket: COINS-1247 Session-Id: f01b1f1e-d256-4604-bfdb-c17205eda6f3 Task-Id: 6eefbcbb-ceb1-4c13-b32f-be3cc12e6c2c
1 parent 6e5c2b8 commit 8ddf8ae

6 files changed

Lines changed: 62 additions & 17 deletions

File tree

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ function extractOutputs(
2323
parsed: WasmParsedTransaction,
2424
toAddressBounceable: boolean
2525
): {
26-
outputs: { address: string; amount: string }[];
26+
outputs: { address: string; amount: string; memo?: string }[];
2727
outputAmount: string;
2828
withdrawAmount: string | undefined;
2929
} {
30-
const outputs: { address: string; amount: string }[] = [];
30+
const outputs: { address: string; amount: string; memo?: string }[] = [];
3131
let withdrawAmount: string | undefined;
3232

3333
for (const action of parsed.sendActions) {
@@ -39,10 +39,14 @@ function extractOutputs(
3939
} else {
4040
// destinationBounceable is always EQ... (bounceable)
4141
// destination respects the original bounce flag (UQ... when bounce=false)
42-
outputs.push({
42+
const output: { address: string; amount: string; memo?: string } = {
4343
address: toAddressBounceable ? action.destinationBounceable : action.destination,
4444
amount: String(action.amount),
45-
});
45+
};
46+
if (action.memo) {
47+
output.memo = action.memo;
48+
}
49+
outputs.push(output);
4650
}
4751

4852
// withdrawAmount comes from the body payload parsed by WASM (not the message TON value)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export interface TxData {
1717
sub_wallet_id: number;
1818
signature: string;
1919
bounceable: boolean;
20+
memoId?: string;
2021
}
2122

2223
export type TransactionExplanation = ITransactionExplanation<TransactionFee>;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class Transaction extends BaseTransaction {
5353
true,
5454
!new TonWeb.Address(this.recipient.address).isBounceable
5555
);
56-
return {
56+
const result: TxData = {
5757
id: this._id as string,
5858
sender: this.sender,
5959
destination: this.recipient.address,
@@ -67,6 +67,10 @@ export class Transaction extends BaseTransaction {
6767
signature: this._signatures[0],
6868
bounceable: this.bounceable,
6969
};
70+
if (this.message && typeof this.message === 'string' && this._type === TransactionType.Send) {
71+
result.memoId = this.message;
72+
}
73+
return result;
7074
}
7175

7276
get signablePayload(): Buffer {

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -219,19 +219,21 @@ export class Ton extends BaseCoin {
219219

220220
const rebuiltTransaction = await transactionBuilder.build();
221221
const parsedTransaction = rebuiltTransaction.toJson();
222+
const inputEntry: { address: string; amount: string; memo?: string } = {
223+
address: parsedTransaction.sender,
224+
amount: parsedTransaction.amount,
225+
};
226+
const outputEntry: { address: string; amount: string; memo?: string } = {
227+
address: parsedTransaction.destination,
228+
amount: parsedTransaction.amount,
229+
};
230+
if (parsedTransaction.memoId) {
231+
inputEntry.memo = parsedTransaction.memoId;
232+
outputEntry.memo = parsedTransaction.memoId;
233+
}
222234
return {
223-
inputs: [
224-
{
225-
address: parsedTransaction.sender,
226-
amount: parsedTransaction.amount,
227-
},
228-
],
229-
outputs: [
230-
{
231-
address: parsedTransaction.destination,
232-
amount: parsedTransaction.amount,
233-
},
234-
],
235+
inputs: [inputEntry],
236+
outputs: [outputEntry],
235237
};
236238
}
237239

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ export const signedSendTransactionForMemoId = {
129129
},
130130
};
131131

132+
export const signedSendTransactionWithNumericMemo = {
133+
tx: 'te6cckECGAEAA7wAAuGIADZN0H0n1tz6xkYgWqJSRmkURKYajjEgXeawBo9cifPIGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmpoxdJlgLSAAAAAAADgEXAgE0AhYBFP8A9KQT9LzyyAsDAgEgBBECAUgFCALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/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+wAACvQAye1UAFEAAAAAKamjF8DDudwJkyEh7jUbJEjFCjriVxsSlRJFyF872V1eegb4QACCQgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr6A613oAAAAAAAAAAAAAAAAAAAAAAAA1NzI4MjY1MDkaHeZ4',
134+
recipient: {
135+
address: 'EQA0i8-CdGnF_DhUHHf92R1ONH6sIA9vLZ_WLcCIhfBBXwtG',
136+
amount: '123400000',
137+
},
138+
memoId: '572826509',
139+
sender: 'EQAbJug-k-tufWMjEC1RKSM0iiJTDUcYkC7zWANHrkT55Fol',
140+
};
141+
132142
export const signedSingleNominatorWithdrawTransaction = {
133143
tx: 'te6cckECGAEAA8MAAuGIADZN0H0n1tz6xkYgWqJSRmkURKYajjEgXeawBo9cifPIGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmpoxdJlgLSAAAAAAADgEXAgE0AhYBFP8A9KQT9LzyyAsDAgEgBBECAUgFCALm0AHQ0wMhcbCSXwTgItdJwSCSXwTgAtMfIYIQcGx1Z70ighBkc3RyvbCSXwXgA/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+wAACvQAye1UAFEAAAAAKamjF8DDudwJkyEh7jUbJEjFCjriVxsSlRJFyF872V1eegb4QACPQgAaRefBOjTi/hwqDjv+7I6nGj9WEAe3ls/rFuBEQvggr6A613oAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAHA0/PoUC5EIEyWuPg==',
134144
txBounceable:

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,30 @@ describe('TON:', function () {
395395
stub.restore();
396396
});
397397
});
398+
399+
it('should parse a TON transfer transaction with numeric memo (memoId)', async function () {
400+
const tx = testData.signedSendTransactionWithNumericMemo;
401+
const parsedTransaction = await basecoin.parseTransaction({
402+
txHex: Buffer.from(tx.tx, 'base64').toString('hex'),
403+
});
404+
405+
parsedTransaction.should.deepEqual({
406+
inputs: [
407+
{
408+
address: tx.sender,
409+
amount: tx.recipient.amount,
410+
memo: tx.memoId,
411+
},
412+
],
413+
outputs: [
414+
{
415+
address: tx.recipient.address,
416+
amount: tx.recipient.amount,
417+
memo: tx.memoId,
418+
},
419+
],
420+
});
421+
});
398422
});
399423

400424
describe('Address Validation', () => {

0 commit comments

Comments
 (0)