Skip to content
Open
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
4 changes: 4 additions & 0 deletions packages/ramps-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
120 changes: 120 additions & 0 deletions packages/ramps-controller/src/RampsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Provider>)
.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', () => {
Expand Down
20 changes: 17 additions & 3 deletions packages/ramps-controller/src/RampsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1183,14 +1183,28 @@
);
}

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 ||

Check failure on line 1195 in packages/ramps-controller/src/RampsController.ts

View workflow job for this annotation

GitHub Actions / Lint, build, and test / Lint (22.x)

Prefer using an optional chain expression instead, as it's more concise and easier to read
supportedCryptos[selectedToken.assetId] !== false;

Check failure on line 1196 in packages/ramps-controller/src/RampsController.ts

View workflow job for this annotation

GitHub Actions / Lint, build, and test / Lint (22.x)

This expression unnecessarily compares a boolean value to a boolean instead of negating it

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 }),
);
}
}

/**
Expand Down
Loading