diff --git a/modules/sdk-coin-trx/src/trxToken.ts b/modules/sdk-coin-trx/src/trxToken.ts index 75bfa659cc..e0c1cd537f 100644 --- a/modules/sdk-coin-trx/src/trxToken.ts +++ b/modules/sdk-coin-trx/src/trxToken.ts @@ -162,9 +162,10 @@ export class TrxToken extends Trx { const txBuilder = getBuilder(this.getChain()).from(rawTx); const tx = await txBuilder.build(); - const recipients = txParams.recipients || (txPrebuild.txInfo as TronTxInfo).recipients; - if (!recipients) { - throw new Error('missing required property recipients'); + const recipients = txParams.recipients || (txPrebuild.txInfo as TronTxInfo | undefined)?.recipients; + if (!recipients || recipients.length === 0) { + // No recipients — server-determined transfer (e.g. consolidation); structural check above is sufficient. + return true; } if (recipients[0].address === tx.outputs[0].address && recipients[0].amount === tx.outputs[0].value) { diff --git a/modules/sdk-coin-trx/test/unit/trxToken.ts b/modules/sdk-coin-trx/test/unit/trxToken.ts index 7238289ef1..a706685091 100644 --- a/modules/sdk-coin-trx/test/unit/trxToken.ts +++ b/modules/sdk-coin-trx/test/unit/trxToken.ts @@ -127,19 +127,37 @@ describe('TrxToken verifyTransaction:', function () { }); describe('non-TSS wallet — builder-based validation (existing path)', () => { + // Full JSON tx format that the builder understands. + const NON_TSS_TX_HEX = + '{"raw_data":{"contractType":2,"contract":[{"parameter":{"value":{"data":"a9059cbb0000000000000000000000008483618ca85c35a9b923d98bebca718f5a1db2790000000000000000000000000000000000000000000000000000000005f5e100","owner_address":"41c51fbeea78910b15b1d3e8a9b62914ca94d1a4ac","contract_address":"4142a1e39aefa49290f2b3f9ed688d7cecf86cd6e0"},"type_url":"type.googleapis.com/protocol.TriggerSmartContract"},"type":"TriggerSmartContract"}],"expiration":1674581767432,"timestamp":1674578167432,"ref_block_bytes":"578b","ref_block_hash":"6113bb9ac351432b","fee_limit":15000000},"raw_data_hex":"0a02578b22086113bb9ac351432b4088eae7a6de305aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541c51fbeea78910b15b1d3e8a9b62914ca94d1a4ac12154142a1e39aefa49290f2b3f9ed688d7cecf86cd6e02244a9059cbb0000000000000000000000008483618ca85c35a9b923d98bebca718f5a1db2790000000000000000000000000000000000000000000000000000000005f5e10070888d8ca5de309001c0c39307","txID":"fe21c49f4febd9089125e3a006943c145721d8fcb7ab84136f8c6663ff92f8ed","signature":["0775cde302689eb8293883c66a89b31e80d608bfc3ad3c283b64a490ea4cc712c55a2fd2e62c75843dd7e77d8c4cb52e0f371fbb29b332c259f8cb63c2e6195301"]}'; + it('should validate a correct non-TSS TRC20 transfer using txBuilder', async function () { - // The non-TSS path uses getBuilder().from(rawTx).build() and checks tx.outputs[0] - // This test uses the full JSON tx format that the builder understands. - const txHex = - '{"raw_data":{"contractType":2,"contract":[{"parameter":{"value":{"data":"a9059cbb0000000000000000000000008483618ca85c35a9b923d98bebca718f5a1db2790000000000000000000000000000000000000000000000000000000005f5e100","owner_address":"41c51fbeea78910b15b1d3e8a9b62914ca94d1a4ac","contract_address":"4142a1e39aefa49290f2b3f9ed688d7cecf86cd6e0"},"type_url":"type.googleapis.com/protocol.TriggerSmartContract"},"type":"TriggerSmartContract"}],"expiration":1674581767432,"timestamp":1674578167432,"ref_block_bytes":"578b","ref_block_hash":"6113bb9ac351432b","fee_limit":15000000},"raw_data_hex":"0a02578b22086113bb9ac351432b4088eae7a6de305aae01081f12a9010a31747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e54726967676572536d617274436f6e747261637412740a1541c51fbeea78910b15b1d3e8a9b62914ca94d1a4ac12154142a1e39aefa49290f2b3f9ed688d7cecf86cd6e02244a9059cbb0000000000000000000000008483618ca85c35a9b923d98bebca718f5a1db2790000000000000000000000000000000000000000000000000000000005f5e10070888d8ca5de309001c0c39307","txID":"fe21c49f4febd9089125e3a006943c145721d8fcb7ab84136f8c6663ff92f8ed","signature":["0775cde302689eb8293883c66a89b31e80d608bfc3ad3c283b64a490ea4cc712c55a2fd2e62c75843dd7e77d8c4cb52e0f371fbb29b332c259f8cb63c2e6195301"]}'; const recipientBase58 = Utils.getBase58AddressFromHex(TRC20_RECIPIENT_HEX); const result = await tokenCoin.verifyTransaction({ - txPrebuild: { txHex }, + txPrebuild: { txHex: NON_TSS_TX_HEX }, txParams: { recipients: [{ address: recipientBase58, amount: TRC20_AMOUNT }] }, } as any); assert.strictEqual(result, true); }); + + it('should return true when recipients is empty array (consolidation path)', async function () { + const result = await tokenCoin.verifyTransaction({ + txPrebuild: { txHex: NON_TSS_TX_HEX }, + txParams: { recipients: [] }, + } as any); + + assert.strictEqual(result, true); + }); + + it('should return true when recipients is absent (consolidation path via txInfo)', async function () { + const result = await tokenCoin.verifyTransaction({ + txPrebuild: { txHex: NON_TSS_TX_HEX, txInfo: {} }, + txParams: {}, + } as any); + + assert.strictEqual(result, true); + }); }); });