Skip to content

feat: Derive fiat asset from feature flags before hardcoded fallback#8631

Open
OGPoyraz wants to merge 2 commits intomainfrom
ogp/fiat-ff-implementation
Open

feat: Derive fiat asset from feature flags before hardcoded fallback#8631
OGPoyraz wants to merge 2 commits intomainfrom
ogp/fiat-ff-implementation

Conversation

@OGPoyraz
Copy link
Copy Markdown
Member

@OGPoyraz OGPoyraz commented Apr 29, 2026

Explanation

Currently deriveFiatAssetForFiatPayment resolves the fiat asset for a payment entirely from a hardcoded map (FIAT_ASSET_ID_BY_TX_TYPE). This makes it impossible to adjust asset mappings without a code release.

This PR introduces a 3-tier resolution for fiat assets:

  1. Feature flag - reads from confirmations_pay_fiat.assetPerTransactionType[txType] via RemoteFeatureFlagController
  2. Hardcoded map - falls back to the existing FIAT_ASSET_ID_BY_TX_TYPE constant
  3. ETH on mainnet - terminal fallback when neither source has an entry

The function signature gains a messenger parameter (already available at the call site in fiat-quotes.ts), and the return type tightens from TransactionPayFiatAsset | undefined to TransactionPayFiatAsset since the ETH mainnet fallback guarantees a value.

References

  • Feature flag key: confirmations_pay_fiat

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Changes fiat-quote asset selection to be feature-flag driven and to always fall back to mainnet ETH, which can alter quote inputs and pricing behavior for unsupported transaction types.

Overview
Enables feature-flag-driven fiat asset resolution for MM Pay fiat quotes: deriveFiatAssetForFiatPayment now consults confirmations_pay_fiat.assetPerTransactionType (via new getFiatAssetPerTransactionType) before falling back to the existing hardcoded map, and finally to an ETH-on-mainnet default.

Updates the fiat quote path to pass a messenger into fiat-asset derivation and to compute raw source amounts using getTokenInfo().decimals (with early return when token info/rates are unavailable). Adds buildCaipAssetType utility for generating CAIP-19 asset identifiers, and expands tests/changelog accordingly.

Reviewed by Cursor Bugbot for commit 0c647d8. Bugbot is set up for automated code reviews on this repo. Configure here.

@OGPoyraz OGPoyraz requested review from a team as code owners April 29, 2026 10:47
@OGPoyraz OGPoyraz force-pushed the ogp/fiat-ff-implementation branch from 5425439 to b24eac7 Compare April 29, 2026 10:48
@OGPoyraz OGPoyraz changed the title feat: read fiat asset from feature flags before hardcoded fallback feat: Derive fiat asset from feature flags before hardcoded fallback Apr 29, 2026
@OGPoyraz OGPoyraz force-pushed the ogp/fiat-ff-implementation branch from b24eac7 to 7ef80a6 Compare April 29, 2026 10:51
import type { TransactionPayFiatAsset } from './constants';
import { ETH_MAINNET_FIAT_ASSET, FIAT_ASSET_ID_BY_TX_TYPE } from './constants';

function resolveTransactionType(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, exported functions at top of file for readability?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

transaction: TransactionMeta,
): TransactionType | undefined {
if (transaction.type === TransactionType.batch) {
return transaction.nestedTransactions?.[0]?.type;
Copy link
Copy Markdown
Member

@matthewwalsh0 matthewwalsh0 May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to assume this is always the first call?

Don't we iterate over calls looking for a supported type in Relay strategy?

In Predict for example, if first deposit, the initial call is the deploy for Safe proxy?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, added a similar utility

const firstMatchingType = transaction.nestedTransactions?.[0]?.type;
if (firstMatchingType) {
return FIAT_ASSET_ID_BY_TX_TYPE[firstMatchingType];
const hardcodedAsset = FIAT_ASSET_ID_BY_TX_TYPE[txType];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent of the feature-flag utils is to abstract these defaults also, so most of the existing functions fallback to defaults.

Can we do that here and encapsulate the constant and mainnet fallbacks in there?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, carried over the fallbacks info FF utility

transactionType: TransactionType,
): TransactionPayFiatAsset | undefined {
const state = messenger.call('RemoteFeatureFlagController:getState');
const fiatFlags = state.remoteFeatureFlags?.confirmations_pay_fiat as
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we define a local FiatFlags type at the top of the file for re-use and readability?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0c647d8. Configure here.


return transaction.nestedTransactions?.find(
(tx) => tx.type && FIAT_ASSET_ID_BY_TX_TYPE[tx.type] !== undefined,
)?.type;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Batch transactions ignore feature-flag-only transaction types

Medium Severity

resolveTransactionType filters nested batch transactions using only FIAT_ASSET_ID_BY_TX_TYPE, so any transaction type defined exclusively via the confirmations_pay_fiat feature flag (and not in the hardcoded map) will never be matched inside a batch. The .find() predicate skips that nested type, returns undefined, and getFiatAssetPerTransactionType falls through to ETH_MAINNET_FIAT_ASSET — silently ignoring the feature flag entry. This defeats the PR's core goal of enabling asset mappings without a code release for batch transactions.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0c647d8. Configure here.

'erc20',
tokenAddress,
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported buildCaipAssetType has no production callers

Low Severity

buildCaipAssetType is exported and tested but has zero production callers anywhere in the codebase. The caipAssetId field was removed from TransactionPayFiatAsset in this PR, but nothing calls this new utility to compute it dynamically. This is dead code that adds maintenance burden without providing value.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0c647d8. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants