diff --git a/modules/sdk-coin-flrp/src/flrp.ts b/modules/sdk-coin-flrp/src/flrp.ts index e25ff59553..951f4a03f3 100644 --- a/modules/sdk-coin-flrp/src/flrp.ts +++ b/modules/sdk-coin-flrp/src/flrp.ts @@ -96,9 +96,15 @@ export class Flrp extends BaseCoin { const explainedTx = tx.explainTransaction(); const type = params.txParams.type; - // 'stake' is the intent-type alias for AddPermissionlessDelegator; normalize it - // so the TransactionType enum lookup succeeds. - const normalizedType = type === 'stake' ? 'AddPermissionlessDelegator' : type; + // WP intentType aliases (lowercase) must map to TransactionType enum keys. + const normalizedType = + type === 'stake' + ? 'AddPermissionlessDelegator' + : type === 'import' + ? 'Import' + : type === 'importtoc' + ? 'ImportToC' + : type; // When type is provided, verify it matches the parsed transaction. // When type is not provided (MPC/TSS intent flow where buildParams are unavailable), diff --git a/modules/sdk-coin-flrp/test/unit/flrp.ts b/modules/sdk-coin-flrp/test/unit/flrp.ts index 9ecfae0d65..6bd35b35be 100644 --- a/modules/sdk-coin-flrp/test/unit/flrp.ts +++ b/modules/sdk-coin-flrp/test/unit/flrp.ts @@ -940,6 +940,19 @@ describe('Flrp test cases', function () { isVerified.should.equal(true); }); + it('should verify MPC ImportInP transaction when txParams.type is the "import" intent alias', async () => { + const txHex = await buildUnsignedImportInP(); + const txPrebuild = { txHex, txInfo: {} }; + const txParams = { + recipients: [], + type: 'import', + locktime: 0, + }; + + const isVerified = await basecoin.verifyTransaction({ txParams, txPrebuild }); + isVerified.should.equal(true); + }); + it('should verify MPC ExportInP transaction', async () => { const txHex = await buildUnsignedExportInP(); const txPrebuild = { txHex, txInfo: {} }; diff --git a/modules/sdk-core/src/bitgo/utils/tss/recipientUtils.ts b/modules/sdk-core/src/bitgo/utils/tss/recipientUtils.ts index 226797f319..9ff3f6cf55 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/recipientUtils.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/recipientUtils.ts @@ -117,7 +117,16 @@ export function resolveEffectiveTxParams( // Use its presence as a generic staking signal — no need to enumerate every intentType. const isStakingIntent = !!(txRequest.intent as PopulatedIntent)?.stakingRequestId; - if (!effectiveTxParams.recipients?.length && !isStakingIntent && !NO_RECIPIENT_TX_TYPES.has(txType)) { + // buildParams.type from the wallet UI is PascalCase ('Import'); intent.intentType from WP + // is lowercase ('import'). Either may be the source of truth depending on the signing path. + const intentType = (txRequest.intent as PopulatedIntent)?.intentType ?? ''; + + if ( + !effectiveTxParams.recipients?.length && + !isStakingIntent && + !NO_RECIPIENT_TX_TYPES.has(txType) && + !NO_RECIPIENT_TX_TYPES.has(intentType) + ) { throw new InvalidTransactionError( 'Recipient details are required to verify this transaction before signing. Pass txParams with at least one recipient.' ); diff --git a/modules/sdk-core/test/unit/bitgo/utils/tss/recipientUtils.ts b/modules/sdk-core/test/unit/bitgo/utils/tss/recipientUtils.ts index 68ed006d4e..9e668cfafe 100644 --- a/modules/sdk-core/test/unit/bitgo/utils/tss/recipientUtils.ts +++ b/modules/sdk-core/test/unit/bitgo/utils/tss/recipientUtils.ts @@ -126,6 +126,13 @@ describe('recipientUtils', function () { } }); + it('does not throw when buildParams.type is PascalCase but intent.intentType is lowercase', function () { + // signTransactionTss passes txPrebuild.buildParams as txParams. Prebuild uses + // type: 'Import' while WP stores intentType: 'import' on the txRequest. + const txRequest = makeTxRequest({ intent: { intentType: 'import', recipients: [] } as any }); + assert.doesNotThrow(() => resolveEffectiveTxParams(txRequest, { type: 'Import', recipients: [] })); + }); + it('throws InvalidTransactionError for unknown types with no recipients', function () { const txRequest = makeTxRequest(); assert.throws(