Skip to content

feat(transaction-pay-controller): surface fiat ramps quote error as fiatPayment.quoteError#9030

Open
amitabh94 wants to merge 2 commits into
ogp/fiat-money-account-deposit-fixfrom
feat/tpc-fiat-quote-error
Open

feat(transaction-pay-controller): surface fiat ramps quote error as fiatPayment.quoteError#9030
amitabh94 wants to merge 2 commits into
ogp/fiat-money-account-deposit-fixfrom
feat/tpc-fiat-quote-error

Conversation

@amitabh94
Copy link
Copy Markdown
Contributor

@amitabh94 amitabh94 commented Jun 6, 2026

Explanation

Stacked on #8987. The fiat strategy currently swallows the ramps quote error (getFiatQuotes catchreturn []), so the client can only show a generic "no quotes" message. This surfaces the provider's specific rejection (e.g. "Minimum purchase is $X") so MetaMask Pay can display it for providers without client-side structured limits.

Changes

  • TransactionFiatPayment.quoteError?: { code: 'LIMIT_EXCEEDED' | 'QUOTE_FAILED'; message?: string } (new TransactionFiatQuoteError type, exported).
  • getRampsQuote classifies the first quotes.error entry (LIMIT_EXCEEDED when the message matches limit keywords and not rate/request, else QUOTE_FAILED) and attaches it to the thrown error.
  • getFiatQuotes writes fiatPayment.quoteError on failure (via the existing updateFiatPayment mechanism, same as rampsQuote) and clears it on success. Still returns [] on no-quote.

Additive; provider scoping and rampsQuote population unchanged. Consumer: mobile useFiatBuyLimitAlert passes quoteError.message as backendError into the shared getProviderLimitMessage (UB2 parity).

Note

  • The jest.config.js moduleNameMapper addition is a local subpath-resolution workaround to run fiat-quotes.test.ts; drop before merge if CI resolves it without.

Checklist

  • Additive, backward-compatible
  • Tests (28 in fiat-quotes.test.ts)
  • Changelog

🤖 Generated with Claude Code


Note

Medium Risk
Additive API is low risk, but turning off restrictToKnownOrNativeProviders changes which on-ramp providers can be selected for fiat deposits.

Overview
Fiat ramps quote failures now persist structured errors on TransactionFiatPayment instead of only returning an empty quote list, so clients (e.g. mobile Pay) can show provider-specific copy like minimum purchase amounts.

Adds optional quoteError (LIMIT_EXCEEDED | QUOTE_FAILED, optional message) and exports TransactionFiatQuoteError. On ramps failure, getRampsQuote reads the first quotes.error entry, classifies the message (limit keywords vs rate/request exclusions), and getFiatQuotes writes that via updateFiatPayment in the existing catch path; success clears quoteError. Non-ramps failures still get QUOTE_FAILED without a message.

RampsController:getQuotes for the fiat strategy now passes restrictToKnownOrNativeProviders: false so aggregator providers can serve moneyAccountDeposit where native-only ramps are unavailable.

Includes fiat-quotes tests for error classification/clearing and a Jest moduleNameMapper tweak for eth keyring v2 subpaths (noted as optional for CI).

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

…iatPayment.quoteError

Add `TransactionFiatQuoteError` type and `quoteError` field to `TransactionFiatPayment`
so the mobile UI can display provider-specific rejection messages (e.g. "Minimum purchase
is $X") for providers without client-side structured limits. The fiat quote flow classifies
the first error entry from `quotes.error` as `LIMIT_EXCEEDED` when the message matches
limit keywords, or `QUOTE_FAILED` otherwise; the field is cleared on success.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@amitabh94 amitabh94 requested review from a team as code owners June 6, 2026 05:48
@amitabh94
Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

fiatPayment.quoteError = quoteError;
},
transactionId,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Superseded fetch persists quoteError

High Severity

The getFiatQuotes catch handler always calls updateFiatPayment to set quoteError but never checks request.signal. A superseded in-flight attempt can still write a stale provider error into fiatPayment after a newer refresh already succeeded, so the limit UI can show a rejection while current quotes are valid. The handler also does not clear rampsQuote, so error and quote fields can disagree.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a3d867d. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 6, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.1-preview-a3d867de5
@metamask-previews/accounts-controller@39.0.0-preview-a3d867de5
@metamask-previews/address-book-controller@7.1.2-preview-a3d867de5
@metamask-previews/ai-controllers@0.7.0-preview-a3d867de5
@metamask-previews/analytics-controller@1.1.0-preview-a3d867de5
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-a3d867de5
@metamask-previews/announcement-controller@8.1.0-preview-a3d867de5
@metamask-previews/app-metadata-controller@2.0.1-preview-a3d867de5
@metamask-previews/approval-controller@9.0.1-preview-a3d867de5
@metamask-previews/assets-controller@8.3.2-preview-a3d867de5
@metamask-previews/assets-controllers@108.5.0-preview-a3d867de5
@metamask-previews/authenticated-user-storage@2.0.0-preview-a3d867de5
@metamask-previews/base-controller@9.1.0-preview-a3d867de5
@metamask-previews/base-data-service@0.1.3-preview-a3d867de5
@metamask-previews/bridge-controller@73.2.1-preview-a3d867de5
@metamask-previews/bridge-status-controller@72.0.2-preview-a3d867de5
@metamask-previews/build-utils@3.0.4-preview-a3d867de5
@metamask-previews/chain-agnostic-permission@1.6.1-preview-a3d867de5
@metamask-previews/chomp-api-service@3.1.0-preview-a3d867de5
@metamask-previews/claims-controller@0.5.2-preview-a3d867de5
@metamask-previews/client-controller@1.0.1-preview-a3d867de5
@metamask-previews/compliance-controller@2.1.0-preview-a3d867de5
@metamask-previews/composable-controller@12.0.1-preview-a3d867de5
@metamask-previews/config-registry-controller@0.4.0-preview-a3d867de5
@metamask-previews/connectivity-controller@0.2.0-preview-a3d867de5
@metamask-previews/controller-utils@12.1.0-preview-a3d867de5
@metamask-previews/core-backend@6.3.2-preview-a3d867de5
@metamask-previews/delegation-controller@3.0.1-preview-a3d867de5
@metamask-previews/earn-controller@12.2.0-preview-a3d867de5
@metamask-previews/eip-5792-middleware@3.0.4-preview-a3d867de5
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-a3d867de5
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-a3d867de5
@metamask-previews/ens-controller@19.1.3-preview-a3d867de5
@metamask-previews/eth-block-tracker@15.0.1-preview-a3d867de5
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-a3d867de5
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-a3d867de5
@metamask-previews/foundryup@1.0.1-preview-a3d867de5
@metamask-previews/gas-fee-controller@26.2.2-preview-a3d867de5
@metamask-previews/gator-permissions-controller@4.2.0-preview-a3d867de5
@metamask-previews/geolocation-controller@0.1.3-preview-a3d867de5
@metamask-previews/json-rpc-engine@10.5.0-preview-a3d867de5
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-a3d867de5
@metamask-previews/keyring-controller@26.0.0-preview-a3d867de5
@metamask-previews/logging-controller@8.0.2-preview-a3d867de5
@metamask-previews/message-manager@14.1.2-preview-a3d867de5
@metamask-previews/messenger@1.2.0-preview-a3d867de5
@metamask-previews/messenger-cli@0.2.0-preview-a3d867de5
@metamask-previews/money-account-balance-service@1.0.2-preview-a3d867de5
@metamask-previews/money-account-controller@0.3.2-preview-a3d867de5
@metamask-previews/money-account-upgrade-controller@2.0.3-preview-a3d867de5
@metamask-previews/multichain-account-service@10.0.2-preview-a3d867de5
@metamask-previews/multichain-api-middleware@3.1.3-preview-a3d867de5
@metamask-previews/multichain-network-controller@3.1.3-preview-a3d867de5
@metamask-previews/multichain-transactions-controller@7.1.1-preview-a3d867de5
@metamask-previews/name-controller@9.1.2-preview-a3d867de5
@metamask-previews/network-controller@32.0.0-preview-a3d867de5
@metamask-previews/network-enablement-controller@5.3.0-preview-a3d867de5
@metamask-previews/notification-services-controller@24.1.2-preview-a3d867de5
@metamask-previews/passkey-controller@2.0.1-preview-a3d867de5
@metamask-previews/permission-controller@13.1.1-preview-a3d867de5
@metamask-previews/permission-log-controller@5.1.0-preview-a3d867de5
@metamask-previews/perps-controller@7.0.0-preview-a3d867de5
@metamask-previews/phishing-controller@17.2.0-preview-a3d867de5
@metamask-previews/polling-controller@16.0.6-preview-a3d867de5
@metamask-previews/preferences-controller@23.1.0-preview-a3d867de5
@metamask-previews/profile-metrics-controller@3.1.6-preview-a3d867de5
@metamask-previews/profile-sync-controller@28.1.1-preview-a3d867de5
@metamask-previews/ramps-controller@14.1.1-preview-a3d867de5
@metamask-previews/rate-limit-controller@7.0.1-preview-a3d867de5
@metamask-previews/react-data-query@0.2.1-preview-a3d867de5
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-a3d867de5
@metamask-previews/sample-controllers@5.0.1-preview-a3d867de5
@metamask-previews/seedless-onboarding-controller@10.0.0-preview-a3d867de5
@metamask-previews/selected-network-controller@26.1.3-preview-a3d867de5
@metamask-previews/shield-controller@5.1.2-preview-a3d867de5
@metamask-previews/signature-controller@39.2.4-preview-a3d867de5
@metamask-previews/snap-account-service@0.3.0-preview-a3d867de5
@metamask-previews/social-controllers@2.2.1-preview-a3d867de5
@metamask-previews/storage-service@1.0.1-preview-a3d867de5
@metamask-previews/subscription-controller@6.1.3-preview-a3d867de5
@metamask-previews/transaction-controller@66.0.1-preview-a3d867de5
@metamask-previews/transaction-pay-controller@23.1.0-preview-a3d867de5
@metamask-previews/user-operation-controller@41.2.3-preview-a3d867de5
@metamask-previews/wallet@2.0.0-preview-a3d867de5

…uote fetch

The fiat strategy was calling RampsController:getQuotes with
restrictToKnownOrNativeProviders: true, which silently drops aggregator
providers (type = 'aggregator', e.g. onramp.money) and returns an empty
quote list when no native provider (e.g. Transak) is available in the
user's region.

This makes quotes impossible in regions served only by aggregators — India
with onramp.money + ETH is a concrete example: the eligibility gate
(getBestProviderForAsset) correctly returns onramp.money, but the subsequent
quote call refused to use it, causing an infinite spinner.

The restriction was originally added for headless UB2 to enforce Transak-
native-only quotes. The fiat strategy (moneyAccountDeposit path) does not
have that constraint — the gate already verified that a supporting provider
exists, so the quote call should trust it and use whatever provider the
cascade selects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@amitabh94
Copy link
Copy Markdown
Contributor Author

@metamask-bot publish preview

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 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

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 86a299a. Configure here.

fiatPayment.quoteError = quoteError;
},
transactionId,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

quoteError set for non-ramps failures

Medium Severity

The shared catch in getFiatQuotes always writes fiatPayment.quoteError, including when getRampsQuote never ran (relay errors, invalid amounts, unsupported multi-token). Those cases get { code: 'QUOTE_FAILED' } with no provider message, which misrepresents a ramps rejection and can drive limit/quote UI from stale or irrelevant state.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 86a299a. Configure here.

@amitabh94
Copy link
Copy Markdown
Contributor Author

@metamask-bot publish preview

@amitabh94
Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 6, 2026

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.1-preview-86a299a60
@metamask-previews/accounts-controller@39.0.0-preview-86a299a60
@metamask-previews/address-book-controller@7.1.2-preview-86a299a60
@metamask-previews/ai-controllers@0.7.0-preview-86a299a60
@metamask-previews/analytics-controller@1.1.0-preview-86a299a60
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-86a299a60
@metamask-previews/announcement-controller@8.1.0-preview-86a299a60
@metamask-previews/app-metadata-controller@2.0.1-preview-86a299a60
@metamask-previews/approval-controller@9.0.1-preview-86a299a60
@metamask-previews/assets-controller@8.3.2-preview-86a299a60
@metamask-previews/assets-controllers@108.5.0-preview-86a299a60
@metamask-previews/authenticated-user-storage@2.0.0-preview-86a299a60
@metamask-previews/base-controller@9.1.0-preview-86a299a60
@metamask-previews/base-data-service@0.1.3-preview-86a299a60
@metamask-previews/bridge-controller@73.2.1-preview-86a299a60
@metamask-previews/bridge-status-controller@72.0.2-preview-86a299a60
@metamask-previews/build-utils@3.0.4-preview-86a299a60
@metamask-previews/chain-agnostic-permission@1.6.1-preview-86a299a60
@metamask-previews/chomp-api-service@3.1.0-preview-86a299a60
@metamask-previews/claims-controller@0.5.2-preview-86a299a60
@metamask-previews/client-controller@1.0.1-preview-86a299a60
@metamask-previews/compliance-controller@2.1.0-preview-86a299a60
@metamask-previews/composable-controller@12.0.1-preview-86a299a60
@metamask-previews/config-registry-controller@0.4.0-preview-86a299a60
@metamask-previews/connectivity-controller@0.2.0-preview-86a299a60
@metamask-previews/controller-utils@12.1.0-preview-86a299a60
@metamask-previews/core-backend@6.3.2-preview-86a299a60
@metamask-previews/delegation-controller@3.0.1-preview-86a299a60
@metamask-previews/earn-controller@12.2.0-preview-86a299a60
@metamask-previews/eip-5792-middleware@3.0.4-preview-86a299a60
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-86a299a60
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-86a299a60
@metamask-previews/ens-controller@19.1.3-preview-86a299a60
@metamask-previews/eth-block-tracker@15.0.1-preview-86a299a60
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-86a299a60
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-86a299a60
@metamask-previews/foundryup@1.0.1-preview-86a299a60
@metamask-previews/gas-fee-controller@26.2.2-preview-86a299a60
@metamask-previews/gator-permissions-controller@4.2.0-preview-86a299a60
@metamask-previews/geolocation-controller@0.1.3-preview-86a299a60
@metamask-previews/json-rpc-engine@10.5.0-preview-86a299a60
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-86a299a60
@metamask-previews/keyring-controller@26.0.0-preview-86a299a60
@metamask-previews/logging-controller@8.0.2-preview-86a299a60
@metamask-previews/message-manager@14.1.2-preview-86a299a60
@metamask-previews/messenger@1.2.0-preview-86a299a60
@metamask-previews/messenger-cli@0.2.0-preview-86a299a60
@metamask-previews/money-account-balance-service@1.0.2-preview-86a299a60
@metamask-previews/money-account-controller@0.3.2-preview-86a299a60
@metamask-previews/money-account-upgrade-controller@2.0.3-preview-86a299a60
@metamask-previews/multichain-account-service@10.0.2-preview-86a299a60
@metamask-previews/multichain-api-middleware@3.1.3-preview-86a299a60
@metamask-previews/multichain-network-controller@3.1.3-preview-86a299a60
@metamask-previews/multichain-transactions-controller@7.1.1-preview-86a299a60
@metamask-previews/name-controller@9.1.2-preview-86a299a60
@metamask-previews/network-controller@32.0.0-preview-86a299a60
@metamask-previews/network-enablement-controller@5.3.0-preview-86a299a60
@metamask-previews/notification-services-controller@24.1.2-preview-86a299a60
@metamask-previews/passkey-controller@2.0.1-preview-86a299a60
@metamask-previews/permission-controller@13.1.1-preview-86a299a60
@metamask-previews/permission-log-controller@5.1.0-preview-86a299a60
@metamask-previews/perps-controller@7.0.0-preview-86a299a60
@metamask-previews/phishing-controller@17.2.0-preview-86a299a60
@metamask-previews/polling-controller@16.0.6-preview-86a299a60
@metamask-previews/preferences-controller@23.1.0-preview-86a299a60
@metamask-previews/profile-metrics-controller@3.1.6-preview-86a299a60
@metamask-previews/profile-sync-controller@28.1.1-preview-86a299a60
@metamask-previews/ramps-controller@14.1.1-preview-86a299a60
@metamask-previews/rate-limit-controller@7.0.1-preview-86a299a60
@metamask-previews/react-data-query@0.2.1-preview-86a299a60
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-86a299a60
@metamask-previews/sample-controllers@5.0.1-preview-86a299a60
@metamask-previews/seedless-onboarding-controller@10.0.0-preview-86a299a60
@metamask-previews/selected-network-controller@26.1.3-preview-86a299a60
@metamask-previews/shield-controller@5.1.2-preview-86a299a60
@metamask-previews/signature-controller@39.2.4-preview-86a299a60
@metamask-previews/snap-account-service@0.3.0-preview-86a299a60
@metamask-previews/social-controllers@2.2.1-preview-86a299a60
@metamask-previews/storage-service@1.0.1-preview-86a299a60
@metamask-previews/subscription-controller@6.1.3-preview-86a299a60
@metamask-previews/transaction-controller@66.0.1-preview-86a299a60
@metamask-previews/transaction-pay-controller@23.1.0-preview-86a299a60
@metamask-previews/user-operation-controller@41.2.3-preview-86a299a60
@metamask-previews/wallet@2.0.0-preview-86a299a60

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant