Skip to content

Commit 4a4f747

Browse files
committed
fix: handle serializedTxHex format in TSS verifyTransaction
TICKET: CHALO-380
1 parent 23d953b commit 4a4f747

2 files changed

Lines changed: 88 additions & 4 deletions

File tree

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,21 @@ export class Trx extends BaseCoin {
379379
}
380380

381381
if (walletType === 'tss') {
382-
// For TSS wallets, txHex is the signableHex (raw_data_hex protobuf bytes),
383-
// not a full transaction JSON. Decode it directly via protobuf.
384-
// Note: decodeTransaction already validates exactly 1 contract exists.
385-
const decodedTx = Utils.decodeTransaction(txPrebuild.txHex);
382+
// For TSS wallets, verifyTransaction is called from two places:
383+
// 1. prebuildAndSignTransaction (wallet.ts) — txHex is serializedTxHex, a full JSON string
384+
// containing { txID, raw_data, raw_data_hex }.
385+
// 2. ECDSA signing flow (ecdsa.ts) — txHex is signableHex, the raw protobuf bytes (raw_data_hex).
386+
// We need to extract the raw_data_hex in case 1 before decoding.
387+
let rawDataHex: string;
388+
try {
389+
// serializedTxHex: full JSON string — extract the raw_data_hex field
390+
rawDataHex = JSON.parse(txPrebuild.txHex).raw_data_hex;
391+
} catch {
392+
// signableHex: already raw protobuf hex (raw_data_hex)
393+
console.debug(`Could not parse txHex as JSON for coin ${this.getChain()}, using txHex directly`);
394+
rawDataHex = txPrebuild.txHex;
395+
}
396+
const decodedTx = Utils.decodeTransaction(rawDataHex);
386397

387398
// decodedTx uses a numeric enum for contract type (from protobuf decoding),
388399
// unlike the multisig path which checks the string 'TransferContract' from node JSON.

modules/sdk-coin-trx/test/unit/verifyTransaction.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,5 +574,78 @@ describe('TRON Verify Transaction:', function () {
574574
message: 'missing txHex in txPrebuild',
575575
});
576576
});
577+
578+
describe('serializedTxHex (JSON string) path', () => {
579+
// prebuildAndSignTransaction passes txHex as serializedTxHex — a full JSON string
580+
// containing { txID, raw_data, raw_data_hex }. The TSS branch must handle this format
581+
// by extracting raw_data_hex before protobuf decoding.
582+
583+
it('should validate TSS TransferContract when txHex is a JSON string (serializedTxHex)', async function () {
584+
const ownerHex = '4173a5993cd182ae152adad8203163f780c65a8aa5';
585+
const toHex = '41d6cd6a2c0ff35a319e6abb5b9503ba0278679882';
586+
const amount = 1000000;
587+
588+
const rawDataHex = buildTssTransferTxHex({ ownerAddress: ownerHex, toAddress: toHex, amount });
589+
const txID = createHash('sha256').update(Buffer.from(rawDataHex, 'hex')).digest('hex');
590+
591+
// Simulate the serializedTxHex format from BitGo API (JSON string with txID + raw_data_hex)
592+
const serializedTxHex = JSON.stringify({
593+
txID,
594+
raw_data_hex: rawDataHex,
595+
raw_data: {},
596+
});
597+
598+
const params = {
599+
txParams: {
600+
recipients: [
601+
{
602+
address: Utils.getBase58AddressFromHex(toHex),
603+
amount: amount.toString(),
604+
},
605+
],
606+
},
607+
txPrebuild: {
608+
txHex: serializedTxHex,
609+
},
610+
wallet: {},
611+
walletType: 'tss',
612+
};
613+
614+
const result = await basecoin.verifyTransaction(params);
615+
assert.strictEqual(result, true);
616+
});
617+
618+
it('should fail when amount mismatches with JSON txHex', async function () {
619+
const ownerHex = '4173a5993cd182ae152adad8203163f780c65a8aa5';
620+
const toHex = '41d6cd6a2c0ff35a319e6abb5b9503ba0278679882';
621+
622+
const rawDataHex = buildTssTransferTxHex({ ownerAddress: ownerHex, toAddress: toHex, amount: 2000000 });
623+
const serializedTxHex = JSON.stringify({
624+
txID: createHash('sha256').update(Buffer.from(rawDataHex, 'hex')).digest('hex'),
625+
raw_data_hex: rawDataHex,
626+
raw_data: {},
627+
});
628+
629+
const params = {
630+
txParams: {
631+
recipients: [
632+
{
633+
address: Utils.getBase58AddressFromHex(toHex),
634+
amount: '1000000', // mismatch
635+
},
636+
],
637+
},
638+
txPrebuild: {
639+
txHex: serializedTxHex,
640+
},
641+
wallet: {},
642+
walletType: 'tss',
643+
};
644+
645+
await assert.rejects(basecoin.verifyTransaction(params), {
646+
message: 'transaction amount in txPrebuild does not match the value given by client',
647+
});
648+
});
649+
});
577650
});
578651
});

0 commit comments

Comments
 (0)