@metamask-previews/ramps-controller 2.1.0-preview-22f11ed5 → 3.0.0-preview-821afcb8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add `preferredProvider` state and `setPreferredProvider()` method to RampsController ([#7617](https://github.com/MetaMask/core/pull/7617))
13
+
14
+ ## [3.0.0]
15
+
16
+ ### Added
17
+
18
+ - Add `getTokens()` method to RampsController for fetching available tokens by region and action ([#7607](https://github.com/MetaMask/core/pull/7607))
19
+
10
20
  ### Changed
11
21
 
12
22
  - **BREAKING:** Rename `geolocation` to `userRegion` and `updateGeolocation()` to `updateUserRegion()` in RampsController ([#7563](https://github.com/MetaMask/core/pull/7563))
@@ -49,7 +59,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
49
59
  - Add `OnRampService` for interacting with the OnRamp API
50
60
  - Add geolocation detection via IP address lookup
51
61
 
52
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@2.1.0...HEAD
62
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@3.0.0...HEAD
63
+ [3.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@2.1.0...@metamask/ramps-controller@3.0.0
53
64
  [2.1.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@2.0.0...@metamask/ramps-controller@2.1.0
54
65
  [2.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@1.0.0...@metamask/ramps-controller@2.0.0
55
66
  [1.0.0]: https://github.com/MetaMask/core/releases/tag/@metamask/ramps-controller@1.0.0
@@ -32,12 +32,24 @@ const rampsControllerMetadata = {
32
32
  includeInStateLogs: true,
33
33
  usedInUi: true,
34
34
  },
35
+ preferredProvider: {
36
+ persist: true,
37
+ includeInDebugSnapshot: true,
38
+ includeInStateLogs: true,
39
+ usedInUi: true,
40
+ },
35
41
  eligibility: {
36
42
  persist: true,
37
43
  includeInDebugSnapshot: true,
38
44
  includeInStateLogs: true,
39
45
  usedInUi: true,
40
46
  },
47
+ tokens: {
48
+ persist: true,
49
+ includeInDebugSnapshot: true,
50
+ includeInStateLogs: true,
51
+ usedInUi: true,
52
+ },
41
53
  requests: {
42
54
  persist: false,
43
55
  includeInDebugSnapshot: true,
@@ -56,7 +68,9 @@ const rampsControllerMetadata = {
56
68
  function getDefaultRampsControllerState() {
57
69
  return {
58
70
  userRegion: null,
71
+ preferredProvider: null,
59
72
  eligibility: null,
73
+ tokens: null,
60
74
  requests: {},
61
75
  };
62
76
  }
@@ -212,6 +226,7 @@ class RampsController extends base_controller_1.BaseController {
212
226
  : userRegion;
213
227
  this.update((state) => {
214
228
  state.userRegion = normalizedRegion;
229
+ state.tokens = null;
215
230
  });
216
231
  if (normalizedRegion) {
217
232
  try {
@@ -222,6 +237,7 @@ class RampsController extends base_controller_1.BaseController {
222
237
  const currentUserRegion = state.userRegion?.toLowerCase().trim();
223
238
  if (currentUserRegion === normalizedRegion) {
224
239
  state.eligibility = null;
240
+ state.tokens = null;
225
241
  }
226
242
  });
227
243
  }
@@ -240,6 +256,7 @@ class RampsController extends base_controller_1.BaseController {
240
256
  const normalizedRegion = region.toLowerCase().trim();
241
257
  this.update((state) => {
242
258
  state.userRegion = normalizedRegion;
259
+ state.tokens = null;
243
260
  });
244
261
  try {
245
262
  return await this.updateEligibility(normalizedRegion, options);
@@ -254,22 +271,45 @@ class RampsController extends base_controller_1.BaseController {
254
271
  const currentUserRegion = state.userRegion?.toLowerCase().trim();
255
272
  if (currentUserRegion === normalizedRegion) {
256
273
  state.eligibility = null;
274
+ state.tokens = null;
257
275
  }
258
276
  });
259
277
  throw error;
260
278
  }
261
279
  }
280
+ /**
281
+ * Sets the user's preferred provider.
282
+ * This allows users to set their preferred ramp provider.
283
+ *
284
+ * @param provider - The provider object to set.
285
+ */
286
+ setPreferredProvider(provider) {
287
+ this.update((state) => {
288
+ state.preferredProvider = provider;
289
+ });
290
+ }
262
291
  /**
263
292
  * Initializes the controller by fetching the user's region from geolocation.
264
293
  * This should be called once at app startup to set up the initial region.
294
+ * After the region is set and eligibility is determined, tokens are fetched
295
+ * and saved to state.
265
296
  *
266
297
  * @param options - Options for cache behavior.
267
298
  * @returns Promise that resolves when initialization is complete.
268
299
  */
269
300
  async init(options) {
270
- await this.updateUserRegion(options).catch(() => {
301
+ const userRegion = await this.updateUserRegion(options).catch(() => {
271
302
  // User region fetch failed - error state will be available via selectors
303
+ return null;
272
304
  });
305
+ if (userRegion) {
306
+ try {
307
+ await this.getTokens(userRegion, 'buy', options);
308
+ }
309
+ catch {
310
+ // Token fetch failed - error state will be available via selectors
311
+ }
312
+ }
273
313
  }
274
314
  /**
275
315
  * Updates the eligibility information for a given region.
@@ -305,6 +345,33 @@ class RampsController extends base_controller_1.BaseController {
305
345
  return this.messenger.call('RampsService:getCountries', action);
306
346
  }, options);
307
347
  }
348
+ /**
349
+ * Fetches the list of available tokens for a given region and action.
350
+ * The tokens are saved in the controller state once fetched.
351
+ *
352
+ * @param region - The region code (e.g., "us", "fr", "us-ny"). If not provided, uses the user's region from controller state.
353
+ * @param action - The ramp action type ('buy' or 'sell').
354
+ * @param options - Options for cache behavior.
355
+ * @returns The tokens response containing topTokens and allTokens.
356
+ */
357
+ async getTokens(region, action = 'buy', options) {
358
+ const regionToUse = region ?? this.state.userRegion;
359
+ if (!regionToUse) {
360
+ throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
361
+ }
362
+ const normalizedRegion = regionToUse.toLowerCase().trim();
363
+ const cacheKey = (0, RequestCache_1.createCacheKey)('getTokens', [normalizedRegion, action]);
364
+ const tokens = await this.executeRequest(cacheKey, async () => {
365
+ return this.messenger.call('RampsService:getTokens', normalizedRegion, action);
366
+ }, options);
367
+ this.update((state) => {
368
+ const userRegion = state.userRegion?.toLowerCase().trim();
369
+ if (userRegion === undefined || userRegion === normalizedRegion) {
370
+ state.tokens = tokens;
371
+ }
372
+ });
373
+ return tokens;
374
+ }
308
375
  }
309
376
  exports.RampsController = RampsController;
310
377
  _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
@@ -1 +1 @@
1
- {"version":3,"file":"RampsController.cjs","sourceRoot":"","sources":["../src/RampsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\n\nimport type { Country, Eligibility } from './RampsService';\nimport type {\n RampsServiceGetGeolocationAction,\n RampsServiceGetCountriesAction,\n RampsServiceGetEligibilityAction,\n} from './RampsService-method-action-types';\nimport type {\n RequestCache as RequestCacheType,\n RequestState,\n ExecuteRequestOptions,\n PendingRequest,\n} from './RequestCache';\nimport {\n DEFAULT_REQUEST_CACHE_TTL,\n DEFAULT_REQUEST_CACHE_MAX_SIZE,\n createCacheKey,\n isCacheExpired,\n createLoadingState,\n createSuccessState,\n createErrorState,\n} from './RequestCache';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link RampsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'RampsController';\n\n// === STATE ===\n\n/**\n * Describes the shape of the state object for {@link RampsController}.\n */\nexport type RampsControllerState = {\n /**\n * The user's selected region code (e.g., \"US-CA\").\n * Initially set via geolocation fetch, but can be manually changed by the user.\n */\n userRegion: string | null;\n /**\n * Eligibility information for the user's current region.\n */\n eligibility: Eligibility | null;\n /**\n * Cache of request states, keyed by cache key.\n * This stores loading, success, and error states for API requests.\n */\n requests: RequestCacheType;\n};\n\n/**\n * The metadata for each property in {@link RampsControllerState}.\n */\nconst rampsControllerMetadata = {\n userRegion: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n eligibility: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n requests: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: false,\n usedInUi: true,\n },\n} satisfies StateMetadata<RampsControllerState>;\n\n/**\n * Constructs the default {@link RampsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link RampsController} state.\n */\nexport function getDefaultRampsControllerState(): RampsControllerState {\n return {\n userRegion: null,\n eligibility: null,\n requests: {},\n };\n}\n\n// === MESSENGER ===\n\n/**\n * Retrieves the state of the {@link RampsController}.\n */\nexport type RampsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n RampsControllerState\n>;\n\n/**\n * Actions that {@link RampsControllerMessenger} exposes to other consumers.\n */\nexport type RampsControllerActions = RampsControllerGetStateAction;\n\n/**\n * Actions from other messengers that {@link RampsController} calls.\n */\ntype AllowedActions =\n | RampsServiceGetGeolocationAction\n | RampsServiceGetCountriesAction\n | RampsServiceGetEligibilityAction;\n\n/**\n * Published when the state of {@link RampsController} changes.\n */\nexport type RampsControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n RampsControllerState\n>;\n\n/**\n * Events that {@link RampsControllerMessenger} exposes to other consumers.\n */\nexport type RampsControllerEvents = RampsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link RampsController} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link RampsController}.\n */\nexport type RampsControllerMessenger = Messenger<\n typeof controllerName,\n RampsControllerActions | AllowedActions,\n RampsControllerEvents | AllowedEvents\n>;\n\n/**\n * Configuration options for the RampsController.\n */\nexport type RampsControllerOptions = {\n /** The messenger suited for this controller. */\n messenger: RampsControllerMessenger;\n /** The desired state with which to initialize this controller. */\n state?: Partial<RampsControllerState>;\n /** Time to live for cached requests in milliseconds. Defaults to 15 minutes. */\n requestCacheTTL?: number;\n /** Maximum number of entries in the request cache. Defaults to 250. */\n requestCacheMaxSize?: number;\n};\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * Manages cryptocurrency on/off ramps functionality.\n */\nexport class RampsController extends BaseController<\n typeof controllerName,\n RampsControllerState,\n RampsControllerMessenger\n> {\n /**\n * Default TTL for cached requests.\n */\n readonly #requestCacheTTL: number;\n\n /**\n * Maximum number of entries in the request cache.\n */\n readonly #requestCacheMaxSize: number;\n\n /**\n * Map of pending requests for deduplication.\n * Key is the cache key, value is the pending request with abort controller.\n */\n readonly #pendingRequests: Map<string, PendingRequest> = new Map();\n\n /**\n * Constructs a new {@link RampsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.requestCacheTTL - Time to live for cached requests in milliseconds.\n * @param args.requestCacheMaxSize - Maximum number of entries in the request cache.\n */\n constructor({\n messenger,\n state = {},\n requestCacheTTL = DEFAULT_REQUEST_CACHE_TTL,\n requestCacheMaxSize = DEFAULT_REQUEST_CACHE_MAX_SIZE,\n }: RampsControllerOptions) {\n super({\n messenger,\n metadata: rampsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultRampsControllerState(),\n ...state,\n // Always reset requests cache on initialization (non-persisted)\n requests: {},\n },\n });\n\n this.#requestCacheTTL = requestCacheTTL;\n this.#requestCacheMaxSize = requestCacheMaxSize;\n }\n\n /**\n * Executes a request with caching and deduplication.\n *\n * If a request with the same cache key is already in flight, returns the\n * existing promise. If valid cached data exists, returns it without making\n * a new request.\n *\n * @param cacheKey - Unique identifier for this request.\n * @param fetcher - Function that performs the actual fetch. Receives an AbortSignal.\n * @param options - Options for cache behavior.\n * @returns The result of the request.\n */\n async executeRequest<TResult>(\n cacheKey: string,\n fetcher: (signal: AbortSignal) => Promise<TResult>,\n options?: ExecuteRequestOptions,\n ): Promise<TResult> {\n const ttl = options?.ttl ?? this.#requestCacheTTL;\n\n // Check for existing pending request - join it instead of making a duplicate\n const pending = this.#pendingRequests.get(cacheKey);\n if (pending) {\n return pending.promise as Promise<TResult>;\n }\n\n // Check cache validity (unless force refresh)\n if (!options?.forceRefresh) {\n const cached = this.state.requests[cacheKey];\n if (cached && !isCacheExpired(cached, ttl)) {\n return cached.data as TResult;\n }\n }\n\n // Create abort controller for this request\n const abortController = new AbortController();\n const lastFetchedAt = Date.now();\n\n // Update state to loading\n this.#updateRequestState(cacheKey, createLoadingState());\n\n // Create the fetch promise\n const promise = (async (): Promise<TResult> => {\n try {\n const data = await fetcher(abortController.signal);\n\n // Don't update state if aborted\n if (abortController.signal.aborted) {\n throw new Error('Request was aborted');\n }\n\n this.#updateRequestState(\n cacheKey,\n createSuccessState(data as Json, lastFetchedAt),\n );\n return data;\n } catch (error) {\n // Don't update state if aborted\n if (abortController.signal.aborted) {\n throw error;\n }\n\n const errorMessage = (error as Error)?.message;\n\n this.#updateRequestState(\n cacheKey,\n createErrorState(errorMessage ?? 'Unknown error', lastFetchedAt),\n );\n throw error;\n } finally {\n // Only delete if this is still our entry (not replaced by a new request)\n const currentPending = this.#pendingRequests.get(cacheKey);\n if (currentPending?.abortController === abortController) {\n this.#pendingRequests.delete(cacheKey);\n }\n }\n })();\n\n // Store pending request for deduplication\n this.#pendingRequests.set(cacheKey, { promise, abortController });\n\n return promise;\n }\n\n /**\n * Aborts a pending request if one exists.\n *\n * @param cacheKey - The cache key of the request to abort.\n * @returns True if a request was aborted.\n */\n abortRequest(cacheKey: string): boolean {\n const pending = this.#pendingRequests.get(cacheKey);\n if (pending) {\n pending.abortController.abort();\n this.#pendingRequests.delete(cacheKey);\n this.#removeRequestState(cacheKey);\n return true;\n }\n return false;\n }\n\n /**\n * Removes a request state from the cache.\n *\n * @param cacheKey - The cache key to remove.\n */\n #removeRequestState(cacheKey: string): void {\n this.update((state) => {\n const requests = state.requests as unknown as Record<\n string,\n RequestState | undefined\n >;\n delete requests[cacheKey];\n });\n }\n\n /**\n * Gets the state of a specific cached request.\n *\n * @param cacheKey - The cache key to look up.\n * @returns The request state, or undefined if not cached.\n */\n getRequestState(cacheKey: string): RequestState | undefined {\n return this.state.requests[cacheKey];\n }\n\n /**\n * Updates the state for a specific request.\n *\n * @param cacheKey - The cache key.\n * @param requestState - The new state for the request.\n */\n #updateRequestState(cacheKey: string, requestState: RequestState): void {\n const maxSize = this.#requestCacheMaxSize;\n\n this.update((state) => {\n const requests = state.requests as unknown as Record<\n string,\n RequestState | undefined\n >;\n requests[cacheKey] = requestState;\n\n // Evict oldest entries if cache exceeds max size\n const keys = Object.keys(requests);\n\n if (keys.length > maxSize) {\n // Sort by timestamp (oldest first)\n const sortedKeys = keys.sort((a, b) => {\n const aTime = requests[a]?.timestamp ?? 0;\n const bTime = requests[b]?.timestamp ?? 0;\n return aTime - bTime;\n });\n\n // Remove oldest entries until we're under the limit\n const entriesToRemove = keys.length - maxSize;\n for (let i = 0; i < entriesToRemove; i++) {\n const keyToRemove = sortedKeys[i];\n if (keyToRemove) {\n delete requests[keyToRemove];\n }\n }\n }\n });\n }\n\n /**\n * Updates the user's region by fetching geolocation and eligibility.\n * This method calls the RampsService to get the geolocation,\n * then automatically fetches eligibility for that region.\n *\n * @param options - Options for cache behavior.\n * @returns The user region string.\n */\n async updateUserRegion(options?: ExecuteRequestOptions): Promise<string> {\n const cacheKey = createCacheKey('updateUserRegion', []);\n\n const userRegion = await this.executeRequest(\n cacheKey,\n async () => {\n const result = await this.messenger.call('RampsService:getGeolocation');\n return result;\n },\n options,\n );\n\n const normalizedRegion = userRegion\n ? userRegion.toLowerCase().trim()\n : userRegion;\n\n this.update((state) => {\n state.userRegion = normalizedRegion;\n });\n\n if (normalizedRegion) {\n try {\n await this.updateEligibility(normalizedRegion, options);\n } catch {\n this.update((state) => {\n const currentUserRegion = state.userRegion?.toLowerCase().trim();\n if (currentUserRegion === normalizedRegion) {\n state.eligibility = null;\n }\n });\n }\n }\n\n return normalizedRegion;\n }\n\n /**\n * Sets the user's region manually (without fetching geolocation).\n * This allows users to override the detected region.\n *\n * @param region - The region code to set (e.g., \"US-CA\").\n * @param options - Options for cache behavior when fetching eligibility.\n * @returns The eligibility information for the region.\n */\n async setUserRegion(\n region: string,\n options?: ExecuteRequestOptions,\n ): Promise<Eligibility> {\n const normalizedRegion = region.toLowerCase().trim();\n\n this.update((state) => {\n state.userRegion = normalizedRegion;\n });\n\n try {\n return await this.updateEligibility(normalizedRegion, options);\n } catch (error) {\n // Eligibility fetch failed, but user region was successfully set.\n // Don't let eligibility errors prevent user region state from being updated.\n // Clear eligibility state to avoid showing stale data from a previous location.\n // Only clear if the region still matches to avoid race conditions where a newer\n // region change has already succeeded.\n this.update((state) => {\n const currentUserRegion = state.userRegion?.toLowerCase().trim();\n if (currentUserRegion === normalizedRegion) {\n state.eligibility = null;\n }\n });\n throw error;\n }\n }\n\n /**\n * Initializes the controller by fetching the user's region from geolocation.\n * This should be called once at app startup to set up the initial region.\n *\n * @param options - Options for cache behavior.\n * @returns Promise that resolves when initialization is complete.\n */\n async init(options?: ExecuteRequestOptions): Promise<void> {\n await this.updateUserRegion(options).catch(() => {\n // User region fetch failed - error state will be available via selectors\n });\n }\n\n /**\n * Updates the eligibility information for a given region.\n *\n * @param isoCode - The ISO code for the region (e.g., \"us\", \"fr\", \"us-ny\").\n * @param options - Options for cache behavior.\n * @returns The eligibility information.\n */\n async updateEligibility(\n isoCode: string,\n options?: ExecuteRequestOptions,\n ): Promise<Eligibility> {\n const normalizedIsoCode = isoCode.toLowerCase().trim();\n const cacheKey = createCacheKey('updateEligibility', [normalizedIsoCode]);\n\n const eligibility = await this.executeRequest(\n cacheKey,\n async () => {\n return this.messenger.call(\n 'RampsService:getEligibility',\n normalizedIsoCode,\n );\n },\n options,\n );\n\n this.update((state) => {\n const userRegion = state.userRegion?.toLowerCase().trim();\n\n if (userRegion === undefined || userRegion === normalizedIsoCode) {\n state.eligibility = eligibility;\n }\n });\n\n return eligibility;\n }\n\n /**\n * Fetches the list of supported countries for a given ramp action.\n *\n * @param action - The ramp action type ('buy' or 'sell').\n * @param options - Options for cache behavior.\n * @returns An array of countries with their eligibility information.\n */\n async getCountries(\n action: 'buy' | 'sell' = 'buy',\n options?: ExecuteRequestOptions,\n ): Promise<Country[]> {\n const cacheKey = createCacheKey('getCountries', [action]);\n\n return this.executeRequest(\n cacheKey,\n async () => {\n return this.messenger.call('RampsService:getCountries', action);\n },\n options,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"RampsController.cjs","sourceRoot":"","sources":["../src/RampsController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAKA,+DAA2D;AAsB3D,qDAQwB;AAExB,kBAAkB;AAElB;;;;GAIG;AACU,QAAA,cAAc,GAAG,iBAAiB,CAAC;AAkChD;;GAEG;AACH,MAAM,uBAAuB,GAAG;IAC9B,UAAU,EAAE;QACV,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,iBAAiB,EAAE;QACjB,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,WAAW,EAAE;QACX,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,MAAM,EAAE;QACN,OAAO,EAAE,IAAI;QACb,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,KAAK;QACzB,QAAQ,EAAE,IAAI;KACf;CAC4C,CAAC;AAEhD;;;;;;;GAOG;AACH,SAAgB,8BAA8B;IAC5C,OAAO;QACL,UAAU,EAAE,IAAI;QAChB,iBAAiB,EAAE,IAAI;QACvB,WAAW,EAAE,IAAI;QACjB,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC;AARD,wEAQC;AAoED,gCAAgC;AAEhC;;GAEG;AACH,MAAa,eAAgB,SAAQ,gCAIpC;IAiBC;;;;;;;;;OASG;IACH,YAAY,EACV,SAAS,EACT,KAAK,GAAG,EAAE,EACV,eAAe,GAAG,wCAAyB,EAC3C,mBAAmB,GAAG,6CAA8B,GAC7B;QACvB,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE,uBAAuB;YACjC,IAAI,EAAE,sBAAc;YACpB,KAAK,EAAE;gBACL,GAAG,8BAA8B,EAAE;gBACnC,GAAG,KAAK;gBACR,gEAAgE;gBAChE,QAAQ,EAAE,EAAE;aACb;SACF,CAAC,CAAC;;QA1CL;;WAEG;QACM,mDAAyB;QAElC;;WAEG;QACM,uDAA6B;QAEtC;;;WAGG;QACM,2CAAgD,IAAI,GAAG,EAAE,EAAC;QA8BjE,uBAAA,IAAI,oCAAoB,eAAe,MAAA,CAAC;QACxC,uBAAA,IAAI,wCAAwB,mBAAmB,MAAA,CAAC;IAClD,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,cAAc,CAClB,QAAgB,EAChB,OAAkD,EAClD,OAA+B;QAE/B,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,uBAAA,IAAI,wCAAiB,CAAC;QAElD,6EAA6E;QAC7E,MAAM,OAAO,GAAG,uBAAA,IAAI,wCAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC,OAA2B,CAAC;QAC7C,CAAC;QAED,8CAA8C;QAC9C,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC7C,IAAI,MAAM,IAAI,CAAC,IAAA,6BAAc,EAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC3C,OAAO,MAAM,CAAC,IAAe,CAAC;YAChC,CAAC;QACH,CAAC;QAED,2CAA2C;QAC3C,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEjC,0BAA0B;QAC1B,uBAAA,IAAI,uEAAoB,MAAxB,IAAI,EAAqB,QAAQ,EAAE,IAAA,iCAAkB,GAAE,CAAC,CAAC;QAEzD,2BAA2B;QAC3B,MAAM,OAAO,GAAG,CAAC,KAAK,IAAsB,EAAE;YAC5C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;gBAEnD,gCAAgC;gBAChC,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnC,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;gBACzC,CAAC;gBAED,uBAAA,IAAI,uEAAoB,MAAxB,IAAI,EACF,QAAQ,EACR,IAAA,iCAAkB,EAAC,IAAY,EAAE,aAAa,CAAC,CAChD,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,gCAAgC;gBAChC,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnC,MAAM,KAAK,CAAC;gBACd,CAAC;gBAED,MAAM,YAAY,GAAI,KAAe,EAAE,OAAO,CAAC;gBAE/C,uBAAA,IAAI,uEAAoB,MAAxB,IAAI,EACF,QAAQ,EACR,IAAA,+BAAgB,EAAC,YAAY,IAAI,eAAe,EAAE,aAAa,CAAC,CACjE,CAAC;gBACF,MAAM,KAAK,CAAC;YACd,CAAC;oBAAS,CAAC;gBACT,yEAAyE;gBACzE,MAAM,cAAc,GAAG,uBAAA,IAAI,wCAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC3D,IAAI,cAAc,EAAE,eAAe,KAAK,eAAe,EAAE,CAAC;oBACxD,uBAAA,IAAI,wCAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QAEL,0CAA0C;QAC1C,uBAAA,IAAI,wCAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;QAElE,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,QAAgB;QAC3B,MAAM,OAAO,GAAG,uBAAA,IAAI,wCAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAChC,uBAAA,IAAI,wCAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACvC,uBAAA,IAAI,uEAAoB,MAAxB,IAAI,EAAqB,QAAQ,CAAC,CAAC;YACnC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAiBD;;;;;OAKG;IACH,eAAe,CAAC,QAAgB;QAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAyCD;;;;;;;OAOG;IACH,KAAK,CAAC,gBAAgB,CAAC,OAA+B;QACpD,MAAM,QAAQ,GAAG,IAAA,6BAAc,EAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;QAExD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,cAAc,CAC1C,QAAQ,EACR,KAAK,IAAI,EAAE;YACT,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YACxE,OAAO,MAAM,CAAC;QAChB,CAAC,EACD,OAAO,CACR,CAAC;QAEF,MAAM,gBAAgB,GAAG,UAAU;YACjC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE;YACjC,CAAC,CAAC,UAAU,CAAC;QAEf,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,UAAU,GAAG,gBAAgB,CAAC;YACpC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBACpB,MAAM,iBAAiB,GAAG,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;oBACjE,IAAI,iBAAiB,KAAK,gBAAgB,EAAE,CAAC;wBAC3C,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;wBACzB,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;oBACtB,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,aAAa,CACjB,MAAc,EACd,OAA+B;QAE/B,MAAM,gBAAgB,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QAErD,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,UAAU,GAAG,gBAAgB,CAAC;YACpC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kEAAkE;YAClE,6EAA6E;YAC7E,gFAAgF;YAChF,gFAAgF;YAChF,uCAAuC;YACvC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,MAAM,iBAAiB,GAAG,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;gBACjE,IAAI,iBAAiB,KAAK,gBAAgB,EAAE,CAAC;oBAC3C,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;oBACzB,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;gBACtB,CAAC;YACH,CAAC,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,oBAAoB,CAAC,QAAyB;QAC5C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,iBAAiB,GAAG,QAAQ,CAAC;QACrC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,IAAI,CAAC,OAA+B;QACxC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACjE,yEAAyE;YACzE,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACnD,CAAC;YAAC,MAAM,CAAC;gBACP,mEAAmE;YACrE,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,iBAAiB,CACrB,OAAe,EACf,OAA+B;QAE/B,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACvD,MAAM,QAAQ,GAAG,IAAA,6BAAc,EAAC,mBAAmB,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAE1E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAC3C,QAAQ,EACR,KAAK,IAAI,EAAE;YACT,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,6BAA6B,EAC7B,iBAAiB,CAClB,CAAC;QACJ,CAAC,EACD,OAAO,CACR,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YAE1D,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,iBAAiB,EAAE,CAAC;gBACjE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;YAClC,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAChB,SAAyB,KAAK,EAC9B,OAA+B;QAE/B,MAAM,QAAQ,GAAG,IAAA,6BAAc,EAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QAE1D,OAAO,IAAI,CAAC,cAAc,CACxB,QAAQ,EACR,KAAK,IAAI,EAAE;YACT,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC,EACD,OAAO,CACR,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,SAAS,CACb,MAAe,EACf,SAAyB,KAAK,EAC9B,OAA+B;QAE/B,MAAM,WAAW,GAAG,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;QAEpD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,wGAAwG,CACzG,CAAC;QACJ,CAAC;QAED,MAAM,gBAAgB,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAA,6BAAc,EAAC,WAAW,EAAE,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC;QAEzE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,cAAc,CACtC,QAAQ,EACR,KAAK,IAAI,EAAE;YACT,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CACxB,wBAAwB,EACxB,gBAAgB,EAChB,MAAM,CACP,CAAC;QACJ,CAAC,EACD,OAAO,CACR,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YAE1D,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,gBAAgB,EAAE,CAAC;gBAChE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;YACxB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA1bD,0CA0bC;yRA5RqB,QAAgB;IAClC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAGtB,CAAC;QACF,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC,qFAkBmB,QAAgB,EAAE,YAA0B;IAC9D,MAAM,OAAO,GAAG,uBAAA,IAAI,4CAAqB,CAAC;IAE1C,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAGtB,CAAC;QACF,QAAQ,CAAC,QAAQ,CAAC,GAAG,YAAY,CAAC;QAElC,iDAAiD;QACjD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEnC,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;YAC1B,mCAAmC;YACnC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC;gBAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC;gBAC1C,OAAO,KAAK,GAAG,KAAK,CAAC;YACvB,CAAC,CAAC,CAAC;YAEH,oDAAoD;YACpD,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;YAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;gBAClC,IAAI,WAAW,EAAE,CAAC;oBAChB,OAAO,QAAQ,CAAC,WAAW,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\nimport type { Json } from '@metamask/utils';\n\nimport type {\n Country,\n Eligibility,\n TokensResponse,\n Provider,\n} from './RampsService';\nimport type {\n RampsServiceGetGeolocationAction,\n RampsServiceGetCountriesAction,\n RampsServiceGetEligibilityAction,\n RampsServiceGetTokensAction,\n} from './RampsService-method-action-types';\nimport type {\n RequestCache as RequestCacheType,\n RequestState,\n ExecuteRequestOptions,\n PendingRequest,\n} from './RequestCache';\nimport {\n DEFAULT_REQUEST_CACHE_TTL,\n DEFAULT_REQUEST_CACHE_MAX_SIZE,\n createCacheKey,\n isCacheExpired,\n createLoadingState,\n createSuccessState,\n createErrorState,\n} from './RequestCache';\n\n// === GENERAL ===\n\n/**\n * The name of the {@link RampsController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'RampsController';\n\n// === STATE ===\n\n/**\n * Describes the shape of the state object for {@link RampsController}.\n */\nexport type RampsControllerState = {\n /**\n * The user's selected region code (e.g., \"US-CA\").\n * Initially set via geolocation fetch, but can be manually changed by the user.\n */\n userRegion: string | null;\n /**\n * The user's preferred provider.\n * Can be manually set by the user.\n */\n preferredProvider: Provider | null;\n /**\n * Eligibility information for the user's current region.\n */\n eligibility: Eligibility | null;\n /**\n * Tokens fetched for the current region and action.\n * Contains topTokens and allTokens arrays.\n */\n tokens: TokensResponse | null;\n /**\n * Cache of request states, keyed by cache key.\n * This stores loading, success, and error states for API requests.\n */\n requests: RequestCacheType;\n};\n\n/**\n * The metadata for each property in {@link RampsControllerState}.\n */\nconst rampsControllerMetadata = {\n userRegion: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n preferredProvider: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n eligibility: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n tokens: {\n persist: true,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n requests: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: false,\n usedInUi: true,\n },\n} satisfies StateMetadata<RampsControllerState>;\n\n/**\n * Constructs the default {@link RampsController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link RampsController} state.\n */\nexport function getDefaultRampsControllerState(): RampsControllerState {\n return {\n userRegion: null,\n preferredProvider: null,\n eligibility: null,\n tokens: null,\n requests: {},\n };\n}\n\n// === MESSENGER ===\n\n/**\n * Retrieves the state of the {@link RampsController}.\n */\nexport type RampsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n RampsControllerState\n>;\n\n/**\n * Actions that {@link RampsControllerMessenger} exposes to other consumers.\n */\nexport type RampsControllerActions = RampsControllerGetStateAction;\n\n/**\n * Actions from other messengers that {@link RampsController} calls.\n */\ntype AllowedActions =\n | RampsServiceGetGeolocationAction\n | RampsServiceGetCountriesAction\n | RampsServiceGetEligibilityAction\n | RampsServiceGetTokensAction;\n\n/**\n * Published when the state of {@link RampsController} changes.\n */\nexport type RampsControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n RampsControllerState\n>;\n\n/**\n * Events that {@link RampsControllerMessenger} exposes to other consumers.\n */\nexport type RampsControllerEvents = RampsControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link RampsController} subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link RampsController}.\n */\nexport type RampsControllerMessenger = Messenger<\n typeof controllerName,\n RampsControllerActions | AllowedActions,\n RampsControllerEvents | AllowedEvents\n>;\n\n/**\n * Configuration options for the RampsController.\n */\nexport type RampsControllerOptions = {\n /** The messenger suited for this controller. */\n messenger: RampsControllerMessenger;\n /** The desired state with which to initialize this controller. */\n state?: Partial<RampsControllerState>;\n /** Time to live for cached requests in milliseconds. Defaults to 15 minutes. */\n requestCacheTTL?: number;\n /** Maximum number of entries in the request cache. Defaults to 250. */\n requestCacheMaxSize?: number;\n};\n\n// === CONTROLLER DEFINITION ===\n\n/**\n * Manages cryptocurrency on/off ramps functionality.\n */\nexport class RampsController extends BaseController<\n typeof controllerName,\n RampsControllerState,\n RampsControllerMessenger\n> {\n /**\n * Default TTL for cached requests.\n */\n readonly #requestCacheTTL: number;\n\n /**\n * Maximum number of entries in the request cache.\n */\n readonly #requestCacheMaxSize: number;\n\n /**\n * Map of pending requests for deduplication.\n * Key is the cache key, value is the pending request with abort controller.\n */\n readonly #pendingRequests: Map<string, PendingRequest> = new Map();\n\n /**\n * Constructs a new {@link RampsController}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this controller.\n * @param args.state - The desired state with which to initialize this\n * controller. Missing properties will be filled in with defaults.\n * @param args.requestCacheTTL - Time to live for cached requests in milliseconds.\n * @param args.requestCacheMaxSize - Maximum number of entries in the request cache.\n */\n constructor({\n messenger,\n state = {},\n requestCacheTTL = DEFAULT_REQUEST_CACHE_TTL,\n requestCacheMaxSize = DEFAULT_REQUEST_CACHE_MAX_SIZE,\n }: RampsControllerOptions) {\n super({\n messenger,\n metadata: rampsControllerMetadata,\n name: controllerName,\n state: {\n ...getDefaultRampsControllerState(),\n ...state,\n // Always reset requests cache on initialization (non-persisted)\n requests: {},\n },\n });\n\n this.#requestCacheTTL = requestCacheTTL;\n this.#requestCacheMaxSize = requestCacheMaxSize;\n }\n\n /**\n * Executes a request with caching and deduplication.\n *\n * If a request with the same cache key is already in flight, returns the\n * existing promise. If valid cached data exists, returns it without making\n * a new request.\n *\n * @param cacheKey - Unique identifier for this request.\n * @param fetcher - Function that performs the actual fetch. Receives an AbortSignal.\n * @param options - Options for cache behavior.\n * @returns The result of the request.\n */\n async executeRequest<TResult>(\n cacheKey: string,\n fetcher: (signal: AbortSignal) => Promise<TResult>,\n options?: ExecuteRequestOptions,\n ): Promise<TResult> {\n const ttl = options?.ttl ?? this.#requestCacheTTL;\n\n // Check for existing pending request - join it instead of making a duplicate\n const pending = this.#pendingRequests.get(cacheKey);\n if (pending) {\n return pending.promise as Promise<TResult>;\n }\n\n // Check cache validity (unless force refresh)\n if (!options?.forceRefresh) {\n const cached = this.state.requests[cacheKey];\n if (cached && !isCacheExpired(cached, ttl)) {\n return cached.data as TResult;\n }\n }\n\n // Create abort controller for this request\n const abortController = new AbortController();\n const lastFetchedAt = Date.now();\n\n // Update state to loading\n this.#updateRequestState(cacheKey, createLoadingState());\n\n // Create the fetch promise\n const promise = (async (): Promise<TResult> => {\n try {\n const data = await fetcher(abortController.signal);\n\n // Don't update state if aborted\n if (abortController.signal.aborted) {\n throw new Error('Request was aborted');\n }\n\n this.#updateRequestState(\n cacheKey,\n createSuccessState(data as Json, lastFetchedAt),\n );\n return data;\n } catch (error) {\n // Don't update state if aborted\n if (abortController.signal.aborted) {\n throw error;\n }\n\n const errorMessage = (error as Error)?.message;\n\n this.#updateRequestState(\n cacheKey,\n createErrorState(errorMessage ?? 'Unknown error', lastFetchedAt),\n );\n throw error;\n } finally {\n // Only delete if this is still our entry (not replaced by a new request)\n const currentPending = this.#pendingRequests.get(cacheKey);\n if (currentPending?.abortController === abortController) {\n this.#pendingRequests.delete(cacheKey);\n }\n }\n })();\n\n // Store pending request for deduplication\n this.#pendingRequests.set(cacheKey, { promise, abortController });\n\n return promise;\n }\n\n /**\n * Aborts a pending request if one exists.\n *\n * @param cacheKey - The cache key of the request to abort.\n * @returns True if a request was aborted.\n */\n abortRequest(cacheKey: string): boolean {\n const pending = this.#pendingRequests.get(cacheKey);\n if (pending) {\n pending.abortController.abort();\n this.#pendingRequests.delete(cacheKey);\n this.#removeRequestState(cacheKey);\n return true;\n }\n return false;\n }\n\n /**\n * Removes a request state from the cache.\n *\n * @param cacheKey - The cache key to remove.\n */\n #removeRequestState(cacheKey: string): void {\n this.update((state) => {\n const requests = state.requests as unknown as Record<\n string,\n RequestState | undefined\n >;\n delete requests[cacheKey];\n });\n }\n\n /**\n * Gets the state of a specific cached request.\n *\n * @param cacheKey - The cache key to look up.\n * @returns The request state, or undefined if not cached.\n */\n getRequestState(cacheKey: string): RequestState | undefined {\n return this.state.requests[cacheKey];\n }\n\n /**\n * Updates the state for a specific request.\n *\n * @param cacheKey - The cache key.\n * @param requestState - The new state for the request.\n */\n #updateRequestState(cacheKey: string, requestState: RequestState): void {\n const maxSize = this.#requestCacheMaxSize;\n\n this.update((state) => {\n const requests = state.requests as unknown as Record<\n string,\n RequestState | undefined\n >;\n requests[cacheKey] = requestState;\n\n // Evict oldest entries if cache exceeds max size\n const keys = Object.keys(requests);\n\n if (keys.length > maxSize) {\n // Sort by timestamp (oldest first)\n const sortedKeys = keys.sort((a, b) => {\n const aTime = requests[a]?.timestamp ?? 0;\n const bTime = requests[b]?.timestamp ?? 0;\n return aTime - bTime;\n });\n\n // Remove oldest entries until we're under the limit\n const entriesToRemove = keys.length - maxSize;\n for (let i = 0; i < entriesToRemove; i++) {\n const keyToRemove = sortedKeys[i];\n if (keyToRemove) {\n delete requests[keyToRemove];\n }\n }\n }\n });\n }\n\n /**\n * Updates the user's region by fetching geolocation and eligibility.\n * This method calls the RampsService to get the geolocation,\n * then automatically fetches eligibility for that region.\n *\n * @param options - Options for cache behavior.\n * @returns The user region string.\n */\n async updateUserRegion(options?: ExecuteRequestOptions): Promise<string> {\n const cacheKey = createCacheKey('updateUserRegion', []);\n\n const userRegion = await this.executeRequest(\n cacheKey,\n async () => {\n const result = await this.messenger.call('RampsService:getGeolocation');\n return result;\n },\n options,\n );\n\n const normalizedRegion = userRegion\n ? userRegion.toLowerCase().trim()\n : userRegion;\n\n this.update((state) => {\n state.userRegion = normalizedRegion;\n state.tokens = null;\n });\n\n if (normalizedRegion) {\n try {\n await this.updateEligibility(normalizedRegion, options);\n } catch {\n this.update((state) => {\n const currentUserRegion = state.userRegion?.toLowerCase().trim();\n if (currentUserRegion === normalizedRegion) {\n state.eligibility = null;\n state.tokens = null;\n }\n });\n }\n }\n\n return normalizedRegion;\n }\n\n /**\n * Sets the user's region manually (without fetching geolocation).\n * This allows users to override the detected region.\n *\n * @param region - The region code to set (e.g., \"US-CA\").\n * @param options - Options for cache behavior when fetching eligibility.\n * @returns The eligibility information for the region.\n */\n async setUserRegion(\n region: string,\n options?: ExecuteRequestOptions,\n ): Promise<Eligibility> {\n const normalizedRegion = region.toLowerCase().trim();\n\n this.update((state) => {\n state.userRegion = normalizedRegion;\n state.tokens = null;\n });\n\n try {\n return await this.updateEligibility(normalizedRegion, options);\n } catch (error) {\n // Eligibility fetch failed, but user region was successfully set.\n // Don't let eligibility errors prevent user region state from being updated.\n // Clear eligibility state to avoid showing stale data from a previous location.\n // Only clear if the region still matches to avoid race conditions where a newer\n // region change has already succeeded.\n this.update((state) => {\n const currentUserRegion = state.userRegion?.toLowerCase().trim();\n if (currentUserRegion === normalizedRegion) {\n state.eligibility = null;\n state.tokens = null;\n }\n });\n throw error;\n }\n }\n\n /**\n * Sets the user's preferred provider.\n * This allows users to set their preferred ramp provider.\n *\n * @param provider - The provider object to set.\n */\n setPreferredProvider(provider: Provider | null): void {\n this.update((state) => {\n state.preferredProvider = provider;\n });\n }\n\n /**\n * Initializes the controller by fetching the user's region from geolocation.\n * This should be called once at app startup to set up the initial region.\n * After the region is set and eligibility is determined, tokens are fetched\n * and saved to state.\n *\n * @param options - Options for cache behavior.\n * @returns Promise that resolves when initialization is complete.\n */\n async init(options?: ExecuteRequestOptions): Promise<void> {\n const userRegion = await this.updateUserRegion(options).catch(() => {\n // User region fetch failed - error state will be available via selectors\n return null;\n });\n\n if (userRegion) {\n try {\n await this.getTokens(userRegion, 'buy', options);\n } catch {\n // Token fetch failed - error state will be available via selectors\n }\n }\n }\n\n /**\n * Updates the eligibility information for a given region.\n *\n * @param isoCode - The ISO code for the region (e.g., \"us\", \"fr\", \"us-ny\").\n * @param options - Options for cache behavior.\n * @returns The eligibility information.\n */\n async updateEligibility(\n isoCode: string,\n options?: ExecuteRequestOptions,\n ): Promise<Eligibility> {\n const normalizedIsoCode = isoCode.toLowerCase().trim();\n const cacheKey = createCacheKey('updateEligibility', [normalizedIsoCode]);\n\n const eligibility = await this.executeRequest(\n cacheKey,\n async () => {\n return this.messenger.call(\n 'RampsService:getEligibility',\n normalizedIsoCode,\n );\n },\n options,\n );\n\n this.update((state) => {\n const userRegion = state.userRegion?.toLowerCase().trim();\n\n if (userRegion === undefined || userRegion === normalizedIsoCode) {\n state.eligibility = eligibility;\n }\n });\n\n return eligibility;\n }\n\n /**\n * Fetches the list of supported countries for a given ramp action.\n *\n * @param action - The ramp action type ('buy' or 'sell').\n * @param options - Options for cache behavior.\n * @returns An array of countries with their eligibility information.\n */\n async getCountries(\n action: 'buy' | 'sell' = 'buy',\n options?: ExecuteRequestOptions,\n ): Promise<Country[]> {\n const cacheKey = createCacheKey('getCountries', [action]);\n\n return this.executeRequest(\n cacheKey,\n async () => {\n return this.messenger.call('RampsService:getCountries', action);\n },\n options,\n );\n }\n\n /**\n * Fetches the list of available tokens for a given region and action.\n * The tokens are saved in the controller state once fetched.\n *\n * @param region - The region code (e.g., \"us\", \"fr\", \"us-ny\"). If not provided, uses the user's region from controller state.\n * @param action - The ramp action type ('buy' or 'sell').\n * @param options - Options for cache behavior.\n * @returns The tokens response containing topTokens and allTokens.\n */\n async getTokens(\n region?: string,\n action: 'buy' | 'sell' = 'buy',\n options?: ExecuteRequestOptions,\n ): Promise<TokensResponse> {\n const regionToUse = region ?? this.state.userRegion;\n\n if (!regionToUse) {\n throw new Error(\n 'Region is required. Either provide a region parameter or ensure userRegion is set in controller state.',\n );\n }\n\n const normalizedRegion = regionToUse.toLowerCase().trim();\n const cacheKey = createCacheKey('getTokens', [normalizedRegion, action]);\n\n const tokens = await this.executeRequest(\n cacheKey,\n async () => {\n return this.messenger.call(\n 'RampsService:getTokens',\n normalizedRegion,\n action,\n );\n },\n options,\n );\n\n this.update((state) => {\n const userRegion = state.userRegion?.toLowerCase().trim();\n\n if (userRegion === undefined || userRegion === normalizedRegion) {\n state.tokens = tokens;\n }\n });\n\n return tokens;\n }\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
2
2
  import { BaseController } from "@metamask/base-controller";
3
3
  import type { Messenger } from "@metamask/messenger";
4
- import type { Country, Eligibility } from "./RampsService.cjs";
5
- import type { RampsServiceGetGeolocationAction, RampsServiceGetCountriesAction, RampsServiceGetEligibilityAction } from "./RampsService-method-action-types.cjs";
4
+ import type { Country, Eligibility, TokensResponse, Provider } from "./RampsService.cjs";
5
+ import type { RampsServiceGetGeolocationAction, RampsServiceGetCountriesAction, RampsServiceGetEligibilityAction, RampsServiceGetTokensAction } from "./RampsService-method-action-types.cjs";
6
6
  import type { RequestCache as RequestCacheType, RequestState, ExecuteRequestOptions } from "./RequestCache.cjs";
7
7
  /**
8
8
  * The name of the {@link RampsController}, used to namespace the
@@ -19,10 +19,20 @@ export type RampsControllerState = {
19
19
  * Initially set via geolocation fetch, but can be manually changed by the user.
20
20
  */
21
21
  userRegion: string | null;
22
+ /**
23
+ * The user's preferred provider.
24
+ * Can be manually set by the user.
25
+ */
26
+ preferredProvider: Provider | null;
22
27
  /**
23
28
  * Eligibility information for the user's current region.
24
29
  */
25
30
  eligibility: Eligibility | null;
31
+ /**
32
+ * Tokens fetched for the current region and action.
33
+ * Contains topTokens and allTokens arrays.
34
+ */
35
+ tokens: TokensResponse | null;
26
36
  /**
27
37
  * Cache of request states, keyed by cache key.
28
38
  * This stores loading, success, and error states for API requests.
@@ -49,7 +59,7 @@ export type RampsControllerActions = RampsControllerGetStateAction;
49
59
  /**
50
60
  * Actions from other messengers that {@link RampsController} calls.
51
61
  */
52
- type AllowedActions = RampsServiceGetGeolocationAction | RampsServiceGetCountriesAction | RampsServiceGetEligibilityAction;
62
+ type AllowedActions = RampsServiceGetGeolocationAction | RampsServiceGetCountriesAction | RampsServiceGetEligibilityAction | RampsServiceGetTokensAction;
53
63
  /**
54
64
  * Published when the state of {@link RampsController} changes.
55
65
  */
@@ -141,9 +151,18 @@ export declare class RampsController extends BaseController<typeof controllerNam
141
151
  * @returns The eligibility information for the region.
142
152
  */
143
153
  setUserRegion(region: string, options?: ExecuteRequestOptions): Promise<Eligibility>;
154
+ /**
155
+ * Sets the user's preferred provider.
156
+ * This allows users to set their preferred ramp provider.
157
+ *
158
+ * @param provider - The provider object to set.
159
+ */
160
+ setPreferredProvider(provider: Provider | null): void;
144
161
  /**
145
162
  * Initializes the controller by fetching the user's region from geolocation.
146
163
  * This should be called once at app startup to set up the initial region.
164
+ * After the region is set and eligibility is determined, tokens are fetched
165
+ * and saved to state.
147
166
  *
148
167
  * @param options - Options for cache behavior.
149
168
  * @returns Promise that resolves when initialization is complete.
@@ -165,6 +184,16 @@ export declare class RampsController extends BaseController<typeof controllerNam
165
184
  * @returns An array of countries with their eligibility information.
166
185
  */
167
186
  getCountries(action?: 'buy' | 'sell', options?: ExecuteRequestOptions): Promise<Country[]>;
187
+ /**
188
+ * Fetches the list of available tokens for a given region and action.
189
+ * The tokens are saved in the controller state once fetched.
190
+ *
191
+ * @param region - The region code (e.g., "us", "fr", "us-ny"). If not provided, uses the user's region from controller state.
192
+ * @param action - The ramp action type ('buy' or 'sell').
193
+ * @param options - Options for cache behavior.
194
+ * @returns The tokens response containing topTokens and allTokens.
195
+ */
196
+ getTokens(region?: string, action?: 'buy' | 'sell', options?: ExecuteRequestOptions): Promise<TokensResponse>;
168
197
  }
169
198
  export {};
170
199
  //# sourceMappingURL=RampsController.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RampsController.d.cts","sourceRoot":"","sources":["../src/RampsController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RampsController.d.cts","sourceRoot":"","sources":["../src/RampsController.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import type { ControllerGetStateAction, ControllerStateChangeEvent } from "@metamask/base-controller";
2
2
  import { BaseController } from "@metamask/base-controller";
3
3
  import type { Messenger } from "@metamask/messenger";
4
- import type { Country, Eligibility } from "./RampsService.mjs";
5
- import type { RampsServiceGetGeolocationAction, RampsServiceGetCountriesAction, RampsServiceGetEligibilityAction } from "./RampsService-method-action-types.mjs";
4
+ import type { Country, Eligibility, TokensResponse, Provider } from "./RampsService.mjs";
5
+ import type { RampsServiceGetGeolocationAction, RampsServiceGetCountriesAction, RampsServiceGetEligibilityAction, RampsServiceGetTokensAction } from "./RampsService-method-action-types.mjs";
6
6
  import type { RequestCache as RequestCacheType, RequestState, ExecuteRequestOptions } from "./RequestCache.mjs";
7
7
  /**
8
8
  * The name of the {@link RampsController}, used to namespace the
@@ -19,10 +19,20 @@ export type RampsControllerState = {
19
19
  * Initially set via geolocation fetch, but can be manually changed by the user.
20
20
  */
21
21
  userRegion: string | null;
22
+ /**
23
+ * The user's preferred provider.
24
+ * Can be manually set by the user.
25
+ */
26
+ preferredProvider: Provider | null;
22
27
  /**
23
28
  * Eligibility information for the user's current region.
24
29
  */
25
30
  eligibility: Eligibility | null;
31
+ /**
32
+ * Tokens fetched for the current region and action.
33
+ * Contains topTokens and allTokens arrays.
34
+ */
35
+ tokens: TokensResponse | null;
26
36
  /**
27
37
  * Cache of request states, keyed by cache key.
28
38
  * This stores loading, success, and error states for API requests.
@@ -49,7 +59,7 @@ export type RampsControllerActions = RampsControllerGetStateAction;
49
59
  /**
50
60
  * Actions from other messengers that {@link RampsController} calls.
51
61
  */
52
- type AllowedActions = RampsServiceGetGeolocationAction | RampsServiceGetCountriesAction | RampsServiceGetEligibilityAction;
62
+ type AllowedActions = RampsServiceGetGeolocationAction | RampsServiceGetCountriesAction | RampsServiceGetEligibilityAction | RampsServiceGetTokensAction;
53
63
  /**
54
64
  * Published when the state of {@link RampsController} changes.
55
65
  */
@@ -141,9 +151,18 @@ export declare class RampsController extends BaseController<typeof controllerNam
141
151
  * @returns The eligibility information for the region.
142
152
  */
143
153
  setUserRegion(region: string, options?: ExecuteRequestOptions): Promise<Eligibility>;
154
+ /**
155
+ * Sets the user's preferred provider.
156
+ * This allows users to set their preferred ramp provider.
157
+ *
158
+ * @param provider - The provider object to set.
159
+ */
160
+ setPreferredProvider(provider: Provider | null): void;
144
161
  /**
145
162
  * Initializes the controller by fetching the user's region from geolocation.
146
163
  * This should be called once at app startup to set up the initial region.
164
+ * After the region is set and eligibility is determined, tokens are fetched
165
+ * and saved to state.
147
166
  *
148
167
  * @param options - Options for cache behavior.
149
168
  * @returns Promise that resolves when initialization is complete.
@@ -165,6 +184,16 @@ export declare class RampsController extends BaseController<typeof controllerNam
165
184
  * @returns An array of countries with their eligibility information.
166
185
  */
167
186
  getCountries(action?: 'buy' | 'sell', options?: ExecuteRequestOptions): Promise<Country[]>;
187
+ /**
188
+ * Fetches the list of available tokens for a given region and action.
189
+ * The tokens are saved in the controller state once fetched.
190
+ *
191
+ * @param region - The region code (e.g., "us", "fr", "us-ny"). If not provided, uses the user's region from controller state.
192
+ * @param action - The ramp action type ('buy' or 'sell').
193
+ * @param options - Options for cache behavior.
194
+ * @returns The tokens response containing topTokens and allTokens.
195
+ */
196
+ getTokens(region?: string, action?: 'buy' | 'sell', options?: ExecuteRequestOptions): Promise<TokensResponse>;
168
197
  }
169
198
  export {};
170
199
  //# sourceMappingURL=RampsController.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RampsController.d.mts","sourceRoot":"","sources":["../src/RampsController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RampsController.d.mts","sourceRoot":"","sources":["../src/RampsController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,kCAAkC;AACnC,OAAO,EAAE,cAAc,EAAE,kCAAkC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAGrD,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,cAAc,EACd,QAAQ,EACT,2BAAuB;AACxB,OAAO,KAAK,EACV,gCAAgC,EAChC,8BAA8B,EAC9B,gCAAgC,EAChC,2BAA2B,EAC5B,+CAA2C;AAC5C,OAAO,KAAK,EACV,YAAY,IAAI,gBAAgB,EAChC,YAAY,EACZ,qBAAqB,EAEtB,2BAAuB;AAaxB;;;;GAIG;AACH,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAIhD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;OAGG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,EAAE,QAAQ,GAAG,IAAI,CAAC;IACnC;;OAEG;IACH,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,QAAQ,EAAE,gBAAgB,CAAC;CAC5B,CAAC;AAsCF;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,IAAI,oBAAoB,CAQrE;AAID;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG,wBAAwB,CAClE,OAAO,cAAc,EACrB,oBAAoB,CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,6BAA6B,CAAC;AAEnE;;GAEG;AACH,KAAK,cAAc,GACf,gCAAgC,GAChC,8BAA8B,GAC9B,gCAAgC,GAChC,2BAA2B,CAAC;AAEhC;;GAEG;AACH,MAAM,MAAM,+BAA+B,GAAG,0BAA0B,CACtE,OAAO,cAAc,EACrB,oBAAoB,CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,+BAA+B,CAAC;AAEpE;;GAEG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,SAAS,CAC9C,OAAO,cAAc,EACrB,sBAAsB,GAAG,cAAc,EACvC,qBAAqB,GAAG,aAAa,CACtC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,gDAAgD;IAChD,SAAS,EAAE,wBAAwB,CAAC;IACpC,kEAAkE;IAClE,KAAK,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtC,gFAAgF;IAChF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAIF;;GAEG;AACH,qBAAa,eAAgB,SAAQ,cAAc,CACjD,OAAO,cAAc,EACrB,oBAAoB,EACpB,wBAAwB,CACzB;;IAiBC;;;;;;;;;OASG;gBACS,EACV,SAAS,EACT,KAAU,EACV,eAA2C,EAC3C,mBAAoD,GACrD,EAAE,sBAAsB;IAiBzB;;;;;;;;;;;OAWG;IACG,cAAc,CAAC,OAAO,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,EAClD,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,OAAO,CAAC;IAmEnB;;;;;OAKG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IA0BvC;;;;;OAKG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IA2C3D;;;;;;;OAOG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;IAsCxE;;;;;;;OAOG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,WAAW,CAAC;IA2BvB;;;;;OAKG;IACH,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI;IAMrD;;;;;;;;OAQG;IACG,IAAI,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D;;;;;;OAMG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,WAAW,CAAC;IA0BvB;;;;;;OAMG;IACG,YAAY,CAChB,MAAM,GAAE,KAAK,GAAG,MAAc,EAC9B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,OAAO,EAAE,CAAC;IAYrB;;;;;;;;OAQG;IACG,SAAS,CACb,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,GAAE,KAAK,GAAG,MAAc,EAC9B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,cAAc,CAAC;CAkC3B"}
@@ -29,12 +29,24 @@ const rampsControllerMetadata = {
29
29
  includeInStateLogs: true,
30
30
  usedInUi: true,
31
31
  },
32
+ preferredProvider: {
33
+ persist: true,
34
+ includeInDebugSnapshot: true,
35
+ includeInStateLogs: true,
36
+ usedInUi: true,
37
+ },
32
38
  eligibility: {
33
39
  persist: true,
34
40
  includeInDebugSnapshot: true,
35
41
  includeInStateLogs: true,
36
42
  usedInUi: true,
37
43
  },
44
+ tokens: {
45
+ persist: true,
46
+ includeInDebugSnapshot: true,
47
+ includeInStateLogs: true,
48
+ usedInUi: true,
49
+ },
38
50
  requests: {
39
51
  persist: false,
40
52
  includeInDebugSnapshot: true,
@@ -53,7 +65,9 @@ const rampsControllerMetadata = {
53
65
  export function getDefaultRampsControllerState() {
54
66
  return {
55
67
  userRegion: null,
68
+ preferredProvider: null,
56
69
  eligibility: null,
70
+ tokens: null,
57
71
  requests: {},
58
72
  };
59
73
  }
@@ -208,6 +222,7 @@ export class RampsController extends BaseController {
208
222
  : userRegion;
209
223
  this.update((state) => {
210
224
  state.userRegion = normalizedRegion;
225
+ state.tokens = null;
211
226
  });
212
227
  if (normalizedRegion) {
213
228
  try {
@@ -218,6 +233,7 @@ export class RampsController extends BaseController {
218
233
  const currentUserRegion = state.userRegion?.toLowerCase().trim();
219
234
  if (currentUserRegion === normalizedRegion) {
220
235
  state.eligibility = null;
236
+ state.tokens = null;
221
237
  }
222
238
  });
223
239
  }
@@ -236,6 +252,7 @@ export class RampsController extends BaseController {
236
252
  const normalizedRegion = region.toLowerCase().trim();
237
253
  this.update((state) => {
238
254
  state.userRegion = normalizedRegion;
255
+ state.tokens = null;
239
256
  });
240
257
  try {
241
258
  return await this.updateEligibility(normalizedRegion, options);
@@ -250,22 +267,45 @@ export class RampsController extends BaseController {
250
267
  const currentUserRegion = state.userRegion?.toLowerCase().trim();
251
268
  if (currentUserRegion === normalizedRegion) {
252
269
  state.eligibility = null;
270
+ state.tokens = null;
253
271
  }
254
272
  });
255
273
  throw error;
256
274
  }
257
275
  }
276
+ /**
277
+ * Sets the user's preferred provider.
278
+ * This allows users to set their preferred ramp provider.
279
+ *
280
+ * @param provider - The provider object to set.
281
+ */
282
+ setPreferredProvider(provider) {
283
+ this.update((state) => {
284
+ state.preferredProvider = provider;
285
+ });
286
+ }
258
287
  /**
259
288
  * Initializes the controller by fetching the user's region from geolocation.
260
289
  * This should be called once at app startup to set up the initial region.
290
+ * After the region is set and eligibility is determined, tokens are fetched
291
+ * and saved to state.
261
292
  *
262
293
  * @param options - Options for cache behavior.
263
294
  * @returns Promise that resolves when initialization is complete.
264
295
  */
265
296
  async init(options) {
266
- await this.updateUserRegion(options).catch(() => {
297
+ const userRegion = await this.updateUserRegion(options).catch(() => {
267
298
  // User region fetch failed - error state will be available via selectors
299
+ return null;
268
300
  });
301
+ if (userRegion) {
302
+ try {
303
+ await this.getTokens(userRegion, 'buy', options);
304
+ }
305
+ catch {
306
+ // Token fetch failed - error state will be available via selectors
307
+ }
308
+ }
269
309
  }
270
310
  /**
271
311
  * Updates the eligibility information for a given region.
@@ -301,6 +341,33 @@ export class RampsController extends BaseController {
301
341
  return this.messenger.call('RampsService:getCountries', action);
302
342
  }, options);
303
343
  }
344
+ /**
345
+ * Fetches the list of available tokens for a given region and action.
346
+ * The tokens are saved in the controller state once fetched.
347
+ *
348
+ * @param region - The region code (e.g., "us", "fr", "us-ny"). If not provided, uses the user's region from controller state.
349
+ * @param action - The ramp action type ('buy' or 'sell').
350
+ * @param options - Options for cache behavior.
351
+ * @returns The tokens response containing topTokens and allTokens.
352
+ */
353
+ async getTokens(region, action = 'buy', options) {
354
+ const regionToUse = region ?? this.state.userRegion;
355
+ if (!regionToUse) {
356
+ throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
357
+ }
358
+ const normalizedRegion = regionToUse.toLowerCase().trim();
359
+ const cacheKey = createCacheKey('getTokens', [normalizedRegion, action]);
360
+ const tokens = await this.executeRequest(cacheKey, async () => {
361
+ return this.messenger.call('RampsService:getTokens', normalizedRegion, action);
362
+ }, options);
363
+ this.update((state) => {
364
+ const userRegion = state.userRegion?.toLowerCase().trim();
365
+ if (userRegion === undefined || userRegion === normalizedRegion) {
366
+ state.tokens = tokens;
367
+ }
368
+ });
369
+ return tokens;
370
+ }
304
371
  }
305
372
  _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
306
373
  this.update((state) => {