diff --git a/packages/ramps-controller/CHANGELOG.md b/packages/ramps-controller/CHANGELOG.md index 7b6125c5e1..8ff873c65a 100644 --- a/packages/ramps-controller/CHANGELOG.md +++ b/packages/ramps-controller/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.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)) +- Stop persisting the countries catalog in `RampsController` state and refetch it on every app startup via `init()` ([#9261](https://github.com/MetaMask/core/pull/9261)) +- Re-sync `userRegion` preset amounts from the countries catalog after each `getCountries()` call ([#9261](https://github.com/MetaMask/core/pull/9261)) - Bump `@metamask/controller-utils` from `^12.2.0` to `^12.3.0` ([#9218](https://github.com/MetaMask/core/pull/9218)) ## [14.3.0] diff --git a/packages/ramps-controller/src/RampsController-method-action-types.ts b/packages/ramps-controller/src/RampsController-method-action-types.ts index b22872242c..e0e89a82d9 100644 --- a/packages/ramps-controller/src/RampsController-method-action-types.ts +++ b/packages/ramps-controller/src/RampsController-method-action-types.ts @@ -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. diff --git a/packages/ramps-controller/src/RampsController.test.ts b/packages/ramps-controller/src/RampsController.test.ts index 46736e7c10..a804f93e11 100644 --- a/packages/ramps-controller/src/RampsController.test.ts +++ b/packages/ramps-controller/src/RampsController.test.ts @@ -921,12 +921,6 @@ describe('RampsController', () => { ), ).toMatchInlineSnapshot(` { - "countries": { - "data": [], - "error": null, - "isLoading": false, - "selected": null, - }, "orders": [], "providerAutoSelected": false, "userRegion": null, @@ -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(), @@ -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( @@ -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'); }, diff --git a/packages/ramps-controller/src/RampsController.ts b/packages/ramps-controller/src/RampsController.ts index 7ab3bcdac9..0ae4d81398 100644 --- a/packages/ramps-controller/src/RampsController.ts +++ b/packages/ramps-controller/src/RampsController.ts @@ -388,7 +388,7 @@ const rampsControllerMetadata = { usedInUi: true, }, countries: { - persist: true, + persist: false, includeInDebugSnapshot: true, includeInStateLogs: true, usedInUi: true, @@ -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. @@ -1412,12 +1412,7 @@ export class RampsController extends BaseController< } async #runInit(options?: ExecuteRequestOptions): Promise { - 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 @@ -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. @@ -1457,6 +1477,8 @@ export class RampsController extends BaseController< state.countries.data = Array.isArray(countries) ? [...countries] : []; }); + this.#syncUserRegionFromCountriesCatalog(); + return countries; }