@metamask-previews/ramps-controller 8.0.0-preview-e7b1aa6 → 8.0.0-preview-27e39dd44
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 +0 -8
- package/dist/RampsController.cjs +113 -175
- package/dist/RampsController.cjs.map +1 -1
- package/dist/RampsController.d.cts +9 -30
- package/dist/RampsController.d.cts.map +1 -1
- package/dist/RampsController.d.mts +9 -30
- package/dist/RampsController.d.mts.map +1 -1
- package/dist/RampsController.mjs +113 -175
- package/dist/RampsController.mjs.map +1 -1
- package/dist/RequestCache.cjs.map +1 -1
- package/dist/RequestCache.d.cts +0 -2
- package/dist/RequestCache.d.cts.map +1 -1
- package/dist/RequestCache.d.mts +0 -2
- package/dist/RequestCache.d.mts.map +1 -1
- package/dist/RequestCache.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,14 +7,6 @@ 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 `widgetUrl` resource state that automatically fetches and stores the buy widget URL whenever the selected quote changes ([#7920](https://github.com/MetaMask/core/pull/7920))
|
|
13
|
-
|
|
14
|
-
### Changed
|
|
15
|
-
|
|
16
|
-
- Refactor: Consolidate reset logic with a shared resetResource helper and fix abort handling for dependent resources ([#7818](https://github.com/MetaMask/core/pull/7818))
|
|
17
|
-
|
|
18
10
|
## [8.0.0]
|
|
19
11
|
|
|
20
12
|
### Changed
|
package/dist/RampsController.cjs
CHANGED
|
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
10
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");
|
|
11
11
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
12
|
};
|
|
13
|
-
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_pendingResourceCount, _RampsController_quotePollingInterval, _RampsController_quotePollingOptions, _RampsController_clearPendingResourceCountForDependentResources,
|
|
13
|
+
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_pendingResourceCount, _RampsController_quotePollingInterval, _RampsController_quotePollingOptions, _RampsController_clearPendingResourceCountForDependentResources, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_fireAndForget, _RampsController_restartPollingIfActive, _RampsController_updateResourceField, _RampsController_setResourceLoading, _RampsController_setResourceError, _RampsController_updateRequestState;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
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");
|
|
@@ -81,12 +81,6 @@ const rampsControllerMetadata = {
|
|
|
81
81
|
includeInStateLogs: false,
|
|
82
82
|
usedInUi: true,
|
|
83
83
|
},
|
|
84
|
-
widgetUrl: {
|
|
85
|
-
persist: false,
|
|
86
|
-
includeInDebugSnapshot: true,
|
|
87
|
-
includeInStateLogs: false,
|
|
88
|
-
usedInUi: true,
|
|
89
|
-
},
|
|
90
84
|
requests: {
|
|
91
85
|
persist: false,
|
|
92
86
|
includeInDebugSnapshot: true,
|
|
@@ -127,39 +121,10 @@ function getDefaultRampsControllerState() {
|
|
|
127
121
|
tokens: createDefaultResourceState(null, null),
|
|
128
122
|
paymentMethods: createDefaultResourceState([], null),
|
|
129
123
|
quotes: createDefaultResourceState(null, null),
|
|
130
|
-
widgetUrl: createDefaultResourceState(null),
|
|
131
124
|
requests: {},
|
|
132
125
|
};
|
|
133
126
|
}
|
|
134
127
|
exports.getDefaultRampsControllerState = getDefaultRampsControllerState;
|
|
135
|
-
const DEPENDENT_RESOURCE_KEYS = [
|
|
136
|
-
'providers',
|
|
137
|
-
'tokens',
|
|
138
|
-
'paymentMethods',
|
|
139
|
-
'quotes',
|
|
140
|
-
];
|
|
141
|
-
const DEPENDENT_RESOURCE_KEYS_SET = new Set(DEPENDENT_RESOURCE_KEYS);
|
|
142
|
-
function resetResource(state, resourceType, defaultResource) {
|
|
143
|
-
const def = defaultResource ?? getDefaultRampsControllerState()[resourceType];
|
|
144
|
-
const resource = state[resourceType];
|
|
145
|
-
resource.data = def.data;
|
|
146
|
-
resource.selected = def.selected;
|
|
147
|
-
resource.isLoading = def.isLoading;
|
|
148
|
-
resource.error = def.error;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Resets the widgetUrl resource to its default state.
|
|
152
|
-
* Mutates state in place; use from within controller update() for atomic updates.
|
|
153
|
-
*
|
|
154
|
-
* @param state - The state object to mutate.
|
|
155
|
-
*/
|
|
156
|
-
function resetWidgetUrl(state) {
|
|
157
|
-
const def = getDefaultRampsControllerState().widgetUrl;
|
|
158
|
-
state.widgetUrl.data = def.data;
|
|
159
|
-
state.widgetUrl.selected = def.selected;
|
|
160
|
-
state.widgetUrl.isLoading = def.isLoading;
|
|
161
|
-
state.widgetUrl.error = def.error;
|
|
162
|
-
}
|
|
163
128
|
/**
|
|
164
129
|
* Resets region-dependent resources (userRegion, providers, tokens, paymentMethods, quotes).
|
|
165
130
|
* Mutates state in place; use from within controller update() for atomic updates.
|
|
@@ -172,11 +137,22 @@ function resetDependentResources(state, options) {
|
|
|
172
137
|
if (options?.clearUserRegionData) {
|
|
173
138
|
state.userRegion = null;
|
|
174
139
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
140
|
+
state.providers.selected = null;
|
|
141
|
+
state.providers.data = [];
|
|
142
|
+
state.providers.isLoading = false;
|
|
143
|
+
state.providers.error = null;
|
|
144
|
+
state.tokens.selected = null;
|
|
145
|
+
state.tokens.data = null;
|
|
146
|
+
state.tokens.isLoading = false;
|
|
147
|
+
state.tokens.error = null;
|
|
148
|
+
state.paymentMethods.data = [];
|
|
149
|
+
state.paymentMethods.selected = null;
|
|
150
|
+
state.paymentMethods.isLoading = false;
|
|
151
|
+
state.paymentMethods.error = null;
|
|
152
|
+
state.quotes.data = null;
|
|
153
|
+
state.quotes.selected = null;
|
|
154
|
+
state.quotes.isLoading = false;
|
|
155
|
+
state.quotes.error = null;
|
|
180
156
|
}
|
|
181
157
|
// === HELPER FUNCTIONS ===
|
|
182
158
|
/**
|
|
@@ -300,49 +276,31 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
300
276
|
__classPrivateFieldSet(this, _RampsController_requestCacheMaxSize, requestCacheMaxSize, "f");
|
|
301
277
|
}
|
|
302
278
|
/**
|
|
303
|
-
* Executes a request with caching
|
|
304
|
-
* request per resource type.
|
|
305
|
-
*
|
|
306
|
-
* 1. **Same cache key in flight** – If a request with this cache key is
|
|
307
|
-
* already pending, returns that promise (deduplication; no second request).
|
|
279
|
+
* Executes a request with caching and deduplication.
|
|
308
280
|
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
281
|
+
* If a request with the same cache key is already in flight, returns the
|
|
282
|
+
* existing promise. If valid cached data exists, returns it without making
|
|
283
|
+
* a new request.
|
|
311
284
|
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
* On success or error, updates request state and resource error;
|
|
317
|
-
* in finally, clears resource loading only if this request was not
|
|
318
|
-
* aborted.
|
|
319
|
-
*
|
|
320
|
-
* @param cacheKey - Unique identifier for this request (e.g. from createCacheKey).
|
|
321
|
-
* @param fetcher - Async function that performs the fetch. Receives an AbortSignal
|
|
322
|
-
* that is aborted when this request is superseded by another for the same resource.
|
|
323
|
-
* @param options - Optional forceRefresh, ttl, and resourceType for loading/error state.
|
|
324
|
-
* @returns The result of the request (from cache, joined promise, or fetcher).
|
|
285
|
+
* @param cacheKey - Unique identifier for this request.
|
|
286
|
+
* @param fetcher - Function that performs the actual fetch. Receives an AbortSignal.
|
|
287
|
+
* @param options - Options for cache behavior.
|
|
288
|
+
* @returns The result of the request.
|
|
325
289
|
*/
|
|
326
290
|
async executeRequest(cacheKey, fetcher, options) {
|
|
327
|
-
// Get TTL for verifying cache expiration
|
|
328
291
|
const ttl = options?.ttl ?? __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
|
|
329
|
-
//
|
|
330
|
-
// Check if a request is already in flight for this cache key
|
|
331
|
-
// If so, return the original promise for that request
|
|
292
|
+
// Check for existing pending request - join it instead of making a duplicate
|
|
332
293
|
const pending = __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey);
|
|
333
294
|
if (pending) {
|
|
334
295
|
return pending.promise;
|
|
335
296
|
}
|
|
336
|
-
// CACHE HIT:
|
|
337
|
-
// If cache is not expired, return the cached data
|
|
338
297
|
if (!options?.forceRefresh) {
|
|
339
298
|
const cached = this.state.requests[cacheKey];
|
|
340
299
|
if (cached && !(0, RequestCache_1.isCacheExpired)(cached, ttl)) {
|
|
341
300
|
return cached.data;
|
|
342
301
|
}
|
|
343
302
|
}
|
|
344
|
-
// Create
|
|
345
|
-
// Record the time the request was started
|
|
303
|
+
// Create abort controller for this request
|
|
346
304
|
const abortController = new AbortController();
|
|
347
305
|
const lastFetchedAt = Date.now();
|
|
348
306
|
const { resourceType } = options ?? {};
|
|
@@ -361,11 +319,15 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
361
319
|
const promise = (async () => {
|
|
362
320
|
try {
|
|
363
321
|
const data = await fetcher(abortController.signal);
|
|
322
|
+
// Don't update state if aborted
|
|
364
323
|
if (abortController.signal.aborted) {
|
|
365
324
|
throw new Error('Request was aborted');
|
|
366
325
|
}
|
|
367
326
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_updateRequestState).call(this, cacheKey, (0, RequestCache_1.createSuccessState)(data, lastFetchedAt));
|
|
368
327
|
if (resourceType) {
|
|
328
|
+
// We need the extra logic because there are two situations where we’re allowed to clear the error:
|
|
329
|
+
// No callback → always clear
|
|
330
|
+
// Callback present → clear only when isResultCurrent() returns true.
|
|
369
331
|
const isCurrent = !options?.isResultCurrent || options.isResultCurrent();
|
|
370
332
|
if (isCurrent) {
|
|
371
333
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_setResourceError).call(this, resourceType, null);
|
|
@@ -374,6 +336,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
374
336
|
return data;
|
|
375
337
|
}
|
|
376
338
|
catch (error) {
|
|
339
|
+
// Don't update state if aborted
|
|
377
340
|
if (abortController.signal.aborted) {
|
|
378
341
|
throw error;
|
|
379
342
|
}
|
|
@@ -388,12 +351,13 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
388
351
|
throw error;
|
|
389
352
|
}
|
|
390
353
|
finally {
|
|
391
|
-
if
|
|
392
|
-
|
|
354
|
+
// Only delete if this is still our entry (not replaced by a new request)
|
|
355
|
+
const currentPending = __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").get(cacheKey);
|
|
356
|
+
if (currentPending?.abortController === abortController) {
|
|
393
357
|
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
|
|
394
358
|
}
|
|
395
359
|
// Clear resource-level loading state only when no requests for this resource remain
|
|
396
|
-
if (resourceType
|
|
360
|
+
if (resourceType) {
|
|
397
361
|
const count = __classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").get(resourceType) ?? 0;
|
|
398
362
|
const next = Math.max(0, count - 1);
|
|
399
363
|
if (next === 0) {
|
|
@@ -406,11 +370,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
406
370
|
}
|
|
407
371
|
}
|
|
408
372
|
})();
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
abortController,
|
|
412
|
-
resourceType,
|
|
413
|
-
});
|
|
373
|
+
// Store pending request for deduplication
|
|
374
|
+
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").set(cacheKey, { promise, abortController });
|
|
414
375
|
return promise;
|
|
415
376
|
}
|
|
416
377
|
/**
|
|
@@ -464,8 +425,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
464
425
|
!this.state.tokens.data ||
|
|
465
426
|
this.state.providers.data.length === 0;
|
|
466
427
|
if (regionChanged) {
|
|
467
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
|
|
468
428
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
429
|
+
}
|
|
430
|
+
if (regionChanged) {
|
|
469
431
|
this.stopQuotePolling();
|
|
470
432
|
}
|
|
471
433
|
this.update((state) => {
|
|
@@ -506,11 +468,15 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
506
468
|
this.stopQuotePolling();
|
|
507
469
|
this.update((state) => {
|
|
508
470
|
state.providers.selected = null;
|
|
509
|
-
|
|
471
|
+
state.paymentMethods.data = [];
|
|
472
|
+
state.paymentMethods.selected = null;
|
|
510
473
|
});
|
|
511
474
|
return;
|
|
512
475
|
}
|
|
513
|
-
const regionCode =
|
|
476
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
477
|
+
if (!regionCode) {
|
|
478
|
+
throw new Error('Region is required. Cannot set selected provider without valid region information.');
|
|
479
|
+
}
|
|
514
480
|
const providers = this.state.providers.data;
|
|
515
481
|
if (!providers || providers.length === 0) {
|
|
516
482
|
throw new Error('Providers not loaded. Cannot set selected provider before providers are fetched.');
|
|
@@ -521,9 +487,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
521
487
|
}
|
|
522
488
|
this.update((state) => {
|
|
523
489
|
state.providers.selected = provider;
|
|
524
|
-
|
|
490
|
+
state.paymentMethods.data = [];
|
|
491
|
+
state.paymentMethods.selected = null;
|
|
525
492
|
state.quotes.selected = null;
|
|
526
|
-
resetWidgetUrl(state);
|
|
527
493
|
});
|
|
528
494
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { provider: provider.id }).then(() => {
|
|
529
495
|
// Restart quote polling after payment methods are fetched
|
|
@@ -551,7 +517,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
551
517
|
await this.setUserRegion(regionCode, options);
|
|
552
518
|
}
|
|
553
519
|
hydrateState(options) {
|
|
554
|
-
const regionCode =
|
|
520
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
521
|
+
if (!regionCode) {
|
|
522
|
+
throw new Error('Region code is required. Cannot hydrate state without valid region information.');
|
|
523
|
+
}
|
|
555
524
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getTokens(regionCode, 'buy', options));
|
|
556
525
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getProviders(regionCode, options));
|
|
557
526
|
}
|
|
@@ -569,7 +538,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
569
538
|
return this.messenger.call('RampsService:getCountries');
|
|
570
539
|
}, { ...options, resourceType: 'countries' });
|
|
571
540
|
this.update((state) => {
|
|
572
|
-
state.countries.data =
|
|
541
|
+
state.countries.data = countries;
|
|
573
542
|
});
|
|
574
543
|
return countries;
|
|
575
544
|
}
|
|
@@ -584,7 +553,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
584
553
|
* @returns The tokens response containing topTokens and allTokens.
|
|
585
554
|
*/
|
|
586
555
|
async getTokens(region, action = 'buy', options) {
|
|
587
|
-
const regionToUse = region ??
|
|
556
|
+
const regionToUse = region ?? this.state.userRegion?.regionCode;
|
|
557
|
+
if (!regionToUse) {
|
|
558
|
+
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
559
|
+
}
|
|
588
560
|
const normalizedRegion = regionToUse.toLowerCase().trim();
|
|
589
561
|
const cacheKey = (0, RequestCache_1.createCacheKey)('getTokens', [
|
|
590
562
|
normalizedRegion,
|
|
@@ -598,7 +570,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
598
570
|
}, {
|
|
599
571
|
...options,
|
|
600
572
|
resourceType: 'tokens',
|
|
601
|
-
isResultCurrent: () =>
|
|
573
|
+
isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
|
|
574
|
+
this.state.userRegion?.regionCode === normalizedRegion,
|
|
602
575
|
});
|
|
603
576
|
this.update((state) => {
|
|
604
577
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -621,11 +594,15 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
621
594
|
this.stopQuotePolling();
|
|
622
595
|
this.update((state) => {
|
|
623
596
|
state.tokens.selected = null;
|
|
624
|
-
|
|
597
|
+
state.paymentMethods.data = [];
|
|
598
|
+
state.paymentMethods.selected = null;
|
|
625
599
|
});
|
|
626
600
|
return;
|
|
627
601
|
}
|
|
628
|
-
const regionCode =
|
|
602
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
603
|
+
if (!regionCode) {
|
|
604
|
+
throw new Error('Region is required. Cannot set selected token without valid region information.');
|
|
605
|
+
}
|
|
629
606
|
const tokens = this.state.tokens.data;
|
|
630
607
|
if (!tokens) {
|
|
631
608
|
throw new Error('Tokens not loaded. Cannot set selected token before tokens are fetched.');
|
|
@@ -637,9 +614,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
637
614
|
}
|
|
638
615
|
this.update((state) => {
|
|
639
616
|
state.tokens.selected = token;
|
|
640
|
-
|
|
617
|
+
state.paymentMethods.data = [];
|
|
618
|
+
state.paymentMethods.selected = null;
|
|
641
619
|
state.quotes.selected = null;
|
|
642
|
-
resetWidgetUrl(state);
|
|
643
620
|
});
|
|
644
621
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { assetId: token.assetId }).then(() => {
|
|
645
622
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_restartPollingIfActive).call(this);
|
|
@@ -659,7 +636,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
659
636
|
* @returns The providers response containing providers array.
|
|
660
637
|
*/
|
|
661
638
|
async getProviders(region, options) {
|
|
662
|
-
const regionToUse = region ??
|
|
639
|
+
const regionToUse = region ?? this.state.userRegion?.regionCode;
|
|
640
|
+
if (!regionToUse) {
|
|
641
|
+
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
642
|
+
}
|
|
663
643
|
const normalizedRegion = regionToUse.toLowerCase().trim();
|
|
664
644
|
const cacheKey = (0, RequestCache_1.createCacheKey)('getProviders', [
|
|
665
645
|
normalizedRegion,
|
|
@@ -678,7 +658,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
678
658
|
}, {
|
|
679
659
|
...options,
|
|
680
660
|
resourceType: 'providers',
|
|
681
|
-
isResultCurrent: () =>
|
|
661
|
+
isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
|
|
662
|
+
this.state.userRegion?.regionCode === normalizedRegion,
|
|
682
663
|
});
|
|
683
664
|
this.update((state) => {
|
|
684
665
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -700,10 +681,13 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
700
681
|
* @returns The payment methods response containing payments array.
|
|
701
682
|
*/
|
|
702
683
|
async getPaymentMethods(region, options) {
|
|
703
|
-
const regionCode = region ??
|
|
684
|
+
const regionCode = region ?? this.state.userRegion?.regionCode ?? null;
|
|
704
685
|
const fiatToUse = options?.fiat ?? this.state.userRegion?.country?.currency ?? null;
|
|
705
686
|
const assetIdToUse = options?.assetId ?? this.state.tokens.selected?.assetId ?? '';
|
|
706
687
|
const providerToUse = options?.provider ?? this.state.providers.selected?.id ?? '';
|
|
688
|
+
if (!regionCode) {
|
|
689
|
+
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
690
|
+
}
|
|
707
691
|
if (!fiatToUse) {
|
|
708
692
|
throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
|
|
709
693
|
}
|
|
@@ -726,9 +710,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
726
710
|
...options,
|
|
727
711
|
resourceType: 'paymentMethods',
|
|
728
712
|
isResultCurrent: () => {
|
|
729
|
-
const regionMatch =
|
|
730
|
-
|
|
731
|
-
const
|
|
713
|
+
const regionMatch = this.state.userRegion?.regionCode === undefined ||
|
|
714
|
+
this.state.userRegion?.regionCode === normalizedRegion;
|
|
715
|
+
const tokenMatch = (this.state.tokens.selected?.assetId ?? '') === assetIdToUse;
|
|
716
|
+
const providerMatch = (this.state.providers.selected?.id ?? '') === providerToUse;
|
|
732
717
|
return regionMatch && tokenMatch && providerMatch;
|
|
733
718
|
},
|
|
734
719
|
});
|
|
@@ -798,7 +783,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
798
783
|
* @returns The quotes response containing success, sorted, error, and customActions.
|
|
799
784
|
*/
|
|
800
785
|
async getQuotes(options) {
|
|
801
|
-
const regionToUse = options.region ??
|
|
786
|
+
const regionToUse = options.region ?? this.state.userRegion?.regionCode;
|
|
802
787
|
const fiatToUse = options.fiat ?? this.state.userRegion?.country?.currency;
|
|
803
788
|
const paymentMethodsToUse = options.paymentMethods ??
|
|
804
789
|
this.state.paymentMethods.data.map((pm) => pm.id);
|
|
@@ -806,6 +791,9 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
806
791
|
this.state.providers.data.map((provider) => provider.id);
|
|
807
792
|
const action = options.action ?? 'buy';
|
|
808
793
|
const assetIdToUse = options.assetId ?? this.state.tokens.selected?.assetId;
|
|
794
|
+
if (!regionToUse) {
|
|
795
|
+
throw new Error('Region is required. Either provide a region parameter or ensure userRegion is set in controller state.');
|
|
796
|
+
}
|
|
809
797
|
if (!fiatToUse) {
|
|
810
798
|
throw new Error('Fiat currency is required. Either provide a fiat parameter or ensure userRegion is set in controller state.');
|
|
811
799
|
}
|
|
@@ -856,7 +844,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
856
844
|
forceRefresh: options.forceRefresh,
|
|
857
845
|
ttl: options.ttl ?? DEFAULT_QUOTES_TTL,
|
|
858
846
|
resourceType: 'quotes',
|
|
859
|
-
isResultCurrent: () =>
|
|
847
|
+
isResultCurrent: () => this.state.userRegion?.regionCode === undefined ||
|
|
848
|
+
this.state.userRegion?.regionCode === normalizedRegion,
|
|
860
849
|
});
|
|
861
850
|
this.update((state) => {
|
|
862
851
|
const userRegionCode = state.userRegion?.regionCode;
|
|
@@ -879,10 +868,14 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
879
868
|
* @throws If required dependencies (region, token, provider, payment method) are not set.
|
|
880
869
|
*/
|
|
881
870
|
startQuotePolling(options) {
|
|
882
|
-
|
|
871
|
+
// Validate required dependencies
|
|
872
|
+
const regionCode = this.state.userRegion?.regionCode;
|
|
883
873
|
const token = this.state.tokens.selected;
|
|
884
874
|
const provider = this.state.providers.selected;
|
|
885
875
|
const paymentMethod = this.state.paymentMethods.selected;
|
|
876
|
+
if (!regionCode) {
|
|
877
|
+
throw new Error('Region is required. Cannot start quote polling without valid region information.');
|
|
878
|
+
}
|
|
886
879
|
if (!token) {
|
|
887
880
|
throw new Error('Token is required. Cannot start quote polling without a selected token.');
|
|
888
881
|
}
|
|
@@ -907,12 +900,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
907
900
|
providers: [provider.id],
|
|
908
901
|
forceRefresh: true,
|
|
909
902
|
}).then((response) => {
|
|
910
|
-
let newSelectedQuote = null;
|
|
911
903
|
// Auto-select logic: only when exactly one quote is returned
|
|
912
904
|
this.update((state) => {
|
|
913
905
|
if (response.success.length === 1) {
|
|
914
|
-
|
|
915
|
-
state.quotes.selected = newSelectedQuote;
|
|
906
|
+
state.quotes.selected = response.success[0];
|
|
916
907
|
}
|
|
917
908
|
else {
|
|
918
909
|
// Keep existing selection if still valid, but update with fresh data
|
|
@@ -921,12 +912,11 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
921
912
|
const freshQuote = response.success.find((quote) => quote.provider === currentSelection.provider &&
|
|
922
913
|
quote.quote.paymentMethod ===
|
|
923
914
|
currentSelection.quote.paymentMethod);
|
|
924
|
-
|
|
925
|
-
state.quotes.selected =
|
|
915
|
+
// Update with fresh quote data, or clear if no longer valid
|
|
916
|
+
state.quotes.selected = freshQuote ?? null;
|
|
926
917
|
}
|
|
927
918
|
}
|
|
928
919
|
});
|
|
929
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_syncWidgetUrl).call(this, newSelectedQuote);
|
|
930
920
|
return undefined;
|
|
931
921
|
}));
|
|
932
922
|
};
|
|
@@ -948,7 +938,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
948
938
|
}
|
|
949
939
|
/**
|
|
950
940
|
* Manually sets the selected quote.
|
|
951
|
-
* Automatically triggers a widget URL fetch for the new quote.
|
|
952
941
|
*
|
|
953
942
|
* @param quote - The quote to select, or null to clear the selection.
|
|
954
943
|
*/
|
|
@@ -956,7 +945,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
956
945
|
this.update((state) => {
|
|
957
946
|
state.quotes.selected = quote;
|
|
958
947
|
});
|
|
959
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_syncWidgetUrl).call(this, quote);
|
|
960
948
|
}
|
|
961
949
|
/**
|
|
962
950
|
* Cleans up controller resources.
|
|
@@ -974,8 +962,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
974
962
|
*
|
|
975
963
|
* @param quote - The quote to fetch the widget URL from.
|
|
976
964
|
* @returns Promise resolving to the widget URL string, or null if not available.
|
|
977
|
-
* @deprecated Read `state.widgetUrl` instead. The widget URL is now automatically
|
|
978
|
-
* fetched and stored in state whenever the selected quote changes.
|
|
979
965
|
*/
|
|
980
966
|
async getWidgetUrl(quote) {
|
|
981
967
|
const buyUrl = quote.quote?.buyURL;
|
|
@@ -994,32 +980,26 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
994
980
|
}
|
|
995
981
|
exports.RampsController = RampsController;
|
|
996
982
|
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_pendingResourceCount = new WeakMap(), _RampsController_quotePollingInterval = new WeakMap(), _RampsController_quotePollingOptions = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_clearPendingResourceCountForDependentResources = function _RampsController_clearPendingResourceCountForDependentResources() {
|
|
997
|
-
|
|
983
|
+
const types = [
|
|
984
|
+
'providers',
|
|
985
|
+
'tokens',
|
|
986
|
+
'paymentMethods',
|
|
987
|
+
'quotes',
|
|
988
|
+
];
|
|
989
|
+
for (const resourceType of types) {
|
|
998
990
|
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").delete(resourceType);
|
|
999
991
|
}
|
|
1000
|
-
},
|
|
1001
|
-
for (const [cacheKey, pending] of __classPrivateFieldGet(this, _RampsController_pendingRequests, "f").entries()) {
|
|
1002
|
-
if (pending.resourceType &&
|
|
1003
|
-
DEPENDENT_RESOURCE_KEYS_SET.has(pending.resourceType)) {
|
|
1004
|
-
pending.abortController.abort();
|
|
1005
|
-
__classPrivateFieldGet(this, _RampsController_pendingRequests, "f").delete(cacheKey);
|
|
1006
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_removeRequestState).call(this, cacheKey);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
}, _RampsController_mutateRequests = function _RampsController_mutateRequests(fn) {
|
|
992
|
+
}, _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
|
|
1010
993
|
this.update((state) => {
|
|
1011
994
|
const requests = state.requests;
|
|
1012
|
-
fn(requests);
|
|
1013
|
-
});
|
|
1014
|
-
}, _RampsController_removeRequestState = function _RampsController_removeRequestState(cacheKey) {
|
|
1015
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_mutateRequests).call(this, (requests) => {
|
|
1016
995
|
delete requests[cacheKey];
|
|
1017
996
|
});
|
|
1018
997
|
}, _RampsController_cleanupState = function _RampsController_cleanupState() {
|
|
1019
998
|
this.stopQuotePolling();
|
|
1020
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
|
|
1021
999
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
1022
|
-
this.update((state) => resetDependentResources(state, {
|
|
1000
|
+
this.update((state) => resetDependentResources(state, {
|
|
1001
|
+
clearUserRegionData: true,
|
|
1002
|
+
}));
|
|
1023
1003
|
}, _RampsController_fireAndForget = function _RampsController_fireAndForget(promise) {
|
|
1024
1004
|
promise.catch((_error) => undefined);
|
|
1025
1005
|
}, _RampsController_restartPollingIfActive = function _RampsController_restartPollingIfActive() {
|
|
@@ -1034,21 +1014,6 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1034
1014
|
// when dependencies are available
|
|
1035
1015
|
}
|
|
1036
1016
|
}
|
|
1037
|
-
}, _RampsController_requireRegion = function _RampsController_requireRegion() {
|
|
1038
|
-
const regionCode = this.state.userRegion?.regionCode;
|
|
1039
|
-
if (!regionCode) {
|
|
1040
|
-
throw new Error('Region is required. Cannot proceed without valid region information.');
|
|
1041
|
-
}
|
|
1042
|
-
return regionCode;
|
|
1043
|
-
}, _RampsController_isRegionCurrent = function _RampsController_isRegionCurrent(normalizedRegion) {
|
|
1044
|
-
const current = this.state.userRegion?.regionCode;
|
|
1045
|
-
return current === undefined || current === normalizedRegion;
|
|
1046
|
-
}, _RampsController_isTokenCurrent = function _RampsController_isTokenCurrent(normalizedAssetId) {
|
|
1047
|
-
const current = this.state.tokens.selected?.assetId ?? '';
|
|
1048
|
-
return current === normalizedAssetId;
|
|
1049
|
-
}, _RampsController_isProviderCurrent = function _RampsController_isProviderCurrent(normalizedProviderId) {
|
|
1050
|
-
const current = this.state.providers.selected?.id ?? '';
|
|
1051
|
-
return current === normalizedProviderId;
|
|
1052
1017
|
}, _RampsController_updateResourceField = function _RampsController_updateResourceField(resourceType, field, value) {
|
|
1053
1018
|
this.update((state) => {
|
|
1054
1019
|
const resource = state[resourceType];
|
|
@@ -1063,8 +1028,11 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1063
1028
|
}, _RampsController_updateRequestState = function _RampsController_updateRequestState(cacheKey, requestState) {
|
|
1064
1029
|
const maxSize = __classPrivateFieldGet(this, _RampsController_requestCacheMaxSize, "f");
|
|
1065
1030
|
const ttl = __classPrivateFieldGet(this, _RampsController_requestCacheTTL, "f");
|
|
1066
|
-
|
|
1031
|
+
this.update((state) => {
|
|
1032
|
+
const requests = state.requests;
|
|
1067
1033
|
requests[cacheKey] = requestState;
|
|
1034
|
+
// Evict expired entries based on TTL
|
|
1035
|
+
// Only evict SUCCESS states that have exceeded their TTL
|
|
1068
1036
|
const keys = Object.keys(requests);
|
|
1069
1037
|
for (const key of keys) {
|
|
1070
1038
|
const entry = requests[key];
|
|
@@ -1073,13 +1041,16 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1073
1041
|
delete requests[key];
|
|
1074
1042
|
}
|
|
1075
1043
|
}
|
|
1044
|
+
// Evict oldest entries if cache still exceeds max size
|
|
1076
1045
|
const remainingKeys = Object.keys(requests);
|
|
1077
1046
|
if (remainingKeys.length > maxSize) {
|
|
1047
|
+
// Sort by timestamp (oldest first)
|
|
1078
1048
|
const sortedKeys = remainingKeys.sort((a, b) => {
|
|
1079
1049
|
const aTime = requests[a]?.timestamp ?? 0;
|
|
1080
1050
|
const bTime = requests[b]?.timestamp ?? 0;
|
|
1081
1051
|
return aTime - bTime;
|
|
1082
1052
|
});
|
|
1053
|
+
// Remove oldest entries until we're under the limit
|
|
1083
1054
|
const entriesToRemove = remainingKeys.length - maxSize;
|
|
1084
1055
|
for (let i = 0; i < entriesToRemove; i++) {
|
|
1085
1056
|
const keyToRemove = sortedKeys[i];
|
|
@@ -1089,38 +1060,5 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1089
1060
|
}
|
|
1090
1061
|
}
|
|
1091
1062
|
});
|
|
1092
|
-
}, _RampsController_syncWidgetUrl = function _RampsController_syncWidgetUrl(quote) {
|
|
1093
|
-
const buyUrl = quote?.quote?.buyURL;
|
|
1094
|
-
if (!buyUrl) {
|
|
1095
|
-
this.update((state) => {
|
|
1096
|
-
resetWidgetUrl(state);
|
|
1097
|
-
});
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
if (this.state.widgetUrl.data === null) {
|
|
1101
|
-
this.update((state) => {
|
|
1102
|
-
state.widgetUrl.isLoading = true;
|
|
1103
|
-
state.widgetUrl.error = null;
|
|
1104
|
-
});
|
|
1105
|
-
}
|
|
1106
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.messenger
|
|
1107
|
-
.call('RampsService:getBuyWidgetUrl', buyUrl)
|
|
1108
|
-
.then((buyWidget) => {
|
|
1109
|
-
this.update((state) => {
|
|
1110
|
-
state.widgetUrl.data = buyWidget;
|
|
1111
|
-
state.widgetUrl.isLoading = false;
|
|
1112
|
-
state.widgetUrl.error = null;
|
|
1113
|
-
});
|
|
1114
|
-
return undefined;
|
|
1115
|
-
})
|
|
1116
|
-
.catch((error) => {
|
|
1117
|
-
this.update((state) => {
|
|
1118
|
-
state.widgetUrl.isLoading = false;
|
|
1119
|
-
state.widgetUrl.error =
|
|
1120
|
-
error instanceof Error
|
|
1121
|
-
? error.message
|
|
1122
|
-
: 'Failed to fetch widget URL';
|
|
1123
|
-
});
|
|
1124
|
-
}));
|
|
1125
1063
|
};
|
|
1126
1064
|
//# sourceMappingURL=RampsController.cjs.map
|