From edb5547f235a8b32800f7fe1a41537260b3bc090 Mon Sep 17 00:00:00 2001 From: Sreeraj S Date: Fri, 5 Jun 2026 10:33:56 +0000 Subject: [PATCH] feat(sdk-core): add execMode support to txrequests API Add optional execMode parameter to prebuildTxWithIntent and the wallet prebuildTransaction flow. When set to 'EXEC_UNSPECIFIED', the server defers transaction execution, giving Figure Markets exchange control over the workflow. Defaults to 'EXEC_TRY' when omitted (existing behavior is unchanged). The field is passed as a top-level body param alongside intent, apiVersion, and preview in the POST /wallet/{id}/txrequests call. Ticket: CSHLD-972 Session-Id: caa99360-6177-4227-8aba-5043f8cb453f Task-Id: 49710c31-924d-41fb-b44b-2dc4a10bce34 --- .../test/v2/unit/internal/tssUtils/eddsa.ts | 76 +++++++++++++++++++ .../src/bitgo/utils/tss/baseTSSUtils.ts | 1 + .../sdk-core/src/bitgo/utils/tss/baseTypes.ts | 7 ++ .../sdk-core/src/bitgo/wallet/BuildParams.ts | 1 + modules/sdk-core/src/bitgo/wallet/iWallet.ts | 5 ++ modules/sdk-core/src/bitgo/wallet/wallet.ts | 17 +++++ 6 files changed, 107 insertions(+) diff --git a/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts b/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts index d3dea3383b..23451528b0 100644 --- a/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts +++ b/modules/bitgo/test/v2/unit/internal/tssUtils/eddsa.ts @@ -1029,6 +1029,82 @@ describe('TSS Utils:', async function () { nockedCreateTx.isDone().should.be.true(); }); + + it('should include execMode in request body when specified', async function () { + const nockedCreateTx = await nockCreateTxRequest({ + walletId: wallet.id(), + requestBody: { + apiVersion: 'lite', + execMode: 'EXEC_UNSPECIFIED', + intent: { + intentType: 'payment', + recipients: [ + { + address: { + address: 'recipient', + }, + amount: { + value: '10000', + symbol: 'tsol', + }, + }, + ], + }, + }, + response: {}, + }); + + await tssUtils.prebuildTxWithIntent({ + reqId, + recipients: [ + { + address: 'recipient', + amount: '10000', + }, + ], + intentType: 'payment', + execMode: 'EXEC_UNSPECIFIED', + }); + + nockedCreateTx.isDone().should.be.true(); + }); + + it('should not include execMode in request body when not specified', async function () { + const nockedCreateTx = await nockCreateTxRequest({ + walletId: wallet.id(), + requestBody: { + apiVersion: 'lite', + intent: { + intentType: 'payment', + recipients: [ + { + address: { + address: 'recipient', + }, + amount: { + value: '10000', + symbol: 'tsol', + }, + }, + ], + }, + }, + response: {}, + }); + + await tssUtils.prebuildTxWithIntent({ + reqId, + recipients: [ + { + address: 'recipient', + amount: '10000', + }, + ], + intentType: 'payment', + }); + + nockedCreateTx.isDone().should.be.true(); + }); }); describe('delete SignatureShare:', async function () { diff --git a/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts b/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts index 1d86001223..261f6405c5 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts @@ -386,6 +386,7 @@ export default class BaseTssUtils extends MpcUtils implements ITssUtil }, apiVersion: apiVersion, preview, + ...(params.execMode !== undefined && { execMode: params.execMode }), }; const reqTracer = params.reqId || new RequestTracer(); diff --git a/modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts b/modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts index 19682197f4..4b83e6ade6 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts @@ -12,6 +12,7 @@ import { AShare, DShare, SShare } from '../../tss/ecdsa/types'; import { MessageStandardType } from '../messageTypes'; export type TxRequestVersion = 'full' | 'lite'; +export type TxRequestExecMode = 'EXEC_TRY' | 'EXEC_UNSPECIFIED'; export interface HopParams { paymentId?: string; userReqSig?: string; @@ -354,6 +355,12 @@ export interface PrebuildTransactionWithIntentOptions extends IntentOptionsBase feeToken?: string; /** Canton-specific params for the cantonCommand intent. */ cantonCommandParams?: CantonCommandParams; + /** + * Controls transaction execution mode on the server side. + * Use 'EXEC_UNSPECIFIED' to defer execution (e.g. Figure Markets exchange flow). + * Defaults to 'EXEC_TRY' when omitted. + */ + execMode?: TxRequestExecMode; } export interface IntentRecipient { address: { diff --git a/modules/sdk-core/src/bitgo/wallet/BuildParams.ts b/modules/sdk-core/src/bitgo/wallet/BuildParams.ts index a635b2057b..1427db7152 100644 --- a/modules/sdk-core/src/bitgo/wallet/BuildParams.ts +++ b/modules/sdk-core/src/bitgo/wallet/BuildParams.ts @@ -135,6 +135,7 @@ export const BuildParams = t.exact( feeToken: t.unknown, // Bridging parameters for cross-chain operations (e.g., BTC to sBTC) bridgingParams: t.unknown, + execMode: t.unknown, }), ]) ); diff --git a/modules/sdk-core/src/bitgo/wallet/iWallet.ts b/modules/sdk-core/src/bitgo/wallet/iWallet.ts index 9583cee260..68897efb24 100644 --- a/modules/sdk-core/src/bitgo/wallet/iWallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/iWallet.ts @@ -296,6 +296,11 @@ export interface PrebuildTransactionOptions { * Parameters for executing DAML commands on Canton. */ cantonCommandParams?: CantonCommandParams; + /** + * Controls transaction execution mode. Use 'EXEC_UNSPECIFIED' to defer + * execution (e.g. Figure Markets exchange flow). Defaults to 'EXEC_TRY'. + */ + execMode?: 'EXEC_TRY' | 'EXEC_UNSPECIFIED'; } export interface PrebuildAndSignTransactionOptions extends PrebuildTransactionOptions, WalletSignTransactionOptions { diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index 80b0133f8f..58550b7589 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -4153,6 +4153,7 @@ export class Wallet implements IWallet { unspents: params.unspents, senderAddress: params.senderAddress, isTestTransaction: params.isTestTransaction, + execMode: params.execMode, }, apiVersion, params.preview @@ -4170,6 +4171,7 @@ export class Wallet implements IWallet { feeToken: params.feeToken, unspents: params.unspents, sequenceId: params.sequenceId, + execMode: params.execMode, }, apiVersion, params.preview @@ -4183,6 +4185,7 @@ export class Wallet implements IWallet { recipients: params.recipients || [], enableTokens: params.enableTokens, memo: params.memo, + execMode: params.execMode, }, apiVersion, params.preview @@ -4195,6 +4198,7 @@ export class Wallet implements IWallet { intentType: 'closeAssociatedTokenAccount', recipients: params.recipients || [], memo: params.memo, + execMode: params.execMode, }, apiVersion, params.preview @@ -4210,6 +4214,7 @@ export class Wallet implements IWallet { receiveAddress: params.receiveAddress, feeOptions, feeToken: params.feeToken, + execMode: params.execMode, }, apiVersion, params.preview @@ -4225,6 +4230,7 @@ export class Wallet implements IWallet { receiveAddress: params.receiveAddress, feeOptions, feeToken: params.feeToken, + execMode: params.execMode, }, apiVersion, params.preview @@ -4237,6 +4243,7 @@ export class Wallet implements IWallet { intentType: 'tokenApproval', tokenName: params.tokenName, feeToken: params.feeToken, + execMode: params.execMode, }, apiVersion, params.preview @@ -4248,6 +4255,7 @@ export class Wallet implements IWallet { reqId, intentType: 'createAccount', recipients: params.recipients || [], + execMode: params.execMode, }, apiVersion, params.preview @@ -4260,6 +4268,7 @@ export class Wallet implements IWallet { intentType: 'transferAccept', txRequestId: params.txRequestId, sequenceId: params.txRequestId, + execMode: params.execMode, }, apiVersion, params.preview @@ -4273,6 +4282,7 @@ export class Wallet implements IWallet { intentType: 'transferReject', txRequestId: params.txRequestId, sequenceId: params.txRequestId, + execMode: params.execMode, }, apiVersion, params.preview @@ -4286,6 +4296,7 @@ export class Wallet implements IWallet { intentType: 'transferOfferWithdrawn', transferOfferId: params.transferOfferId, sequenceId: params.transferOfferId, + execMode: params.execMode, }, apiVersion, params.preview @@ -4303,6 +4314,7 @@ export class Wallet implements IWallet { cantonCommandParams: params.cantonCommandParams, sequenceId: params.sequenceId, comment: params.comment, + execMode: params.execMode, }, apiVersion, params.preview @@ -4323,6 +4335,7 @@ export class Wallet implements IWallet { nonce: params.nonce, memo: params.memo, feeOptions, + execMode: params.execMode, }, apiVersion, params.preview @@ -4339,6 +4352,7 @@ export class Wallet implements IWallet { nonce: params.nonce, feeOptions, feeToken: params.feeToken, + execMode: params.execMode, }, apiVersion, params.preview @@ -4355,6 +4369,7 @@ export class Wallet implements IWallet { nonce: params.nonce, feeOptions, feeToken: params.feeToken, + execMode: params.execMode, }, apiVersion, params.preview @@ -4368,6 +4383,7 @@ export class Wallet implements IWallet { sequenceId: params.sequenceId, comment: params.comment, recipients: params.recipients || [], + execMode: params.execMode, }, apiVersion, params.preview @@ -4381,6 +4397,7 @@ export class Wallet implements IWallet { sequenceId: params.sequenceId, comment: params.comment, recipients: params.recipients || [], + execMode: params.execMode, }, apiVersion, params.preview