From 4aca7f2645a559fb6f1054b08117cffcf42efe22 Mon Sep 17 00:00:00 2001 From: AxelGes Date: Tue, 3 Mar 2026 23:35:17 -0300 Subject: [PATCH 1/3] fix(ramps-controller): skip payment methods fetch when token is unsupported by provider When setSelectedProvider is called, avoid fetching payment methods if the selected token is explicitly not supported by the new provider (according to supportedCryptoCurrencies). Fetching with an unsupported token causes the API to return empty results for the wrong reason, leaving the user with no payment methods and no feedback. The UI's Token Not Available modal handles this case instead. --- .../ramps-controller/src/RampsController.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/ramps-controller/src/RampsController.ts b/packages/ramps-controller/src/RampsController.ts index 23a4b10fad6..c874e322ef3 100644 --- a/packages/ramps-controller/src/RampsController.ts +++ b/packages/ramps-controller/src/RampsController.ts @@ -1183,14 +1183,28 @@ 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. + const tokenSupportedByProvider = + !selectedToken || + !supportedCryptos || + supportedCryptos[selectedToken.assetId] === true; + this.update((state) => { state.providers.selected = provider; resetResource(state, 'paymentMethods'); }); - this.#fireAndForget( - this.getPaymentMethods(regionCode, { provider: provider.id }), - ); + if (tokenSupportedByProvider) { + this.#fireAndForget( + this.getPaymentMethods(regionCode, { provider: provider.id }), + ); + } } /** From f43565bbdedeec6a97e3062512c995e302b39843 Mon Sep 17 00:00:00 2001 From: AxelGes Date: Tue, 3 Mar 2026 23:55:12 -0300 Subject: [PATCH 2/3] test(ramps-controller): add tests for setSelectedProvider token support check --- .../src/RampsController.test.ts | 120 ++++++++++++++++++ .../ramps-controller/src/RampsController.ts | 2 +- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/packages/ramps-controller/src/RampsController.test.ts b/packages/ramps-controller/src/RampsController.test.ts index e9391e4070c..9d745fe7011 100644 --- a/packages/ramps-controller/src/RampsController.test.ts +++ b/packages/ramps-controller/src/RampsController.test.ts @@ -2600,6 +2600,126 @@ describe('RampsController', () => { }, ); }); + + it('skips getPaymentMethods when selected token is explicitly not supported by the new provider', async () => { + const unsupportedToken: RampsToken = { + assetId: 'eip155:1/slip44:0', + chainId: 'eip155:1', + name: 'Bitcoin', + symbol: 'BTC', + decimals: 8, + iconUrl: '', + tokenSupported: true, + }; + + const providerWithExclusion: Provider = { + ...mockProvider, + supportedCryptoCurrencies: { + 'eip155:1/slip44:60': true, + [unsupportedToken.assetId]: false, + }, + }; + + const getPaymentMethodsMock = jest.fn(async () => ({ payments: [] })); + + await withController( + { + options: { + state: { + userRegion: createMockUserRegion('us-ca'), + providers: createResourceState([providerWithExclusion], null), + tokens: createResourceState(null, unsupportedToken), + }, + }, + }, + async ({ controller, rootMessenger }) => { + rootMessenger.registerActionHandler( + 'RampsService:getPaymentMethods', + getPaymentMethodsMock, + ); + + controller.setSelectedProvider(providerWithExclusion.id); + + expect(getPaymentMethodsMock).not.toHaveBeenCalled(); + }, + ); + }); + + it('fetches getPaymentMethods when provider has no supportedCryptoCurrencies field', async () => { + const providerWithoutField: Provider = { ...mockProvider }; + delete (providerWithoutField as Partial) + .supportedCryptoCurrencies; + + const getPaymentMethodsMock = jest.fn(async () => ({ payments: [] })); + + await withController( + { + options: { + state: { + userRegion: createMockUserRegion('us-ca'), + providers: createResourceState([providerWithoutField], null), + }, + }, + }, + async ({ controller, rootMessenger }) => { + rootMessenger.registerActionHandler( + 'RampsService:getPaymentMethods', + getPaymentMethodsMock, + ); + + controller.setSelectedProvider(providerWithoutField.id); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(getPaymentMethodsMock).toHaveBeenCalledTimes(1); + }, + ); + }); + + it('fetches getPaymentMethods when selected token is explicitly supported by the new provider', async () => { + const supportedToken: RampsToken = { + assetId: 'eip155:1/slip44:60', + chainId: 'eip155:1', + name: 'Ether', + symbol: 'ETH', + decimals: 18, + iconUrl: '', + tokenSupported: true, + }; + + const providerWithSupport: Provider = { + ...mockProvider, + supportedCryptoCurrencies: { + [supportedToken.assetId]: true, + }, + }; + + const getPaymentMethodsMock = jest.fn(async () => ({ payments: [] })); + + await withController( + { + options: { + state: { + userRegion: createMockUserRegion('us-ca'), + providers: createResourceState([providerWithSupport], null), + tokens: createResourceState(null, supportedToken), + }, + }, + }, + async ({ controller, rootMessenger }) => { + rootMessenger.registerActionHandler( + 'RampsService:getPaymentMethods', + getPaymentMethodsMock, + ); + + controller.setSelectedProvider(providerWithSupport.id); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(getPaymentMethodsMock).toHaveBeenCalledTimes(1); + }, + ); + }); }); describe('setSelectedToken', () => { diff --git a/packages/ramps-controller/src/RampsController.ts b/packages/ramps-controller/src/RampsController.ts index c874e322ef3..559143445dd 100644 --- a/packages/ramps-controller/src/RampsController.ts +++ b/packages/ramps-controller/src/RampsController.ts @@ -1193,7 +1193,7 @@ export class RampsController extends BaseController< const tokenSupportedByProvider = !selectedToken || !supportedCryptos || - supportedCryptos[selectedToken.assetId] === true; + supportedCryptos[selectedToken.assetId]; this.update((state) => { state.providers.selected = provider; From d222a3aadda897621eb5bc63f2ff2f3465e55ddb Mon Sep 17 00:00:00 2001 From: AxelGes Date: Wed, 4 Mar 2026 00:03:12 -0300 Subject: [PATCH 3/3] fix(ramps-controller): use blocklist semantics for supportedCryptoCurrencies check and add changelog entry Use !== false instead of === true so that tokens absent from the map are treated as supported (blocklist, not allowlist). --- packages/ramps-controller/CHANGELOG.md | 4 ++++ packages/ramps-controller/src/RampsController.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ramps-controller/CHANGELOG.md b/packages/ramps-controller/CHANGELOG.md index 65c60b9343a..3a069e0a568 100644 --- a/packages/ramps-controller/CHANGELOG.md +++ b/packages/ramps-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- `setSelectedProvider` no longer fetches payment methods when the selected token is explicitly not supported by the new provider, preventing empty payment method state with no user feedback ([#8103](https://github.com/MetaMask/core/pull/8103)) + ## [10.1.0] ### Added diff --git a/packages/ramps-controller/src/RampsController.ts b/packages/ramps-controller/src/RampsController.ts index 559143445dd..8b39f6b1323 100644 --- a/packages/ramps-controller/src/RampsController.ts +++ b/packages/ramps-controller/src/RampsController.ts @@ -1193,7 +1193,7 @@ export class RampsController extends BaseController< const tokenSupportedByProvider = !selectedToken || !supportedCryptos || - supportedCryptos[selectedToken.assetId]; + supportedCryptos[selectedToken.assetId] !== false; this.update((state) => { state.providers.selected = provider;