From 0c6c2cdaaec71bc6f62b3eaf416cefdb65937093 Mon Sep 17 00:00:00 2001 From: Himanshu Singroha Date: Fri, 17 Oct 2025 16:04:24 +0530 Subject: [PATCH] fix: return recipients in unsigned tranasction Ticket: COIN-6108 TICKET: COIN-6108 --- .../src/abstractEthLikeNewCoins.ts | 38 +++++++++++++++++-- .../sdk-coin-ethlike/test/unit/ethlikeCoin.ts | 31 +++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts index b885331f39..9c80a6d582 100644 --- a/modules/abstract-eth/src/abstractEthLikeNewCoins.ts +++ b/modules/abstract-eth/src/abstractEthLikeNewCoins.ts @@ -67,6 +67,7 @@ import { AbstractEthLikeCoin } from './abstractEthLikeCoin'; import { EthLikeToken } from './ethLikeToken'; import { calculateForwarderV1Address, + decodeTransferData, ERC1155TransferBuilder, ERC721TransferBuilder, getBufferedByteCode, @@ -1570,6 +1571,30 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { }; } + /** + * Extract recipients from transaction hex + * @param txHex - The transaction hex string + * @returns Array of recipients with address and amount + */ + private async extractRecipientsFromTxHex(txHex: string): Promise> { + const txBuffer = optionalDeps.ethUtil.toBuffer(txHex); + const decodedTx = optionalDeps.EthTx.TransactionFactory.fromSerializedData(txBuffer); + const recipients: Array<{ address: string; amount: string }> = []; + + if (decodedTx.data && decodedTx.data.length > 0) { + const dataHex = optionalDeps.ethUtil.bufferToHex(decodedTx.data); + const transferData = decodeTransferData(dataHex); + if (transferData.to) { + recipients.push({ + address: transferData.to, + amount: transferData.amount, + }); + } + } + + return recipients; + } + async sendCrossChainRecoveryTransaction( params: SendCrossChainRecoveryOptions ): Promise<{ coin: string; txHex?: string; txid: string }> { @@ -1617,15 +1642,22 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin { }; } - async buildCrossChainRecoveryTransaction( - recoveryId: string - ): Promise<{ coin: string; txHex: string; txid: string; walletVersion?: number }> { + async buildCrossChainRecoveryTransaction(recoveryId: string): Promise<{ + coin: string; + txHex: string; + txid: string; + walletVersion?: number; + recipients: Array<{ address: string; amount: string }>; + }> { const res = await this.bitgo.get(this.bitgo.microservicesUrl(`/api/recovery/v1/crosschain/${recoveryId}/buildtx`)); + // Extract recipients from the transaction hex + const recipients = await this.extractRecipientsFromTxHex(res.body.txHex); return { coin: res.body.coin, txHex: res.body.txHex, txid: res.body.txid, walletVersion: res.body.walletVersion, + recipients, }; } diff --git a/modules/sdk-coin-ethlike/test/unit/ethlikeCoin.ts b/modules/sdk-coin-ethlike/test/unit/ethlikeCoin.ts index 21549e9324..f2d2f11e11 100644 --- a/modules/sdk-coin-ethlike/test/unit/ethlikeCoin.ts +++ b/modules/sdk-coin-ethlike/test/unit/ethlikeCoin.ts @@ -94,6 +94,37 @@ describe('EthLike coin tests', function () { result.txHex.should.equal(mockData.ccr[coin.name].txHex); }); + it('should build cross chain recovery transaction and extract recipients', async function () { + const recoveryId = '0x1234567890abcdef'; + const mockResponse = { + coin: coin.name, + txHex: mockData.ccr[coin.name].txHex, + txid: mockData.ccr[coin.name].txid, + walletVersion: 1, + }; + + nock(bitgo.microservicesUrl(`/api/recovery/v1/crosschain`)) + .get(`/${recoveryId}/buildtx`) + .reply(200, mockResponse); + + const result = await basecoin.buildCrossChainRecoveryTransaction(recoveryId); + + result.should.have.property('coin'); + result.coin.should.equal(coin.name); + result.should.have.property('txHex'); + result.txHex.should.equal(mockData.ccr[coin.name].txHex); + result.should.have.property('txid'); + result.txid.should.equal(mockData.ccr[coin.name].txid); + result.should.have.property('walletVersion'); + result.should.have.property('recipients'); + result.recipients.should.be.an.Array(); + const recipient = result.recipients[0]; + recipient.should.have.property('address'); + recipient.should.have.property('amount'); + recipient.address.should.be.a.String(); + recipient.amount.should.be.a.String(); + }); + it('should generate signature data for custodial hot wallet and sign using hsm signature', async function () { const baseAddress = '0x702cf81e03aa310ec9481d814e3d04a20b04b505'; const destinationAddress = '0xb9f62c71d5f6949cfb211a67fb13ccf079cc760b';