From 3091595c3dd4bdcb7691d30a06143fdb51c49baa Mon Sep 17 00:00:00 2001 From: George Weiler Date: Mon, 30 Mar 2026 14:59:35 -0600 Subject: [PATCH 1/3] fix: do not fetch payment methods until a user has selected a token --- .../src/RampsController.test.ts | 55 ++++++++++++++++++- .../ramps-controller/src/RampsController.ts | 15 +++-- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/packages/ramps-controller/src/RampsController.test.ts b/packages/ramps-controller/src/RampsController.test.ts index 5a8f322c4f7..ffda9e528e6 100644 --- a/packages/ramps-controller/src/RampsController.test.ts +++ b/packages/ramps-controller/src/RampsController.test.ts @@ -2869,11 +2869,21 @@ describe('RampsController', () => { ); }); - it('fetches getPaymentMethods when provider has no supportedCryptoCurrencies field', async () => { + it('fetches getPaymentMethods when provider has no supportedCryptoCurrencies field and a token is selected', async () => { const providerWithoutField: Provider = { ...mockProvider }; delete (providerWithoutField as Partial) .supportedCryptoCurrencies; + const selectedToken: RampsToken = { + assetId: 'eip155:1/slip44:60', + chainId: 'eip155:1', + name: 'Ether', + symbol: 'ETH', + decimals: 18, + iconUrl: '', + tokenSupported: true, + }; + const getPaymentMethodsMock = jest.fn(async () => ({ payments: [] })); await withController( @@ -2882,6 +2892,7 @@ describe('RampsController', () => { state: { userRegion: createMockUserRegion('us-ca'), providers: createResourceState([providerWithoutField], null), + tokens: createResourceState(null, selectedToken), }, }, }, @@ -2903,6 +2914,36 @@ describe('RampsController', () => { ); }); + it('skips getPaymentMethods when no token is selected', async () => { + const getPaymentMethodsMock = jest.fn(async () => ({ payments: [] })); + + await withController( + { + options: { + state: { + userRegion: createMockUserRegion('us-ca'), + providers: createResourceState([mockProvider], null), + }, + }, + }, + async ({ rootMessenger }) => { + rootMessenger.registerActionHandler( + 'RampsService:getPaymentMethods', + getPaymentMethodsMock, + ); + + rootMessenger.call( + 'RampsController:setSelectedProvider', + mockProvider.id, + ); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(getPaymentMethodsMock).not.toHaveBeenCalled(); + }, + ); + }); + it('fetches getPaymentMethods when selected token is explicitly supported by the new provider', async () => { const supportedToken: RampsToken = { assetId: 'eip155:1/slip44:60', @@ -4417,6 +4458,16 @@ describe('RampsController', () => { }); it('does not update paymentMethods when selectedProvider changes during request', async () => { + const selectedToken: RampsToken = { + assetId: 'eip155:1/slip44:60', + chainId: 'eip155:1', + name: 'Ether', + symbol: 'ETH', + decimals: 18, + iconUrl: '', + tokenSupported: true, + }; + const providerA: Provider = { id: '/providers/provider-a', name: 'Provider A', @@ -4472,7 +4523,7 @@ describe('RampsController', () => { options: { state: { userRegion: createMockUserRegion('us-ca'), - tokens: createResourceState(null, null), + tokens: createResourceState(null, selectedToken), providers: createResourceState([providerA, providerB], providerA), paymentMethods: createResourceState([], null), }, diff --git a/packages/ramps-controller/src/RampsController.ts b/packages/ramps-controller/src/RampsController.ts index c37ae2c5b1a..5849151b983 100644 --- a/packages/ramps-controller/src/RampsController.ts +++ b/packages/ramps-controller/src/RampsController.ts @@ -1228,14 +1228,13 @@ export class RampsController extends BaseController< const selectedToken = this.state.tokens.selected; const supportedCryptos = provider.supportedCryptoCurrencies; - // Only fetch payment methods if the selected token is supported by the new - // provider. If it isn't, the payment methods request would fail or return - // empty for the wrong reason; the UI will show the Token Not Available modal - // so the user can change token or pick a different provider. + // Only fetch payment methods when a token is selected AND that token is + // supported by the new provider. Without a token the API receives an empty + // `crypto` param and returns an unfiltered list — payment methods must + // always be scoped to a specific token. const assetId = selectedToken?.assetId; - const tokenSupportedByProvider = !( - assetId && supportedCryptos?.[assetId] === false - ); + const shouldFetchPaymentMethods = + Boolean(assetId) && !(supportedCryptos?.[assetId as string] === false); this.update((state) => { state.providers.selected = provider; @@ -1243,7 +1242,7 @@ export class RampsController extends BaseController< resetResource(state, 'paymentMethods'); }); - if (tokenSupportedByProvider) { + if (shouldFetchPaymentMethods) { this.#fireAndForget( this.getPaymentMethods(regionCode, { provider: provider.id }), ); From 8b1380ed64b1cd48b8dc18237b792920003659e4 Mon Sep 17 00:00:00 2001 From: George Weiler Date: Mon, 30 Mar 2026 15:34:43 -0600 Subject: [PATCH 2/3] chore: changelog --- packages/ramps-controller/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/ramps-controller/CHANGELOG.md b/packages/ramps-controller/CHANGELOG.md index 1456e0bb1e9..82aeef4ccf2 100644 --- a/packages/ramps-controller/CHANGELOG.md +++ b/packages/ramps-controller/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- `setSelectedProvider` no longer fetches payment methods when no token is selected ([#TBD](https://github.com/MetaMask/core/pull/TBD)) + - Previously, the `tokenSupportedByProvider` guard evaluated to `true` when no token was selected (vacuous truth), causing an unfiltered payment methods fetch with an empty `crypto` parameter. The guard now requires a token to be selected before triggering the fetch. + ## [12.1.0] ### Added From a2ba97f25d760094fd3c7ca670c600c75317d95f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 31 Mar 2026 03:13:55 +0000 Subject: [PATCH 3/3] fix: update changelog with PR number #8342 Co-authored-by: George Weiler --- packages/ramps-controller/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ramps-controller/CHANGELOG.md b/packages/ramps-controller/CHANGELOG.md index 82aeef4ccf2..d3d50775268 100644 --- a/packages/ramps-controller/CHANGELOG.md +++ b/packages/ramps-controller/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `setSelectedProvider` no longer fetches payment methods when no token is selected ([#TBD](https://github.com/MetaMask/core/pull/TBD)) +- `setSelectedProvider` no longer fetches payment methods when no token is selected ([#8342](https://github.com/MetaMask/core/pull/8342)) - Previously, the `tokenSupportedByProvider` guard evaluated to `true` when no token was selected (vacuous truth), causing an unfiltered payment methods fetch with an empty `crypto` parameter. The guard now requires a token to be selected before triggering the fetch. ## [12.1.0]