From 8f8e439692b5ba8f8285d2dda5f9a220693686a6 Mon Sep 17 00:00:00 2001 From: Vibhav Simha G Date: Fri, 29 May 2026 11:12:28 +0530 Subject: [PATCH] fix(sdk-core): preserve tss build params after presign TSS signing relies on txPrebuild.buildParams to carry SDK transaction intent into signing. Some presign hooks replace txPrebuild and can drop that metadata before TSS verification. - Restore buildParams after TSS presign when the presigned prebuild omits them. - Add a wallet unit test covering a presign hook that replaces txPrebuild. Ticket: WCI-505 --- modules/bitgo/test/v2/unit/wallet.ts | 41 +++++++++++++++++++++ modules/sdk-core/src/bitgo/wallet/wallet.ts | 13 +++++++ 2 files changed, 54 insertions(+) diff --git a/modules/bitgo/test/v2/unit/wallet.ts b/modules/bitgo/test/v2/unit/wallet.ts index 81506eb49c..6c7ec24cde 100644 --- a/modules/bitgo/test/v2/unit/wallet.ts +++ b/modules/bitgo/test/v2/unit/wallet.ts @@ -7,6 +7,7 @@ import * as sinon from 'sinon'; import '../lib/asserts'; import nock = require('nock'); import * as _ from 'lodash'; +import * as assert from 'assert'; import { BaseTssUtils, @@ -2543,6 +2544,46 @@ describe('V2 Wallet:', function () { sandbox.verifyAndRestore(); }); + it('should preserve tss txPrebuild buildParams when presign replaces the txPrebuild', async function () { + const recipients = [ + { + address: '6DadkZcx9JZgeQUDbHh12cmqCpaqehmVxv6sGy49jrah', + amount: '1000', + }, + ]; + const buildParams = { + recipients, + type: 'transfer', + }; + + sandbox.stub(tsol, 'presignTransaction').callsFake(async (presignParams) => { + return { + ...presignParams, + txPrebuild: { + txRequestId: 'id', + txHex: 'refreshed-tx-hex', + }, + }; + }); + const signTxRequest = sandbox.stub(TssUtils.prototype, 'signTxRequest').resolves(txRequest); + + await tssSolWallet.signTransaction({ + txPrebuild: { + txRequestId: 'id', + txHex: 'original-tx-hex', + buildParams, + }, + prv: 'user-prv', + }); + + signTxRequest.should.have.been.calledOnce(); + const firstSignTxRequestCall = signTxRequest.firstCall; + assert.ok(firstSignTxRequestCall); + const signTxRequestParams = firstSignTxRequestCall.args[0]; + assert.ok(signTxRequestParams); + should(signTxRequestParams.txParams).deepEqual(buildParams); + }); + describe('preBuildAndSignTransaction', async function () { const params = { walletPassphrase: 'passphrase12345', diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index c5ccbd4336..2c0ddcb2de 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -2235,6 +2235,19 @@ export class Wallet implements IWallet { tssUtils: this.tssUtils, }); + if ( + this.multisigType() === 'tss' && + this.multisigTypeVersion() === 'MPCv2' && + this.baseCoin.getMPCAlgorithm() === 'eddsa' && + typeof params.txPrebuild === 'object' && + typeof presign.txPrebuild === 'object' && + params.txPrebuild.buildParams && + !presign.txPrebuild.buildParams + ) { + // Sol presign hook refreshes txPrebuild, but buildParams is SDK-local intent metadata. + presign.txPrebuild.buildParams = params.txPrebuild.buildParams; + } + if (this.multisigType() === 'tss') { return this.signTransactionTss({ ...presign,