Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions modules/sdk-coin-flrp/src/flrp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
13 changes: 13 additions & 0 deletions modules/sdk-coin-flrp/test/unit/flrp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {} };
Expand Down
11 changes: 10 additions & 1 deletion modules/sdk-core/src/bitgo/utils/tss/recipientUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading