diff --git a/modules/sdk-coin-flrp/src/flrp.ts b/modules/sdk-coin-flrp/src/flrp.ts index 4fc79fc75f..9d2c132c76 100644 --- a/modules/sdk-coin-flrp/src/flrp.ts +++ b/modules/sdk-coin-flrp/src/flrp.ts @@ -96,7 +96,11 @@ export class Flrp extends BaseCoin { // so the TransactionType enum lookup succeeds. const normalizedType = type === 'stake' ? 'AddPermissionlessDelegator' : type; - if (!normalizedType || (normalizedType !== 'ImportToC' && explainedTx.type !== TransactionType[normalizedType])) { + // When type is provided, verify it matches the parsed transaction. + // When type is not provided (MPC/TSS intent flow where buildParams are unavailable), + // skip the type match check and rely on explainedTx.type from the parsed hex — + // the transaction was built by wallet-platform and the hex is the source of truth. + if (normalizedType && normalizedType !== 'ImportToC' && explainedTx.type !== TransactionType[normalizedType]) { throw new Error('Tx type does not match with expected txParams type'); } @@ -126,8 +130,9 @@ export class Flrp extends BaseCoin { } break; case TransactionType.AddPermissionlessDelegator: - // Validate delegation transaction against both txParams and explainedTx - this.validateDelegationTx(params.txParams, explainedTx); + if (params.txParams.stakingOptions) { + this.validateDelegationTx(params.txParams, explainedTx); + } break; default: throw new Error('Tx type is not supported yet'); diff --git a/modules/sdk-coin-flrp/test/resources/transactionData/multisigDelegationTx.ts b/modules/sdk-coin-flrp/test/resources/transactionData/multisigDelegationTx.ts index 8ae01e17fb..83d1f87b46 100644 --- a/modules/sdk-coin-flrp/test/resources/transactionData/multisigDelegationTx.ts +++ b/modules/sdk-coin-flrp/test/resources/transactionData/multisigDelegationTx.ts @@ -98,3 +98,12 @@ export const MULTISIG_ADDRESS_SORTING = { { role: 'bitgo', originalIndex: 1, sortedIndex: 1 }, ], }; + +/** + * MPC/TSS delegation transaction hex (single signer, built by wallet-platform via buildTransactionWithIntent). + * From txRequest b5f89e8b-2273-4e29-80bd-60bd488e172f on Coston2 testnet. + * This is the serializedTxHex that verifyTransaction receives during MPC signing, + * where txParams has no type and no stakingOptions (only { recipients: [] }). + */ +export const MPC_DELEGATION_UNSIGNED_TX_HEX = + '00000000001a0000007200000000000000000000000000000000000000000000000000000000000000000000000158734f94af871c3d131b56131b6fb7a0291eacadd261e69dfb42a9cdf6f7fddd000000070000000253e0efe80000000000000000000000010000000184fa441f981fce7cd63189d5071802376ad6676e000000022ebd7484b5704be67db0344abd9d2223d5c7100795f68c7a6a4a77888dccb9820000000158734f94af871c3d131b56131b6fb7a0291eacadd261e69dfb42a9cdf6f7fddd0000000500002d79883d20000000000100000000933f844e2d35c52963abd7912ec39faa71ed5994a52c584d9bf1f6bd480c976c0000000058734f94af871c3d131b56131b6fb7a0291eacadd261e69dfb42a9cdf6f7fddd0000000500000002540be400000000010000000000000000664b4924a25af8be5f07052b2c2e582f7c10a654000000006a1192a4000000006a2407a400002d79883d200000000000000000000000000000000000000000000000000000000000000000000000000158734f94af871c3d131b56131b6fb7a0291eacadd261e69dfb42a9cdf6f7fddd0000000700002d79883d20000000000000000000000000010000000184fa441f981fce7cd63189d5071802376ad6676e0000000b00000000000000000000000100000001243f65fbcc8458492e09d7693c3966a734c0a30f00000002000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fa441f981fce7cd63189d5071802376ad6676e000000090000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084fa441f981fce7cd63189d5071802376ad6676ee5e72aa5'; diff --git a/modules/sdk-coin-flrp/test/unit/flrp.ts b/modules/sdk-coin-flrp/test/unit/flrp.ts index 96956c4259..dbb5f4df84 100644 --- a/modules/sdk-coin-flrp/test/unit/flrp.ts +++ b/modules/sdk-coin-flrp/test/unit/flrp.ts @@ -12,6 +12,7 @@ import { IMPORT_IN_C } from '../resources/transactionData/importInC'; import { MULTISIG_DELEGATION_FULLY_SIGNED_TX_HEX, MULTISIG_DELEGATION_PARAMS, + MPC_DELEGATION_UNSIGNED_TX_HEX, } from '../resources/transactionData/multisigDelegationTx'; import { HalfSignedAccountTransaction, TransactionType, MPCAlgorithm } from '@bitgo/sdk-core'; import { secp256k1 } from '@flarenetwork/flarejs'; @@ -978,6 +979,22 @@ describe('Flrp test cases', function () { const isVerified = await basecoin.verifyTransaction({ txParams, txPrebuild }); isVerified.should.equal(true); }); + + it('should verify delegation transaction when txParams.type is undefined (MPC intent flow)', async () => { + // In the MPC/TSS staking flow, the txRequest is created by staking-service (not the UI). + // The UI only has txRequestId — no buildParams. The SDK falls back to { recipients: [] } + // with no type and no stakingOptions. verifyTransaction must still pass because the + // parsed hex (explainedTx.type) is the source of truth built by wallet-platform. + const txPrebuild = { txHex: MPC_DELEGATION_UNSIGNED_TX_HEX, txInfo: {} }; + const txParams = { + recipients: [], + // no type — simulates MPC staking flow where buildParams is unavailable + // no stakingOptions — not passed through in the MPC intent flow + }; + + const isVerified = await basecoin.verifyTransaction({ txParams, txPrebuild }); + isVerified.should.equal(true); + }); }); describe('verifyTransaction with TSS wallet (Avalanche atomic)', () => {