@metamask/ramps-controller 5.1.0 → 6.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 +8 -1
- package/dist/RampsController.cjs +230 -190
- package/dist/RampsController.cjs.map +1 -1
- package/dist/RampsController.d.cts +52 -106
- package/dist/RampsController.d.cts.map +1 -1
- package/dist/RampsController.d.mts +52 -106
- package/dist/RampsController.d.mts.map +1 -1
- package/dist/RampsController.mjs +229 -189
- package/dist/RampsController.mjs.map +1 -1
- package/dist/RequestCache.cjs.map +1 -1
- package/dist/RequestCache.d.cts +11 -0
- package/dist/RequestCache.d.cts.map +1 -1
- package/dist/RequestCache.d.mts +11 -0
- package/dist/RequestCache.d.mts.map +1 -1
- package/dist/RequestCache.mjs.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/RampsController.cjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
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");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
2
7
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
8
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
9
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
12
|
};
|
|
8
|
-
var
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
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
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_updateRequestState;
|
|
13
|
+
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_pendingResourceCount, _RampsController_clearPendingResourceCountForDependentResources, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_fireAndForget, _RampsController_updateResourceField, _RampsController_setResourceLoading, _RampsController_setResourceError, _RampsController_updateRequestState;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.RampsController = exports.getDefaultRampsControllerState = exports.controllerName = void 0;
|
|
15
|
+
exports.RampsController = exports.getDefaultRampsControllerState = exports.RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS = exports.controllerName = void 0;
|
|
16
16
|
const base_controller_1 = require("@metamask/base-controller");
|
|
17
17
|
const RequestCache_1 = require("./RequestCache.cjs");
|
|
18
18
|
// === GENERAL ===
|
|
@@ -22,6 +22,19 @@ const RequestCache_1 = require("./RequestCache.cjs");
|
|
|
22
22
|
* when composed with other controllers.
|
|
23
23
|
*/
|
|
24
24
|
exports.controllerName = 'RampsController';
|
|
25
|
+
/**
|
|
26
|
+
* RampsService action types that RampsController calls via the messenger.
|
|
27
|
+
* Any host (e.g. mobile) that creates a RampsController messenger must delegate
|
|
28
|
+
* these actions from the root messenger so the controller can function.
|
|
29
|
+
*/
|
|
30
|
+
exports.RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS = [
|
|
31
|
+
'RampsService:getGeolocation',
|
|
32
|
+
'RampsService:getCountries',
|
|
33
|
+
'RampsService:getTokens',
|
|
34
|
+
'RampsService:getProviders',
|
|
35
|
+
'RampsService:getPaymentMethods',
|
|
36
|
+
'RampsService:getQuotes',
|
|
37
|
+
];
|
|
25
38
|
/**
|
|
26
39
|
* Default TTL for quotes requests (15 seconds).
|
|
27
40
|
* Quotes are time-sensitive and should have a shorter cache duration.
|
|
@@ -37,12 +50,6 @@ const rampsControllerMetadata = {
|
|
|
37
50
|
includeInStateLogs: true,
|
|
38
51
|
usedInUi: true,
|
|
39
52
|
},
|
|
40
|
-
selectedProvider: {
|
|
41
|
-
persist: false,
|
|
42
|
-
includeInDebugSnapshot: true,
|
|
43
|
-
includeInStateLogs: true,
|
|
44
|
-
usedInUi: true,
|
|
45
|
-
},
|
|
46
53
|
countries: {
|
|
47
54
|
persist: true,
|
|
48
55
|
includeInDebugSnapshot: true,
|
|
@@ -61,24 +68,12 @@ const rampsControllerMetadata = {
|
|
|
61
68
|
includeInStateLogs: true,
|
|
62
69
|
usedInUi: true,
|
|
63
70
|
},
|
|
64
|
-
selectedToken: {
|
|
65
|
-
persist: false,
|
|
66
|
-
includeInDebugSnapshot: true,
|
|
67
|
-
includeInStateLogs: true,
|
|
68
|
-
usedInUi: true,
|
|
69
|
-
},
|
|
70
71
|
paymentMethods: {
|
|
71
72
|
persist: false,
|
|
72
73
|
includeInDebugSnapshot: true,
|
|
73
74
|
includeInStateLogs: true,
|
|
74
75
|
usedInUi: true,
|
|
75
76
|
},
|
|
76
|
-
selectedPaymentMethod: {
|
|
77
|
-
persist: false,
|
|
78
|
-
includeInDebugSnapshot: true,
|
|
79
|
-
includeInStateLogs: true,
|
|
80
|
-
usedInUi: true,
|
|
81
|
-
},
|
|
82
77
|
quotes: {
|
|
83
78
|
persist: false,
|
|
84
79
|
includeInDebugSnapshot: true,
|
|
@@ -92,6 +87,23 @@ const rampsControllerMetadata = {
|
|
|
92
87
|
usedInUi: true,
|
|
93
88
|
},
|
|
94
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Creates a default resource state object.
|
|
92
|
+
*
|
|
93
|
+
* @template TData - The type of the resource data.
|
|
94
|
+
* @template TSelected - The type of the selected item.
|
|
95
|
+
* @param data - The initial data value.
|
|
96
|
+
* @param selected - The initial selected value.
|
|
97
|
+
* @returns A ResourceState object with default loading and error values.
|
|
98
|
+
*/
|
|
99
|
+
function createDefaultResourceState(data, selected = null) {
|
|
100
|
+
return {
|
|
101
|
+
data,
|
|
102
|
+
selected,
|
|
103
|
+
isLoading: false,
|
|
104
|
+
error: null,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
95
107
|
/**
|
|
96
108
|
* Constructs the default {@link RampsController} state. This allows
|
|
97
109
|
* consumers to provide a partial state object when initializing the controller
|
|
@@ -103,18 +115,43 @@ const rampsControllerMetadata = {
|
|
|
103
115
|
function getDefaultRampsControllerState() {
|
|
104
116
|
return {
|
|
105
117
|
userRegion: null,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
paymentMethods: [],
|
|
112
|
-
selectedPaymentMethod: null,
|
|
113
|
-
quotes: null,
|
|
118
|
+
countries: createDefaultResourceState([]),
|
|
119
|
+
providers: createDefaultResourceState([], null),
|
|
120
|
+
tokens: createDefaultResourceState(null, null),
|
|
121
|
+
paymentMethods: createDefaultResourceState([], null),
|
|
122
|
+
quotes: createDefaultResourceState(null),
|
|
114
123
|
requests: {},
|
|
115
124
|
};
|
|
116
125
|
}
|
|
117
126
|
exports.getDefaultRampsControllerState = getDefaultRampsControllerState;
|
|
127
|
+
/**
|
|
128
|
+
* Resets region-dependent resources (userRegion, providers, tokens, paymentMethods, quotes).
|
|
129
|
+
* Mutates state in place; use from within controller update() for atomic updates.
|
|
130
|
+
*
|
|
131
|
+
* @param state - The state object to mutate.
|
|
132
|
+
* @param options - Options for the reset.
|
|
133
|
+
* @param options.clearUserRegionData - When true, sets userRegion to null (e.g. for full cleanup).
|
|
134
|
+
*/
|
|
135
|
+
function resetDependentResources(state, options) {
|
|
136
|
+
if (options?.clearUserRegionData) {
|
|
137
|
+
state.userRegion = null;
|
|
138
|
+
}
|
|
139
|
+
state.providers.selected = null;
|
|
140
|
+
state.providers.data = [];
|
|
141
|
+
state.providers.isLoading = false;
|
|
142
|
+
state.providers.error = null;
|
|
143
|
+
state.tokens.selected = null;
|
|
144
|
+
state.tokens.data = null;
|
|
145
|
+
state.tokens.isLoading = false;
|
|
146
|
+
state.tokens.error = null;
|
|
147
|
+
state.paymentMethods.data = [];
|
|
148
|
+
state.paymentMethods.selected = null;
|
|
149
|
+
state.paymentMethods.isLoading = false;
|
|
150
|
+
state.paymentMethods.error = null;
|
|
151
|
+
state.quotes.data = null;
|
|
152
|
+
state.quotes.isLoading = false;
|
|
153
|
+
state.quotes.error = null;
|
|
154
|
+
}
|
|
118
155
|
// === HELPER FUNCTIONS ===
|
|
119
156
|
/**
|
|
120
157
|
* Finds a country and state from a region code string.
|
|
@@ -173,6 +210,15 @@ function findRegionFromCode(regionCode, countries) {
|
|
|
173
210
|
* Manages cryptocurrency on/off ramps functionality.
|
|
174
211
|
*/
|
|
175
212
|
class RampsController extends base_controller_1.BaseController {
|
|
213
|
+
/**
|
|
214
|
+
* Clears the pending resource count map. Used only in tests to exercise the
|
|
215
|
+
* defensive path when get() returns undefined in the finally block.
|
|
216
|
+
*
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
clearPendingResourceCountForTest() {
|
|
220
|
+
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").clear();
|
|
221
|
+
}
|
|
176
222
|
/**
|
|
177
223
|
* Constructs a new {@link RampsController}.
|
|
178
224
|
*
|
|
@@ -209,6 +255,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
209
255
|
* Key is the cache key, value is the pending request with abort controller.
|
|
210
256
|
*/
|
|
211
257
|
_RampsController_pendingRequests.set(this, new Map());
|
|
258
|
+
/**
|
|
259
|
+
* Count of in-flight requests per resource type.
|
|
260
|
+
* Used so isLoading is only cleared when the last request for that resource finishes.
|
|
261
|
+
*/
|
|
262
|
+
_RampsController_pendingResourceCount.set(this, new Map());
|
|
212
263
|
__classPrivateFieldSet(this, _RampsController_requestCacheTTL, requestCacheTTL, "f");
|
|
213
264
|
__classPrivateFieldSet(this, _RampsController_requestCacheMaxSize, requestCacheMaxSize, "f");
|
|
214
265
|
}
|
|
@@ -240,8 +291,18 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
240
291
|
// Create abort controller for this request
|
|
241
292
|
const abortController = new AbortController();
|
|
242
293
|
const lastFetchedAt = Date.now();
|
|
294
|
+
const { resourceType } = options ?? {};
|
|
243
295
|
// Update state to loading
|
|
244
296
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createLoadingState)());
|
|
297
|
+
// Set resource-level loading state (only on cache miss). Ref-count so concurrent
|
|
298
|
+
// requests for the same resource type (different cache keys) keep isLoading true.
|
|
299
|
+
if (resourceType) {
|
|
300
|
+
const count = __classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").get(resourceType) ?? 0;
|
|
301
|
+
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").set(resourceType, count + 1);
|
|
302
|
+
if (count === 0) {
|
|
303
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceLoading).call(this, resourceType, true);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
245
306
|
// Create the fetch promise
|
|
246
307
|
const promise = (async () => {
|
|
247
308
|
try {
|
|
@@ -251,6 +312,15 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
251
312
|
throw new Error('Request was aborted');
|
|
252
313
|
}
|
|
253
314
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createSuccessState)(data, lastFetchedAt));
|
|
315
|
+
if (resourceType) {
|
|
316
|
+
// We need the extra logic because there are two situations where we’re allowed to clear the error:
|
|
317
|
+
// No callback → always clear
|
|
318
|
+
// Callback present → clear only when isResultCurrent() returns true.
|
|
319
|
+
const isCurrent = !options?.isResultCurrent || options.isResultCurrent();
|
|
320
|
+
if (isCurrent) {
|
|
321
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceError).call(this, resourceType, null);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
254
324
|
return data;
|
|
255
325
|
}
|
|
256
326
|
catch (error) {
|
|
@@ -258,8 +328,14 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
258
328
|
if (abortController.signal.aborted) {
|
|
259
329
|
throw error;
|
|
260
330
|
}
|
|
261
|
-
const errorMessage = error?.message;
|
|
262
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createErrorState)(errorMessage
|
|
331
|
+
const errorMessage = error?.message ?? 'Unknown error';
|
|
332
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createErrorState)(errorMessage, lastFetchedAt));
|
|
333
|
+
if (resourceType) {
|
|
334
|
+
const isCurrent = !options?.isResultCurrent || options.isResultCurrent();
|
|
335
|
+
if (isCurrent) {
|
|
336
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceError).call(this, resourceType, errorMessage);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
263
339
|
throw error;
|
|
264
340
|
}
|
|
265
341
|
finally {
|
|
@@ -268,6 +344,18 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
268
344
|
if (currentPending?.abortController === abortController) {
|
|
269
345
|
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
|
|
270
346
|
}
|
|
347
|
+
// Clear resource-level loading state only when no requests for this resource remain
|
|
348
|
+
if (resourceType) {
|
|
349
|
+
const count = __classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").get(resourceType) ?? 0;
|
|
350
|
+
const next = Math.max(0, count - 1);
|
|
351
|
+
if (next === 0) {
|
|
352
|
+
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").delete(resourceType);
|
|
353
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceLoading).call(this, resourceType, false);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").set(resourceType, next);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
271
359
|
}
|
|
272
360
|
})();
|
|
273
361
|
// Store pending request for deduplication
|
|
@@ -310,37 +398,40 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
310
398
|
async setUserRegion(region, options) {
|
|
311
399
|
const normalizedRegion = region.toLowerCase().trim();
|
|
312
400
|
try {
|
|
313
|
-
const
|
|
314
|
-
if (!
|
|
401
|
+
const countriesData = this.state.countries.data;
|
|
402
|
+
if (!countriesData || countriesData.length === 0) {
|
|
315
403
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_cleanupState).call(this);
|
|
316
404
|
throw new Error('No countries found. Cannot set user region without valid country information.');
|
|
317
405
|
}
|
|
318
|
-
const userRegion = findRegionFromCode(normalizedRegion,
|
|
406
|
+
const userRegion = findRegionFromCode(normalizedRegion, countriesData);
|
|
319
407
|
if (!userRegion) {
|
|
320
408
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_cleanupState).call(this);
|
|
321
409
|
throw new Error(`Region "${normalizedRegion}" not found in countries data. Cannot set user region without valid country information.`);
|
|
322
410
|
}
|
|
323
|
-
// Only cleanup state if region is actually changing
|
|
324
411
|
const regionChanged = normalizedRegion !== this.state.userRegion?.regionCode;
|
|
325
|
-
|
|
412
|
+
const needsRefetch = regionChanged ||
|
|
413
|
+
!this.state.tokens.data ||
|
|
414
|
+
this.state.providers.data.length === 0;
|
|
415
|
+
if (regionChanged) {
|
|
416
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
417
|
+
}
|
|
326
418
|
this.update((state) => {
|
|
327
419
|
if (regionChanged) {
|
|
328
|
-
state
|
|
329
|
-
state.selectedToken = null;
|
|
330
|
-
state.tokens = null;
|
|
331
|
-
state.providers = [];
|
|
332
|
-
state.paymentMethods = [];
|
|
333
|
-
state.selectedPaymentMethod = null;
|
|
334
|
-
state.quotes = null;
|
|
420
|
+
resetDependentResources(state);
|
|
335
421
|
}
|
|
336
422
|
state.userRegion = userRegion;
|
|
337
423
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
this.
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
this.
|
|
424
|
+
if (needsRefetch) {
|
|
425
|
+
const refetchPromises = [];
|
|
426
|
+
if (regionChanged || !this.state.tokens.data) {
|
|
427
|
+
refetchPromises.push(this.getTokens(userRegion.regionCode, 'buy', options));
|
|
428
|
+
}
|
|
429
|
+
if (regionChanged || this.state.providers.data.length === 0) {
|
|
430
|
+
refetchPromises.push(this.getProviders(userRegion.regionCode, options));
|
|
431
|
+
}
|
|
432
|
+
if (refetchPromises.length > 0) {
|
|
433
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, Promise.all(refetchPromises));
|
|
434
|
+
}
|
|
344
435
|
}
|
|
345
436
|
return userRegion;
|
|
346
437
|
}
|
|
@@ -360,9 +451,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
360
451
|
setSelectedProvider(providerId) {
|
|
361
452
|
if (providerId === null) {
|
|
362
453
|
this.update((state) => {
|
|
363
|
-
state.
|
|
364
|
-
state.paymentMethods = [];
|
|
365
|
-
state.
|
|
454
|
+
state.providers.selected = null;
|
|
455
|
+
state.paymentMethods.data = [];
|
|
456
|
+
state.paymentMethods.selected = null;
|
|
366
457
|
});
|
|
367
458
|
return;
|
|
368
459
|
}
|
|
@@ -370,7 +461,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
370
461
|
if (!regionCode) {
|
|
371
462
|
throw new Error('Region is required. Cannot set selected provider without valid region information.');
|
|
372
463
|
}
|
|
373
|
-
const
|
|
464
|
+
const providers = this.state.providers.data;
|
|
374
465
|
if (!providers || providers.length === 0) {
|
|
375
466
|
throw new Error('Providers not loaded. Cannot set selected provider before providers are fetched.');
|
|
376
467
|
}
|
|
@@ -379,16 +470,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
379
470
|
throw new Error(`Provider with ID "${providerId}" not found in available providers.`);
|
|
380
471
|
}
|
|
381
472
|
this.update((state) => {
|
|
382
|
-
state.
|
|
383
|
-
state.paymentMethods = [];
|
|
384
|
-
state.
|
|
385
|
-
});
|
|
386
|
-
// fetch payment methods for the new provider
|
|
387
|
-
// this is needed because you can change providers without changing the token
|
|
388
|
-
// (getPaymentMethods will use state as its default)
|
|
389
|
-
this.triggerGetPaymentMethods(regionCode, {
|
|
390
|
-
provider: provider.id,
|
|
473
|
+
state.providers.selected = provider;
|
|
474
|
+
state.paymentMethods.data = [];
|
|
475
|
+
state.paymentMethods.selected = null;
|
|
391
476
|
});
|
|
477
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { provider: provider.id }));
|
|
392
478
|
}
|
|
393
479
|
/**
|
|
394
480
|
* Initializes the controller by fetching the user's region from geolocation.
|
|
@@ -414,8 +500,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
414
500
|
if (!regionCode) {
|
|
415
501
|
throw new Error('Region code is required. Cannot hydrate state without valid region information.');
|
|
416
502
|
}
|
|
417
|
-
this.
|
|
418
|
-
this.
|
|
503
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getTokens(regionCode, 'buy', options));
|
|
504
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getProviders(regionCode, options));
|
|
419
505
|
}
|
|
420
506
|
/**
|
|
421
507
|
* Fetches the list of supported countries.
|
|
@@ -429,9 +515,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
429
515
|
const cacheKey = (0, RequestCache_1.createCacheKey)('getCountries', []);
|
|
430
516
|
const countries = await this.executeRequest(cacheKey, async () => {
|
|
431
517
|
return this.messenger.call('RampsService:getCountries');
|
|
432
|
-
}, options);
|
|
518
|
+
}, { ...options, resourceType: 'countries' });
|
|
433
519
|
this.update((state) => {
|
|
434
|
-
state.countries = countries;
|
|
520
|
+
state.countries.data = countries;
|
|
435
521
|
});
|
|
436
522
|
return countries;
|
|
437
523
|
}
|
|
@@ -460,11 +546,16 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
460
546
|
return this.messenger.call('RampsService:getTokens', normalizedRegion, action, {
|
|
461
547
|
provider: options?.provider,
|
|
462
548
|
});
|
|
463
|
-
},
|
|
549
|
+
}, {
|
|
550
|
+
...options,
|
|
551
|
+
resourceType: 'tokens',
|
|
552
|
+
isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
|
|
553
|
+
this.state.userRegion?.regionCode === normalizedRegion,
|
|
554
|
+
});
|
|
464
555
|
this.update((state) => {
|
|
465
556
|
const userRegionCode = state.userRegion?.regionCode;
|
|
466
557
|
if (userRegionCode === undefined || userRegionCode === normalizedRegion) {
|
|
467
|
-
state.tokens = tokens;
|
|
558
|
+
state.tokens.data = tokens;
|
|
468
559
|
}
|
|
469
560
|
});
|
|
470
561
|
return tokens;
|
|
@@ -480,9 +571,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
480
571
|
setSelectedToken(assetId) {
|
|
481
572
|
if (!assetId) {
|
|
482
573
|
this.update((state) => {
|
|
483
|
-
state.
|
|
484
|
-
state.paymentMethods = [];
|
|
485
|
-
state.
|
|
574
|
+
state.tokens.selected = null;
|
|
575
|
+
state.paymentMethods.data = [];
|
|
576
|
+
state.paymentMethods.selected = null;
|
|
486
577
|
});
|
|
487
578
|
return;
|
|
488
579
|
}
|
|
@@ -490,7 +581,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
490
581
|
if (!regionCode) {
|
|
491
582
|
throw new Error('Region is required. Cannot set selected token without valid region information.');
|
|
492
583
|
}
|
|
493
|
-
const
|
|
584
|
+
const tokens = this.state.tokens.data;
|
|
494
585
|
if (!tokens) {
|
|
495
586
|
throw new Error('Tokens not loaded. Cannot set selected token before tokens are fetched.');
|
|
496
587
|
}
|
|
@@ -500,13 +591,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
500
591
|
throw new Error(`Token with asset ID "${assetId}" not found in available tokens.`);
|
|
501
592
|
}
|
|
502
593
|
this.update((state) => {
|
|
503
|
-
state.
|
|
504
|
-
state.paymentMethods = [];
|
|
505
|
-
state.
|
|
506
|
-
});
|
|
507
|
-
this.triggerGetPaymentMethods(regionCode, {
|
|
508
|
-
assetId: token.assetId,
|
|
594
|
+
state.tokens.selected = token;
|
|
595
|
+
state.paymentMethods.data = [];
|
|
596
|
+
state.paymentMethods.selected = null;
|
|
509
597
|
});
|
|
598
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { assetId: token.assetId }));
|
|
510
599
|
}
|
|
511
600
|
/**
|
|
512
601
|
* Fetches the list of providers for a given region.
|
|
@@ -540,11 +629,16 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
540
629
|
fiat: options?.fiat,
|
|
541
630
|
payments: options?.payments,
|
|
542
631
|
});
|
|
543
|
-
},
|
|
632
|
+
}, {
|
|
633
|
+
...options,
|
|
634
|
+
resourceType: 'providers',
|
|
635
|
+
isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
|
|
636
|
+
this.state.userRegion?.regionCode === normalizedRegion,
|
|
637
|
+
});
|
|
544
638
|
this.update((state) => {
|
|
545
639
|
const userRegionCode = state.userRegion?.regionCode;
|
|
546
640
|
if (userRegionCode === undefined || userRegionCode === normalizedRegion) {
|
|
547
|
-
state.providers = providers;
|
|
641
|
+
state.providers.data = providers;
|
|
548
642
|
}
|
|
549
643
|
});
|
|
550
644
|
return { providers };
|
|
@@ -563,8 +657,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
563
657
|
async getPaymentMethods(region, options) {
|
|
564
658
|
const regionCode = region ?? this.state.userRegion?.regionCode ?? null;
|
|
565
659
|
const fiatToUse = options?.fiat ?? this.state.userRegion?.country?.currency ?? null;
|
|
566
|
-
const assetIdToUse = options?.assetId ?? this.state.
|
|
567
|
-
const providerToUse = options?.provider ?? this.state.
|
|
660
|
+
const assetIdToUse = options?.assetId ?? this.state.tokens.selected?.assetId ?? '';
|
|
661
|
+
const providerToUse = options?.provider ?? this.state.providers.selected?.id ?? '';
|
|
568
662
|
if (!regionCode) {
|
|
569
663
|
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
570
664
|
}
|
|
@@ -586,21 +680,31 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
586
680
|
assetId: assetIdToUse,
|
|
587
681
|
provider: providerToUse,
|
|
588
682
|
});
|
|
589
|
-
},
|
|
683
|
+
}, {
|
|
684
|
+
...options,
|
|
685
|
+
resourceType: 'paymentMethods',
|
|
686
|
+
isResultCurrent: () => {
|
|
687
|
+
const regionMatch = this.state.userRegion?.regionCode === undefined ||
|
|
688
|
+
this.state.userRegion?.regionCode === normalizedRegion;
|
|
689
|
+
const tokenMatch = (this.state.tokens.selected?.assetId ?? '') === assetIdToUse;
|
|
690
|
+
const providerMatch = (this.state.providers.selected?.id ?? '') === providerToUse;
|
|
691
|
+
return regionMatch && tokenMatch && providerMatch;
|
|
692
|
+
},
|
|
693
|
+
});
|
|
590
694
|
this.update((state) => {
|
|
591
|
-
const currentAssetId = state.
|
|
592
|
-
const currentProviderId = state.
|
|
695
|
+
const currentAssetId = state.tokens.selected?.assetId ?? '';
|
|
696
|
+
const currentProviderId = state.providers.selected?.id ?? '';
|
|
593
697
|
const tokenSelectionUnchanged = assetIdToUse === currentAssetId;
|
|
594
698
|
const providerSelectionUnchanged = providerToUse === currentProviderId;
|
|
595
699
|
// 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
|
|
596
700
|
// ex: if the user rapidly changes the token or provider, the in-flight payment methods might not be valid
|
|
597
701
|
// so this check will ensure that the payment methods are still valid for the token and provider that were requested
|
|
598
702
|
if (tokenSelectionUnchanged && providerSelectionUnchanged) {
|
|
599
|
-
state.paymentMethods = response.payments;
|
|
703
|
+
state.paymentMethods.data = response.payments;
|
|
600
704
|
// this will auto-select the first payment method if the selected payment method is not in the new payment methods
|
|
601
|
-
const currentSelectionStillValid = response.payments.some((pm) => pm.id === state.
|
|
705
|
+
const currentSelectionStillValid = response.payments.some((pm) => pm.id === state.paymentMethods.selected?.id);
|
|
602
706
|
if (!currentSelectionStillValid) {
|
|
603
|
-
state.
|
|
707
|
+
state.paymentMethods.selected = response.payments[0] ?? null;
|
|
604
708
|
}
|
|
605
709
|
}
|
|
606
710
|
});
|
|
@@ -616,11 +720,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
616
720
|
setSelectedPaymentMethod(paymentMethodId) {
|
|
617
721
|
if (!paymentMethodId) {
|
|
618
722
|
this.update((state) => {
|
|
619
|
-
state.
|
|
723
|
+
state.paymentMethods.selected = null;
|
|
620
724
|
});
|
|
621
725
|
return;
|
|
622
726
|
}
|
|
623
|
-
const
|
|
727
|
+
const paymentMethods = this.state.paymentMethods.data;
|
|
624
728
|
if (!paymentMethods || paymentMethods.length === 0) {
|
|
625
729
|
throw new Error('Payment methods not loaded. Cannot set selected payment method before payment methods are fetched.');
|
|
626
730
|
}
|
|
@@ -629,7 +733,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
629
733
|
throw new Error(`Payment method with ID "${paymentMethodId}" not found in available payment methods.`);
|
|
630
734
|
}
|
|
631
735
|
this.update((state) => {
|
|
632
|
-
state.
|
|
736
|
+
state.paymentMethods.selected = paymentMethod;
|
|
633
737
|
});
|
|
634
738
|
}
|
|
635
739
|
/**
|
|
@@ -654,7 +758,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
654
758
|
const regionToUse = options.region ?? this.state.userRegion?.regionCode;
|
|
655
759
|
const fiatToUse = options.fiat ?? this.state.userRegion?.country?.currency;
|
|
656
760
|
const paymentMethodsToUse = options.paymentMethods ??
|
|
657
|
-
this.state.paymentMethods.map((pm) => pm.id);
|
|
761
|
+
this.state.paymentMethods.data.map((pm) => pm.id);
|
|
658
762
|
const action = options.action ?? 'buy';
|
|
659
763
|
if (!regionToUse) {
|
|
660
764
|
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
@@ -705,11 +809,14 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
705
809
|
}, {
|
|
706
810
|
forceRefresh: options.forceRefresh,
|
|
707
811
|
ttl: options.ttl ?? DEFAULT_QUOTES_TTL,
|
|
812
|
+
resourceType: 'quotes',
|
|
813
|
+
isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
|
|
814
|
+
this.state.userRegion?.regionCode === normalizedRegion,
|
|
708
815
|
});
|
|
709
816
|
this.update((state) => {
|
|
710
817
|
const userRegionCode = state.userRegion?.regionCode;
|
|
711
818
|
if (userRegionCode === undefined || userRegionCode === normalizedRegion) {
|
|
712
|
-
state.quotes = response;
|
|
819
|
+
state.quotes.data = response;
|
|
713
820
|
}
|
|
714
821
|
});
|
|
715
822
|
return response;
|
|
@@ -724,108 +831,41 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
724
831
|
getWidgetUrl(quote) {
|
|
725
832
|
return quote.quote?.widgetUrl ?? null;
|
|
726
833
|
}
|
|
727
|
-
// ============================================================
|
|
728
|
-
// Sync Trigger Methods
|
|
729
|
-
// These fire-and-forget methods are for use in React effects.
|
|
730
|
-
// Errors are stored in state and available via selectors.
|
|
731
|
-
// ============================================================
|
|
732
|
-
/**
|
|
733
|
-
* Triggers setting the user region without throwing.
|
|
734
|
-
*
|
|
735
|
-
* @param region - The region code to set (e.g., "US-CA").
|
|
736
|
-
* @param options - Options for cache behavior.
|
|
737
|
-
*/
|
|
738
|
-
triggerSetUserRegion(region, options) {
|
|
739
|
-
this.setUserRegion(region, options).catch(() => {
|
|
740
|
-
// Error stored in state
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Triggers fetching countries without throwing.
|
|
745
|
-
*
|
|
746
|
-
* @param options - Options for cache behavior.
|
|
747
|
-
*/
|
|
748
|
-
triggerGetCountries(options) {
|
|
749
|
-
this.getCountries(options).catch(() => {
|
|
750
|
-
// Error stored in state
|
|
751
|
-
});
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Triggers fetching tokens without throwing.
|
|
755
|
-
*
|
|
756
|
-
* @param region - The region code. If not provided, uses userRegion from state.
|
|
757
|
-
* @param action - The ramp action type ('buy' or 'sell').
|
|
758
|
-
* @param options - Options for cache behavior.
|
|
759
|
-
*/
|
|
760
|
-
triggerGetTokens(region, action = 'buy', options) {
|
|
761
|
-
this.getTokens(region, action, options).catch(() => {
|
|
762
|
-
// Error stored in state
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Triggers fetching providers without throwing.
|
|
767
|
-
*
|
|
768
|
-
* @param region - The region code. If not provided, uses userRegion from state.
|
|
769
|
-
* @param options - Options for cache behavior and query filters.
|
|
770
|
-
*/
|
|
771
|
-
triggerGetProviders(region, options) {
|
|
772
|
-
this.getProviders(region, options).catch(() => {
|
|
773
|
-
// Error stored in state
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Triggers fetching payment methods without throwing.
|
|
778
|
-
*
|
|
779
|
-
* @param region - User's region code (e.g., "us", "fr", "us-ny").
|
|
780
|
-
* @param options - Query parameters for filtering payment methods.
|
|
781
|
-
* @param options.fiat - Fiat currency code. If not provided, uses userRegion currency.
|
|
782
|
-
* @param options.assetId - CAIP-19 cryptocurrency identifier.
|
|
783
|
-
* @param options.provider - Provider ID path.
|
|
784
|
-
*/
|
|
785
|
-
triggerGetPaymentMethods(region, options) {
|
|
786
|
-
this.getPaymentMethods(region, options).catch(() => {
|
|
787
|
-
// Error stored in state
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Triggers fetching quotes without throwing.
|
|
792
|
-
*
|
|
793
|
-
* @param options - The parameters for fetching quotes.
|
|
794
|
-
* @param options.region - User's region code. If not provided, uses userRegion from state.
|
|
795
|
-
* @param options.fiat - Fiat currency code. If not provided, uses userRegion currency.
|
|
796
|
-
* @param options.assetId - CAIP-19 cryptocurrency identifier.
|
|
797
|
-
* @param options.amount - The amount (in fiat for buy, crypto for sell).
|
|
798
|
-
* @param options.walletAddress - The destination wallet address.
|
|
799
|
-
* @param options.paymentMethods - Array of payment method IDs. If not provided, uses paymentMethods from state.
|
|
800
|
-
* @param options.provider - Optional provider ID to filter quotes.
|
|
801
|
-
* @param options.redirectUrl - Optional redirect URL after order completion.
|
|
802
|
-
* @param options.action - The ramp action type. Defaults to 'buy'.
|
|
803
|
-
* @param options.forceRefresh - Whether to bypass cache.
|
|
804
|
-
* @param options.ttl - Custom TTL for this request.
|
|
805
|
-
*/
|
|
806
|
-
triggerGetQuotes(options) {
|
|
807
|
-
this.getQuotes(options).catch(() => {
|
|
808
|
-
// Error stored in state
|
|
809
|
-
});
|
|
810
|
-
}
|
|
811
834
|
}
|
|
812
835
|
exports.RampsController = RampsController;
|
|
813
|
-
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_instances = new WeakSet(),
|
|
836
|
+
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_pendingResourceCount = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_clearPendingResourceCountForDependentResources = function _RampsController_clearPendingResourceCountForDependentResources() {
|
|
837
|
+
const types = [
|
|
838
|
+
'providers',
|
|
839
|
+
'tokens',
|
|
840
|
+
'paymentMethods',
|
|
841
|
+
'quotes',
|
|
842
|
+
];
|
|
843
|
+
for (const resourceType of types) {
|
|
844
|
+
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").delete(resourceType);
|
|
845
|
+
}
|
|
846
|
+
}, _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
|
|
814
847
|
this.update((state) => {
|
|
815
848
|
const requests = state.requests;
|
|
816
849
|
delete requests[cacheKey];
|
|
817
850
|
});
|
|
818
851
|
}, _RampsController_cleanupState = function _RampsController_cleanupState() {
|
|
852
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
853
|
+
this.update((state) => resetDependentResources(state, {
|
|
854
|
+
clearUserRegionData: true,
|
|
855
|
+
}));
|
|
856
|
+
}, _RampsController_fireAndForget = function _RampsController_fireAndForget(promise) {
|
|
857
|
+
promise.catch((_error) => undefined);
|
|
858
|
+
}, _RampsController_updateResourceField = function _RampsController_updateResourceField(resourceType, field, value) {
|
|
819
859
|
this.update((state) => {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
state.providers = [];
|
|
825
|
-
state.paymentMethods = [];
|
|
826
|
-
state.selectedPaymentMethod = null;
|
|
827
|
-
state.quotes = null;
|
|
860
|
+
const resource = state[resourceType];
|
|
861
|
+
if (resource) {
|
|
862
|
+
resource[field] = value;
|
|
863
|
+
}
|
|
828
864
|
});
|
|
865
|
+
}, _RampsController_setResourceLoading = function _RampsController_setResourceLoading(resourceType, loading) {
|
|
866
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateResourceField).call(this, resourceType, 'isLoading', loading);
|
|
867
|
+
}, _RampsController_setResourceError = function _RampsController_setResourceError(resourceType, error) {
|
|
868
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateResourceField).call(this, resourceType, 'error', error);
|
|
829
869
|
}, _RampsController_updateRequestState = function _RampsController_updateRequestState(cacheKey, requestState) {
|
|
830
870
|
const maxSize = __classPrivateFieldGet(this, _RampsController_requestCacheMaxSize, "f");
|
|
831
871
|
const ttl = __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
|