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
7 changes: 7 additions & 0 deletions packages/ramps-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Stop persisting the countries catalog in `RampsController` state and refetch it on every app startup via `init()` ([#TBD](https://github.com/MetaMask/core/pull/TBD), [TRAM-3682](https://consensyssoftware.atlassian.net/browse/TRAM-3682))
- Re-sync `userRegion` preset amounts from the countries catalog after each `getCountries()` call ([#TBD](https://github.com/MetaMask/core/pull/TBD), [TRAM-3682](https://consensyssoftware.atlassian.net/browse/TRAM-3682))

## [15.0.0]

### Changed

- **BREAKING:** `RampsController.getProviders`, `RampsService.getProviders`, `RampsController.getPaymentMethods`, and `RampsService.getPaymentMethods` no longer accept a `fiat` query filter; region local fiat filtering is applied server-side when omitted ([#9245](https://github.com/MetaMask/core/pull/9245))
- Bump `@metamask/controller-utils` from `^12.2.0` to `^12.3.0` ([#9218](https://github.com/MetaMask/core/pull/9218))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export type RampsControllerSetSelectedProviderAction = {
* This should be called once at app startup to set up the initial region.
*
* Idempotent: subsequent calls return the same promise unless forceRefresh is set.
* Skips getCountries when countries are already loaded; skips geolocation when
* Always refetches the countries catalog on startup. Skips geolocation when
* userRegion already exists.
*
* @param options - Options for cache behavior. forceRefresh bypasses idempotency and re-runs the full flow.
Expand Down
66 changes: 58 additions & 8 deletions packages/ramps-controller/src/RampsController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,12 +921,6 @@ describe('RampsController', () => {
),
).toMatchInlineSnapshot(`
{
"countries": {
"data": [],
"error": null,
"isLoading": false,
"selected": null,
},
"orders": [],
"providerAutoSelected": false,
"userRegion": null,
Expand Down Expand Up @@ -1880,6 +1874,62 @@ describe('RampsController', () => {
});
});

it('re-syncs userRegion preset amounts after getCountries', async () => {
const staleCountries: Country[] = [
{
isoCode: 'CR',
id: '/regions/cr',
flag: '🇨🇷',
name: 'Costa Rica',
phone: {
prefix: '+506',
placeholder: '8312 3456',
template: 'XXXX XXXX',
},
currency: 'CRC',
supported: { buy: true, sell: true },
defaultAmount: 100,
quickAmounts: [20, 50, 100],
},
];
const freshCountries: Country[] = [
{
...staleCountries[0],
defaultAmount: 25000,
quickAmounts: [10000, 25000, 50000],
},
];

await withController(
{
options: {
state: {
userRegion: {
country: staleCountries[0],
state: null,
regionCode: 'cr',
},
},
},
},
async ({ controller, rootMessenger }) => {
rootMessenger.registerActionHandler(
'RampsService:getCountries',
async () => freshCountries,
);

await rootMessenger.call('RampsController:getCountries');

expect(controller.state.userRegion?.country.defaultAmount).toBe(
25000,
);
expect(
controller.state.userRegion?.country.quickAmounts,
).toStrictEqual([10000, 25000, 50000]);
},
);
});

it('throws when updating resource field and resource is null', async () => {
const stateWithNullCountries = {
...getDefaultRampsControllerState(),
Expand Down Expand Up @@ -2110,7 +2160,7 @@ describe('RampsController', () => {
});
});

it('skips getCountries and geolocation when userRegion and countries exist', async () => {
it('refetches countries on init but skips geolocation when userRegion exists', async () => {
let getCountriesCalled = false;
let getGeolocationCalled = false;
await withController(
Expand Down Expand Up @@ -2148,7 +2198,7 @@ describe('RampsController', () => {

await rootMessenger.call('RampsController:init');

expect(getCountriesCalled).toBe(false);
expect(getCountriesCalled).toBe(true);
expect(getGeolocationCalled).toBe(false);
expect(controller.state.userRegion?.regionCode).toBe('us-ca');
},
Expand Down
38 changes: 30 additions & 8 deletions packages/ramps-controller/src/RampsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ const rampsControllerMetadata = {
usedInUi: true,
},
countries: {
persist: true,
persist: false,
includeInDebugSnapshot: true,
includeInStateLogs: true,
usedInUi: true,
Expand Down Expand Up @@ -1383,7 +1383,7 @@ export class RampsController extends BaseController<
* This should be called once at app startup to set up the initial region.
*
* Idempotent: subsequent calls return the same promise unless forceRefresh is set.
* Skips getCountries when countries are already loaded; skips geolocation when
* Always refetches the countries catalog on startup. Skips geolocation when
* userRegion already exists.
*
* @param options - Options for cache behavior. forceRefresh bypasses idempotency and re-runs the full flow.
Expand Down Expand Up @@ -1412,12 +1412,7 @@ export class RampsController extends BaseController<
}

async #runInit(options?: ExecuteRequestOptions): Promise<void> {
const forceRefresh = options?.forceRefresh === true;
const hasCountries = this.state.countries.data.length > 0;

if (forceRefresh || !hasCountries) {
await this.getCountries(options);
}
await this.getCountries({ ...options, forceRefresh: true });

// Always prefer the user's persisted region. Geolocation is only used to
// seed the initial value; once the user (or a prior init) has set a region
Expand All @@ -1434,6 +1429,31 @@ export class RampsController extends BaseController<
await this.setUserRegion(regionCode, options);
}

/**
* Re-applies `userRegion` from the current countries catalog so preset
* amounts and support flags stay in sync after a catalog refresh.
*/
#syncUserRegionFromCountriesCatalog(): void {
const regionCode = this.state.userRegion?.regionCode;
if (!regionCode) {
return;
}

const countriesData = this.state.countries.data;
if (!countriesData.length) {
return;
}

const userRegion = findRegionFromCode(regionCode, countriesData);
if (!userRegion) {
return;
}

this.update((state) => {
state.userRegion = userRegion;
});
}

/**
* Fetches the list of supported countries.
* The API returns countries with support information for both buy and sell actions.
Expand All @@ -1457,6 +1477,8 @@ export class RampsController extends BaseController<
state.countries.data = Array.isArray(countries) ? [...countries] : [];
});

this.#syncUserRegionFromCountriesCatalog();

return countries;
}

Expand Down
Loading