@metamask/ramps-controller 4.0.0 → 5.0.0
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 +36 -1
- package/dist/RampsController.cjs +342 -152
- package/dist/RampsController.cjs.map +1 -1
- package/dist/RampsController.d.cts +121 -24
- package/dist/RampsController.d.cts.map +1 -1
- package/dist/RampsController.d.mts +121 -24
- package/dist/RampsController.d.mts.map +1 -1
- package/dist/RampsController.mjs +343 -153
- package/dist/RampsController.mjs.map +1 -1
- package/dist/RampsService-method-action-types.cjs.map +1 -1
- package/dist/RampsService-method-action-types.d.cts +19 -2
- package/dist/RampsService-method-action-types.d.cts.map +1 -1
- package/dist/RampsService-method-action-types.d.mts +19 -2
- package/dist/RampsService-method-action-types.d.mts.map +1 -1
- package/dist/RampsService-method-action-types.mjs.map +1 -1
- package/dist/RampsService.cjs +81 -13
- package/dist/RampsService.cjs.map +1 -1
- package/dist/RampsService.d.cts +106 -9
- package/dist/RampsService.d.cts.map +1 -1
- package/dist/RampsService.d.mts +106 -9
- package/dist/RampsService.d.mts.map +1 -1
- package/dist/RampsService.mjs +81 -13
- package/dist/RampsService.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [5.0.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Add `hydrateState()` method to fetch providers and tokens for user region ([#7707](https://github.com/MetaMask/core/pull/7707))
|
|
15
|
+
- Add `countries` state to RampsController with 24 hour TTL caching ([#7707](https://github.com/MetaMask/core/pull/7707))
|
|
16
|
+
- Add `SupportedActions` type for `{ buy: boolean; sell: boolean }` support info
|
|
17
|
+
- Add `selectedToken` state and `setSelectedToken()` method to RampsController ([#7734](https://github.com/MetaMask/core/pull/7734))
|
|
18
|
+
- Add `RampsEnvironment.Local` option to RampsService for local development ([#7734](https://github.com/MetaMask/core/pull/7734))
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Reorganize `init()` to only fetch geolocation and countries; remove token and provider fetching ([#7707](https://github.com/MetaMask/core/pull/7707))
|
|
23
|
+
- **BREAKING:** Change `Country.supported` and `State.supported` from `boolean` to `SupportedActions` object. The API now returns buy/sell support info in a single call.
|
|
24
|
+
- **BREAKING:** Remove `action` parameter from `getCountries()`. Countries are no longer fetched separately for buy/sell actions.
|
|
25
|
+
- **BREAKING:** Rename `preferredProvider` to `selectedProvider` and `setPreferredProvider()` to `setSelectedProvider()` in RampsController ([#7734](https://github.com/MetaMask/core/pull/7734))
|
|
26
|
+
- **BREAKING:** Change `getPaymentMethods(options)` to `getPaymentMethods(region, options)` with region as first parameter ([#7734](https://github.com/MetaMask/core/pull/7734))
|
|
27
|
+
|
|
28
|
+
## [4.1.0]
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Add sync trigger methods to RampsController ([#7662](https://github.com/MetaMask/core/pull/7662))
|
|
33
|
+
|
|
34
|
+
- Export `RampAction` type for `'buy' | 'sell'` ramp actions ([#7663](https://github.com/MetaMask/core/pull/7663))
|
|
35
|
+
- Add payment methods support with `getPaymentMethods()` method, `paymentMethods` and `selectedPaymentMethod` state ([#7665](https://github.com/MetaMask/core/pull/7665))
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Evict expired cache entries based on TTL in addition to size-based eviction ([#7674](https://github.com/MetaMask/core/pull/7674))
|
|
40
|
+
|
|
41
|
+
- Update `getTokens()` to use v2 API endpoint and support optional provider parameter ([#7664](https://github.com/MetaMask/core/pull/7664))
|
|
42
|
+
|
|
10
43
|
## [4.0.0]
|
|
11
44
|
|
|
12
45
|
### Added
|
|
@@ -79,7 +112,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
79
112
|
- Add `OnRampService` for interacting with the OnRamp API
|
|
80
113
|
- Add geolocation detection via IP address lookup
|
|
81
114
|
|
|
82
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@
|
|
115
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@5.0.0...HEAD
|
|
116
|
+
[5.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@4.1.0...@metamask/ramps-controller@5.0.0
|
|
117
|
+
[4.1.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@4.0.0...@metamask/ramps-controller@4.1.0
|
|
83
118
|
[4.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@3.0.0...@metamask/ramps-controller@4.0.0
|
|
84
119
|
[3.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@2.1.0...@metamask/ramps-controller@3.0.0
|
|
85
120
|
[2.1.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@2.0.0...@metamask/ramps-controller@2.1.0
|
package/dist/RampsController.cjs
CHANGED
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_removeRequestState, _RampsController_updateRequestState;
|
|
13
|
+
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_updateRequestState;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.RampsController = exports.getDefaultRampsControllerState = exports.controllerName = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
@@ -32,7 +32,13 @@ const rampsControllerMetadata = {
|
|
|
32
32
|
includeInStateLogs: true,
|
|
33
33
|
usedInUi: true,
|
|
34
34
|
},
|
|
35
|
-
|
|
35
|
+
selectedProvider: {
|
|
36
|
+
persist: false,
|
|
37
|
+
includeInDebugSnapshot: true,
|
|
38
|
+
includeInStateLogs: true,
|
|
39
|
+
usedInUi: true,
|
|
40
|
+
},
|
|
41
|
+
countries: {
|
|
36
42
|
persist: true,
|
|
37
43
|
includeInDebugSnapshot: true,
|
|
38
44
|
includeInStateLogs: true,
|
|
@@ -50,6 +56,24 @@ const rampsControllerMetadata = {
|
|
|
50
56
|
includeInStateLogs: true,
|
|
51
57
|
usedInUi: true,
|
|
52
58
|
},
|
|
59
|
+
selectedToken: {
|
|
60
|
+
persist: false,
|
|
61
|
+
includeInDebugSnapshot: true,
|
|
62
|
+
includeInStateLogs: true,
|
|
63
|
+
usedInUi: true,
|
|
64
|
+
},
|
|
65
|
+
paymentMethods: {
|
|
66
|
+
persist: false,
|
|
67
|
+
includeInDebugSnapshot: true,
|
|
68
|
+
includeInStateLogs: true,
|
|
69
|
+
usedInUi: true,
|
|
70
|
+
},
|
|
71
|
+
selectedPaymentMethod: {
|
|
72
|
+
persist: false,
|
|
73
|
+
includeInDebugSnapshot: true,
|
|
74
|
+
includeInStateLogs: true,
|
|
75
|
+
usedInUi: true,
|
|
76
|
+
},
|
|
53
77
|
requests: {
|
|
54
78
|
persist: false,
|
|
55
79
|
includeInDebugSnapshot: true,
|
|
@@ -68,9 +92,13 @@ const rampsControllerMetadata = {
|
|
|
68
92
|
function getDefaultRampsControllerState() {
|
|
69
93
|
return {
|
|
70
94
|
userRegion: null,
|
|
71
|
-
|
|
95
|
+
selectedProvider: null,
|
|
96
|
+
countries: [],
|
|
72
97
|
providers: [],
|
|
73
98
|
tokens: null,
|
|
99
|
+
selectedToken: null,
|
|
100
|
+
paymentMethods: [],
|
|
101
|
+
selectedPaymentMethod: null,
|
|
74
102
|
requests: {},
|
|
75
103
|
};
|
|
76
104
|
}
|
|
@@ -191,7 +219,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
191
219
|
if (pending) {
|
|
192
220
|
return pending.promise;
|
|
193
221
|
}
|
|
194
|
-
// Check cache validity (unless force refresh)
|
|
195
222
|
if (!options?.forceRefresh) {
|
|
196
223
|
const cached = this.state.requests[cacheKey];
|
|
197
224
|
if (cached && !(0, RequestCache_1.isCacheExpired)(cached, ttl)) {
|
|
@@ -260,86 +287,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
260
287
|
getRequestState(cacheKey) {
|
|
261
288
|
return this.state.requests[cacheKey];
|
|
262
289
|
}
|
|
263
|
-
/**
|
|
264
|
-
* Updates the user's region by fetching geolocation.
|
|
265
|
-
* This method calls the RampsService to get the geolocation.
|
|
266
|
-
*
|
|
267
|
-
* @param options - Options for cache behavior.
|
|
268
|
-
* @returns The user region object.
|
|
269
|
-
*/
|
|
270
|
-
async updateUserRegion(options) {
|
|
271
|
-
// If a userRegion already exists and forceRefresh is not requested,
|
|
272
|
-
// return it immediately without fetching geolocation.
|
|
273
|
-
// This ensures that once a region is set (either via geolocation or manual selection),
|
|
274
|
-
// it will not be overwritten by subsequent geolocation fetches.
|
|
275
|
-
if (this.state.userRegion && !options?.forceRefresh) {
|
|
276
|
-
return this.state.userRegion;
|
|
277
|
-
}
|
|
278
|
-
// When forceRefresh is true, clear the existing region, tokens, and providers before fetching
|
|
279
|
-
if (options?.forceRefresh) {
|
|
280
|
-
this.update((state) => {
|
|
281
|
-
state.userRegion = null;
|
|
282
|
-
state.tokens = null;
|
|
283
|
-
state.providers = [];
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
const cacheKey = (0, RequestCache_1.createCacheKey)('updateUserRegion', []);
|
|
287
|
-
const regionCode = await this.executeRequest(cacheKey, async () => {
|
|
288
|
-
const result = await this.messenger.call('RampsService:getGeolocation');
|
|
289
|
-
return result;
|
|
290
|
-
}, options);
|
|
291
|
-
if (!regionCode) {
|
|
292
|
-
this.update((state) => {
|
|
293
|
-
state.userRegion = null;
|
|
294
|
-
state.tokens = null;
|
|
295
|
-
state.providers = [];
|
|
296
|
-
});
|
|
297
|
-
return null;
|
|
298
|
-
}
|
|
299
|
-
const normalizedRegion = regionCode.toLowerCase().trim();
|
|
300
|
-
try {
|
|
301
|
-
const countries = await this.getCountries('buy', options);
|
|
302
|
-
const userRegion = findRegionFromCode(normalizedRegion, countries);
|
|
303
|
-
if (userRegion) {
|
|
304
|
-
this.update((state) => {
|
|
305
|
-
const regionChanged = state.userRegion?.regionCode !== userRegion.regionCode;
|
|
306
|
-
state.userRegion = userRegion;
|
|
307
|
-
// Clear tokens and providers when region changes
|
|
308
|
-
if (regionChanged) {
|
|
309
|
-
state.tokens = null;
|
|
310
|
-
state.providers = [];
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
// Fetch providers for the new region
|
|
314
|
-
if (userRegion.regionCode) {
|
|
315
|
-
try {
|
|
316
|
-
await this.getProviders(userRegion.regionCode, options);
|
|
317
|
-
}
|
|
318
|
-
catch {
|
|
319
|
-
// Provider fetch failed - error state will be available via selectors
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return userRegion;
|
|
323
|
-
}
|
|
324
|
-
// Region not found in countries data
|
|
325
|
-
this.update((state) => {
|
|
326
|
-
state.userRegion = null;
|
|
327
|
-
state.tokens = null;
|
|
328
|
-
state.providers = [];
|
|
329
|
-
});
|
|
330
|
-
return null;
|
|
331
|
-
}
|
|
332
|
-
catch {
|
|
333
|
-
// If countries fetch fails, we can't create a valid UserRegion
|
|
334
|
-
// Return null to indicate we don't have valid country data
|
|
335
|
-
this.update((state) => {
|
|
336
|
-
state.userRegion = null;
|
|
337
|
-
state.tokens = null;
|
|
338
|
-
state.providers = [];
|
|
339
|
-
});
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
290
|
/**
|
|
344
291
|
* Sets the user's region manually (without fetching geolocation).
|
|
345
292
|
* This allows users to override the detected region.
|
|
@@ -351,100 +298,129 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
351
298
|
async setUserRegion(region, options) {
|
|
352
299
|
const normalizedRegion = region.toLowerCase().trim();
|
|
353
300
|
try {
|
|
354
|
-
const countries =
|
|
301
|
+
const { countries } = this.state;
|
|
302
|
+
if (!countries || countries.length === 0) {
|
|
303
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_cleanupState).call(this);
|
|
304
|
+
throw new Error('No countries found. Cannot set user region without valid country information.');
|
|
305
|
+
}
|
|
355
306
|
const userRegion = findRegionFromCode(normalizedRegion, countries);
|
|
356
|
-
if (userRegion) {
|
|
357
|
-
this.
|
|
358
|
-
|
|
307
|
+
if (!userRegion) {
|
|
308
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_cleanupState).call(this);
|
|
309
|
+
throw new Error(`Region "${normalizedRegion}" not found in countries data. Cannot set user region without valid country information.`);
|
|
310
|
+
}
|
|
311
|
+
// Only cleanup state if region is actually changing
|
|
312
|
+
const regionChanged = normalizedRegion !== this.state.userRegion?.regionCode;
|
|
313
|
+
// Set the new region atomically with cleanup to avoid intermediate null state
|
|
314
|
+
this.update((state) => {
|
|
315
|
+
if (regionChanged) {
|
|
316
|
+
state.selectedProvider = null;
|
|
317
|
+
state.selectedToken = null;
|
|
359
318
|
state.tokens = null;
|
|
360
319
|
state.providers = [];
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
await this.getProviders(userRegion.regionCode, options);
|
|
365
|
-
}
|
|
366
|
-
catch {
|
|
367
|
-
// Provider fetch failed - error state will be available via selectors
|
|
320
|
+
state.paymentMethods = [];
|
|
321
|
+
state.selectedPaymentMethod = null;
|
|
368
322
|
}
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
// Region not found in countries data
|
|
372
|
-
this.update((state) => {
|
|
373
|
-
state.userRegion = null;
|
|
374
|
-
state.tokens = null;
|
|
375
|
-
state.providers = [];
|
|
323
|
+
state.userRegion = userRegion;
|
|
376
324
|
});
|
|
377
|
-
|
|
325
|
+
// Only trigger fetches if region changed or if data is missing
|
|
326
|
+
if (regionChanged || !this.state.tokens) {
|
|
327
|
+
this.triggerGetTokens(userRegion.regionCode, 'buy', options);
|
|
328
|
+
}
|
|
329
|
+
if (regionChanged || this.state.providers.length === 0) {
|
|
330
|
+
this.triggerGetProviders(userRegion.regionCode, options);
|
|
331
|
+
}
|
|
332
|
+
return userRegion;
|
|
378
333
|
}
|
|
379
334
|
catch (error) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (error instanceof Error && error.message.includes('not found')) {
|
|
383
|
-
throw error;
|
|
384
|
-
}
|
|
385
|
-
// Countries fetch failed
|
|
386
|
-
this.update((state) => {
|
|
387
|
-
state.userRegion = null;
|
|
388
|
-
state.tokens = null;
|
|
389
|
-
state.providers = [];
|
|
390
|
-
});
|
|
391
|
-
throw new Error('Failed to fetch countries data. Cannot set user region without valid country information.');
|
|
335
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_cleanupState).call(this);
|
|
336
|
+
throw error;
|
|
392
337
|
}
|
|
393
338
|
}
|
|
394
339
|
/**
|
|
395
|
-
* Sets the user's
|
|
396
|
-
*
|
|
340
|
+
* Sets the user's selected provider by ID, or clears the selection.
|
|
341
|
+
* Looks up the provider from the current providers in state and automatically
|
|
342
|
+
* fetches payment methods for that provider.
|
|
397
343
|
*
|
|
398
|
-
* @param
|
|
344
|
+
* @param providerId - The provider ID (e.g., "/providers/moonpay"), or null to clear.
|
|
345
|
+
* @throws If region is not set, providers are not loaded, or provider is not found.
|
|
399
346
|
*/
|
|
400
|
-
|
|
347
|
+
setSelectedProvider(providerId) {
|
|
348
|
+
if (providerId === null) {
|
|
349
|
+
this.update((state) => {
|
|
350
|
+
state.selectedProvider = null;
|
|
351
|
+
state.paymentMethods = [];
|
|
352
|
+
state.selectedPaymentMethod = null;
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
357
|
+
if (!regionCode) {
|
|
358
|
+
throw new Error('Region is required. Cannot set selected provider without valid region information.');
|
|
359
|
+
}
|
|
360
|
+
const { providers } = this.state;
|
|
361
|
+
if (!providers || providers.length === 0) {
|
|
362
|
+
throw new Error('Providers not loaded. Cannot set selected provider before providers are fetched.');
|
|
363
|
+
}
|
|
364
|
+
const provider = providers.find((prov) => prov.id === providerId);
|
|
365
|
+
if (!provider) {
|
|
366
|
+
throw new Error(`Provider with ID "${providerId}" not found in available providers.`);
|
|
367
|
+
}
|
|
401
368
|
this.update((state) => {
|
|
402
|
-
state.
|
|
369
|
+
state.selectedProvider = provider;
|
|
370
|
+
state.paymentMethods = [];
|
|
371
|
+
state.selectedPaymentMethod = null;
|
|
372
|
+
});
|
|
373
|
+
// fetch payment methods for the new provider
|
|
374
|
+
// this is needed because you can change providers without changing the token
|
|
375
|
+
// (getPaymentMethods will use state as its default)
|
|
376
|
+
this.triggerGetPaymentMethods(regionCode, {
|
|
377
|
+
provider: provider.id,
|
|
403
378
|
});
|
|
404
379
|
}
|
|
405
380
|
/**
|
|
406
381
|
* Initializes the controller by fetching the user's region from geolocation.
|
|
407
382
|
* This should be called once at app startup to set up the initial region.
|
|
408
|
-
* After the region is set, tokens are fetched and saved to state.
|
|
409
383
|
*
|
|
410
384
|
* If a userRegion already exists (from persistence or manual selection),
|
|
411
|
-
* this method will skip geolocation fetch and
|
|
385
|
+
* this method will skip geolocation fetch and use the existing region.
|
|
412
386
|
*
|
|
413
387
|
* @param options - Options for cache behavior.
|
|
414
388
|
* @returns Promise that resolves when initialization is complete.
|
|
415
389
|
*/
|
|
416
390
|
async init(options) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
await this.getProviders(userRegion.regionCode, options);
|
|
430
|
-
}
|
|
431
|
-
catch {
|
|
432
|
-
// Provider fetch failed - error state will be available via selectors
|
|
433
|
-
}
|
|
391
|
+
await this.getCountries(options);
|
|
392
|
+
let regionCode = this.state.userRegion?.regionCode;
|
|
393
|
+
regionCode ?? (regionCode = await this.messenger.call('RampsService:getGeolocation'));
|
|
394
|
+
if (!regionCode) {
|
|
395
|
+
throw new Error('Failed to fetch geolocation. Cannot initialize controller without valid region information.');
|
|
396
|
+
}
|
|
397
|
+
await this.setUserRegion(regionCode, options);
|
|
398
|
+
}
|
|
399
|
+
hydrateState(options) {
|
|
400
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
401
|
+
if (!regionCode) {
|
|
402
|
+
throw new Error('Region code is required. Cannot hydrate state without valid region information.');
|
|
434
403
|
}
|
|
404
|
+
this.triggerGetTokens(regionCode, 'buy', options);
|
|
405
|
+
this.triggerGetProviders(regionCode, options);
|
|
435
406
|
}
|
|
436
407
|
/**
|
|
437
|
-
* Fetches the list of supported countries
|
|
408
|
+
* Fetches the list of supported countries.
|
|
409
|
+
* The API returns countries with support information for both buy and sell actions.
|
|
410
|
+
* The countries are saved in the controller state once fetched.
|
|
438
411
|
*
|
|
439
|
-
* @param action - The ramp action type ('buy' or 'sell').
|
|
440
412
|
* @param options - Options for cache behavior.
|
|
441
413
|
* @returns An array of countries.
|
|
442
414
|
*/
|
|
443
|
-
async getCountries(
|
|
444
|
-
const cacheKey = (0, RequestCache_1.createCacheKey)('getCountries', [
|
|
445
|
-
|
|
446
|
-
return this.messenger.call('RampsService:getCountries'
|
|
415
|
+
async getCountries(options) {
|
|
416
|
+
const cacheKey = (0, RequestCache_1.createCacheKey)('getCountries', []);
|
|
417
|
+
const countries = await this.executeRequest(cacheKey, async () => {
|
|
418
|
+
return this.messenger.call('RampsService:getCountries');
|
|
447
419
|
}, options);
|
|
420
|
+
this.update((state) => {
|
|
421
|
+
state.countries = countries;
|
|
422
|
+
});
|
|
423
|
+
return countries;
|
|
448
424
|
}
|
|
449
425
|
/**
|
|
450
426
|
* Fetches the list of available tokens for a given region and action.
|
|
@@ -452,7 +428,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
452
428
|
*
|
|
453
429
|
* @param region - The region code (e.g., "us", "fr", "us-ny"). If not provided, uses the user's region from controller state.
|
|
454
430
|
* @param action - The ramp action type ('buy' or 'sell').
|
|
455
|
-
* @param options - Options for cache behavior.
|
|
431
|
+
* @param options - Options for cache behavior and query filters.
|
|
432
|
+
* @param options.provider - Provider ID(s) to filter by.
|
|
456
433
|
* @returns The tokens response containing topTokens and allTokens.
|
|
457
434
|
*/
|
|
458
435
|
async getTokens(region, action = 'buy', options) {
|
|
@@ -461,9 +438,15 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
461
438
|
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
462
439
|
}
|
|
463
440
|
const normalizedRegion = regionToUse.toLowerCase().trim();
|
|
464
|
-
const cacheKey = (0, RequestCache_1.createCacheKey)('getTokens', [
|
|
441
|
+
const cacheKey = (0, RequestCache_1.createCacheKey)('getTokens', [
|
|
442
|
+
normalizedRegion,
|
|
443
|
+
action,
|
|
444
|
+
options?.provider,
|
|
445
|
+
]);
|
|
465
446
|
const tokens = await this.executeRequest(cacheKey, async () => {
|
|
466
|
-
return this.messenger.call('RampsService:getTokens', normalizedRegion, action
|
|
447
|
+
return this.messenger.call('RampsService:getTokens', normalizedRegion, action, {
|
|
448
|
+
provider: options?.provider,
|
|
449
|
+
});
|
|
467
450
|
}, options);
|
|
468
451
|
this.update((state) => {
|
|
469
452
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -473,6 +456,45 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
473
456
|
});
|
|
474
457
|
return tokens;
|
|
475
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Sets the user's selected token by asset ID.
|
|
461
|
+
* Looks up the token from the current tokens in state and automatically
|
|
462
|
+
* fetches payment methods for that token.
|
|
463
|
+
*
|
|
464
|
+
* @param assetId - The asset identifier in CAIP-19 format (e.g., "eip155:1/erc20:0x..."), or undefined to clear.
|
|
465
|
+
* @throws If region is not set, tokens are not loaded, or token is not found.
|
|
466
|
+
*/
|
|
467
|
+
setSelectedToken(assetId) {
|
|
468
|
+
if (!assetId) {
|
|
469
|
+
this.update((state) => {
|
|
470
|
+
state.selectedToken = null;
|
|
471
|
+
state.paymentMethods = [];
|
|
472
|
+
state.selectedPaymentMethod = null;
|
|
473
|
+
});
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
477
|
+
if (!regionCode) {
|
|
478
|
+
throw new Error('Region is required. Cannot set selected token without valid region information.');
|
|
479
|
+
}
|
|
480
|
+
const { tokens } = this.state;
|
|
481
|
+
if (!tokens) {
|
|
482
|
+
throw new Error('Tokens not loaded. Cannot set selected token before tokens are fetched.');
|
|
483
|
+
}
|
|
484
|
+
const token = tokens.allTokens.find((tok) => tok.assetId === assetId) ??
|
|
485
|
+
tokens.topTokens.find((tok) => tok.assetId === assetId);
|
|
486
|
+
if (!token) {
|
|
487
|
+
throw new Error(`Token with asset ID "${assetId}" not found in available tokens.`);
|
|
488
|
+
}
|
|
489
|
+
this.update((state) => {
|
|
490
|
+
state.selectedToken = token;
|
|
491
|
+
state.paymentMethods = [];
|
|
492
|
+
state.selectedPaymentMethod = null;
|
|
493
|
+
});
|
|
494
|
+
this.triggerGetPaymentMethods(regionCode, {
|
|
495
|
+
assetId: token.assetId,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
476
498
|
/**
|
|
477
499
|
* Fetches the list of providers for a given region.
|
|
478
500
|
* The providers are saved in the controller state once fetched.
|
|
@@ -514,6 +536,152 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
514
536
|
});
|
|
515
537
|
return { providers };
|
|
516
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Fetches the list of payment methods for a given context.
|
|
541
|
+
* The payment methods are saved in the controller state once fetched.
|
|
542
|
+
*
|
|
543
|
+
* @param region - User's region code (e.g. "fr", "us-ny").
|
|
544
|
+
* @param options - Query parameters for filtering payment methods.
|
|
545
|
+
* @param options.fiat - Fiat currency code (e.g., "usd"). If not provided, uses the user's region currency.
|
|
546
|
+
* @param options.assetId - CAIP-19 cryptocurrency identifier.
|
|
547
|
+
* @param options.provider - Provider ID path.
|
|
548
|
+
* @returns The payment methods response containing payments array.
|
|
549
|
+
*/
|
|
550
|
+
async getPaymentMethods(region, options) {
|
|
551
|
+
const regionCode = region ?? this.state.userRegion?.regionCode ?? null;
|
|
552
|
+
const fiatToUse = options?.fiat ?? this.state.userRegion?.country?.currency ?? null;
|
|
553
|
+
const assetIdToUse = options?.assetId ?? this.state.selectedToken?.assetId ?? '';
|
|
554
|
+
const providerToUse = options?.provider ?? this.state.selectedProvider?.id ?? '';
|
|
555
|
+
if (!regionCode) {
|
|
556
|
+
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
557
|
+
}
|
|
558
|
+
if (!fiatToUse) {
|
|
559
|
+
throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
|
|
560
|
+
}
|
|
561
|
+
const normalizedRegion = regionCode.toLowerCase().trim();
|
|
562
|
+
const normalizedFiat = fiatToUse.toLowerCase().trim();
|
|
563
|
+
const cacheKey = (0, RequestCache_1.createCacheKey)('getPaymentMethods', [
|
|
564
|
+
normalizedRegion,
|
|
565
|
+
normalizedFiat,
|
|
566
|
+
assetIdToUse,
|
|
567
|
+
providerToUse,
|
|
568
|
+
]);
|
|
569
|
+
const response = await this.executeRequest(cacheKey, async () => {
|
|
570
|
+
return this.messenger.call('RampsService:getPaymentMethods', {
|
|
571
|
+
region: normalizedRegion,
|
|
572
|
+
fiat: normalizedFiat,
|
|
573
|
+
assetId: assetIdToUse,
|
|
574
|
+
provider: providerToUse,
|
|
575
|
+
});
|
|
576
|
+
}, options);
|
|
577
|
+
this.update((state) => {
|
|
578
|
+
const currentAssetId = state.selectedToken?.assetId ?? '';
|
|
579
|
+
const currentProviderId = state.selectedProvider?.id ?? '';
|
|
580
|
+
const tokenSelectionUnchanged = assetIdToUse === currentAssetId;
|
|
581
|
+
const providerSelectionUnchanged = providerToUse === currentProviderId;
|
|
582
|
+
// this is a race condition check to ensure that the selected token and provider in state are the same as the tokens we're requesting for
|
|
583
|
+
// ex: if the user rapidly changes the token or provider, the in-flight payment methods might not be valid
|
|
584
|
+
// so this check will ensure that the payment methods are still valid for the token and provider that were requested
|
|
585
|
+
if (tokenSelectionUnchanged && providerSelectionUnchanged) {
|
|
586
|
+
state.paymentMethods = response.payments;
|
|
587
|
+
// this will auto-select the first payment method if the selected payment method is not in the new payment methods
|
|
588
|
+
const currentSelectionStillValid = response.payments.some((pm) => pm.id === state.selectedPaymentMethod?.id);
|
|
589
|
+
if (!currentSelectionStillValid) {
|
|
590
|
+
state.selectedPaymentMethod = response.payments[0] ?? null;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
return response;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Sets the user's selected payment method by ID.
|
|
598
|
+
* Looks up the payment method from the current payment methods in state.
|
|
599
|
+
*
|
|
600
|
+
* @param paymentMethodId - The payment method ID (e.g., "/payments/debit-credit-card"), or null to clear.
|
|
601
|
+
* @throws If payment methods are not loaded or payment method is not found.
|
|
602
|
+
*/
|
|
603
|
+
setSelectedPaymentMethod(paymentMethodId) {
|
|
604
|
+
if (!paymentMethodId) {
|
|
605
|
+
this.update((state) => {
|
|
606
|
+
state.selectedPaymentMethod = null;
|
|
607
|
+
});
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
const { paymentMethods } = this.state;
|
|
611
|
+
if (!paymentMethods || paymentMethods.length === 0) {
|
|
612
|
+
throw new Error('Payment methods not loaded. Cannot set selected payment method before payment methods are fetched.');
|
|
613
|
+
}
|
|
614
|
+
const paymentMethod = paymentMethods.find((pm) => pm.id === paymentMethodId);
|
|
615
|
+
if (!paymentMethod) {
|
|
616
|
+
throw new Error(`Payment method with ID "${paymentMethodId}" not found in available payment methods.`);
|
|
617
|
+
}
|
|
618
|
+
this.update((state) => {
|
|
619
|
+
state.selectedPaymentMethod = paymentMethod;
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
// ============================================================
|
|
623
|
+
// Sync Trigger Methods
|
|
624
|
+
// These fire-and-forget methods are for use in React effects.
|
|
625
|
+
// Errors are stored in state and available via selectors.
|
|
626
|
+
// ============================================================
|
|
627
|
+
/**
|
|
628
|
+
* Triggers setting the user region without throwing.
|
|
629
|
+
*
|
|
630
|
+
* @param region - The region code to set (e.g., "US-CA").
|
|
631
|
+
* @param options - Options for cache behavior.
|
|
632
|
+
*/
|
|
633
|
+
triggerSetUserRegion(region, options) {
|
|
634
|
+
this.setUserRegion(region, options).catch(() => {
|
|
635
|
+
// Error stored in state
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Triggers fetching countries without throwing.
|
|
640
|
+
*
|
|
641
|
+
* @param options - Options for cache behavior.
|
|
642
|
+
*/
|
|
643
|
+
triggerGetCountries(options) {
|
|
644
|
+
this.getCountries(options).catch(() => {
|
|
645
|
+
// Error stored in state
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Triggers fetching tokens without throwing.
|
|
650
|
+
*
|
|
651
|
+
* @param region - The region code. If not provided, uses userRegion from state.
|
|
652
|
+
* @param action - The ramp action type ('buy' or 'sell').
|
|
653
|
+
* @param options - Options for cache behavior.
|
|
654
|
+
*/
|
|
655
|
+
triggerGetTokens(region, action = 'buy', options) {
|
|
656
|
+
this.getTokens(region, action, options).catch(() => {
|
|
657
|
+
// Error stored in state
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Triggers fetching providers without throwing.
|
|
662
|
+
*
|
|
663
|
+
* @param region - The region code. If not provided, uses userRegion from state.
|
|
664
|
+
* @param options - Options for cache behavior and query filters.
|
|
665
|
+
*/
|
|
666
|
+
triggerGetProviders(region, options) {
|
|
667
|
+
this.getProviders(region, options).catch(() => {
|
|
668
|
+
// Error stored in state
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Triggers fetching payment methods without throwing.
|
|
673
|
+
*
|
|
674
|
+
* @param region - User's region code (e.g., "us", "fr", "us-ny").
|
|
675
|
+
* @param options - Query parameters for filtering payment methods.
|
|
676
|
+
* @param options.fiat - Fiat currency code. If not provided, uses userRegion currency.
|
|
677
|
+
* @param options.assetId - CAIP-19 cryptocurrency identifier.
|
|
678
|
+
* @param options.provider - Provider ID path.
|
|
679
|
+
*/
|
|
680
|
+
triggerGetPaymentMethods(region, options) {
|
|
681
|
+
this.getPaymentMethods(region, options).catch(() => {
|
|
682
|
+
// Error stored in state
|
|
683
|
+
});
|
|
684
|
+
}
|
|
517
685
|
}
|
|
518
686
|
exports.RampsController = RampsController;
|
|
519
687
|
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
|
|
@@ -521,22 +689,44 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
521
689
|
const requests = state.requests;
|
|
522
690
|
delete requests[cacheKey];
|
|
523
691
|
});
|
|
692
|
+
}, _RampsController_cleanupState = function _RampsController_cleanupState() {
|
|
693
|
+
this.update((state) => {
|
|
694
|
+
state.userRegion = null;
|
|
695
|
+
state.selectedProvider = null;
|
|
696
|
+
state.selectedToken = null;
|
|
697
|
+
state.tokens = null;
|
|
698
|
+
state.providers = [];
|
|
699
|
+
state.paymentMethods = [];
|
|
700
|
+
state.selectedPaymentMethod = null;
|
|
701
|
+
});
|
|
524
702
|
}, _RampsController_updateRequestState = function _RampsController_updateRequestState(cacheKey, requestState) {
|
|
525
703
|
const maxSize = __classPrivateFieldGet(this, _RampsController_requestCacheMaxSize, "f");
|
|
704
|
+
const ttl = __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
|
|
526
705
|
this.update((state) => {
|
|
527
706
|
const requests = state.requests;
|
|
528
707
|
requests[cacheKey] = requestState;
|
|
529
|
-
// Evict
|
|
708
|
+
// Evict expired entries based on TTL
|
|
709
|
+
// Only evict SUCCESS states that have exceeded their TTL
|
|
530
710
|
const keys = Object.keys(requests);
|
|
531
|
-
|
|
711
|
+
for (const key of keys) {
|
|
712
|
+
const entry = requests[key];
|
|
713
|
+
if (entry &&
|
|
714
|
+
entry.status === RequestCache_1.RequestStatus.SUCCESS &&
|
|
715
|
+
(0, RequestCache_1.isCacheExpired)(entry, ttl)) {
|
|
716
|
+
delete requests[key];
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
// Evict oldest entries if cache still exceeds max size
|
|
720
|
+
const remainingKeys = Object.keys(requests);
|
|
721
|
+
if (remainingKeys.length > maxSize) {
|
|
532
722
|
// Sort by timestamp (oldest first)
|
|
533
|
-
const sortedKeys =
|
|
723
|
+
const sortedKeys = remainingKeys.sort((a, b) => {
|
|
534
724
|
const aTime = requests[a]?.timestamp ?? 0;
|
|
535
725
|
const bTime = requests[b]?.timestamp ?? 0;
|
|
536
726
|
return aTime - bTime;
|
|
537
727
|
});
|
|
538
728
|
// Remove oldest entries until we're under the limit
|
|
539
|
-
const entriesToRemove =
|
|
729
|
+
const entriesToRemove = remainingKeys.length - maxSize;
|
|
540
730
|
for (let i = 0; i < entriesToRemove; i++) {
|
|
541
731
|
const keyToRemove = sortedKeys[i];
|
|
542
732
|
if (keyToRemove) {
|