@metamask/ramps-controller 9.0.0 → 10.1.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 +31 -1
- package/dist/RampsController.cjs +165 -200
- package/dist/RampsController.cjs.map +1 -1
- package/dist/RampsController.d.cts +61 -44
- package/dist/RampsController.d.cts.map +1 -1
- package/dist/RampsController.d.mts +61 -44
- package/dist/RampsController.d.mts.map +1 -1
- package/dist/RampsController.mjs +165 -200
- package/dist/RampsController.mjs.map +1 -1
- package/dist/RampsService.cjs.map +1 -1
- package/dist/RampsService.d.cts +14 -0
- package/dist/RampsService.d.cts.map +1 -1
- package/dist/RampsService.d.mts +14 -0
- package/dist/RampsService.d.mts.map +1 -1
- package/dist/RampsService.mjs.map +1 -1
- package/dist/RequestCache.cjs.map +1 -1
- package/dist/RequestCache.d.cts +1 -1
- package/dist/RequestCache.d.cts.map +1 -1
- package/dist/RequestCache.d.mts +1 -1
- package/dist/RequestCache.d.mts.map +1 -1
- package/dist/RequestCache.mjs.map +1 -1
- package/dist/TransakService.cjs +29 -16
- package/dist/TransakService.cjs.map +1 -1
- package/dist/TransakService.d.cts +2 -1
- package/dist/TransakService.d.cts.map +1 -1
- package/dist/TransakService.d.mts +2 -1
- package/dist/TransakService.d.mts.map +1 -1
- package/dist/TransakService.mjs +29 -16
- package/dist/TransakService.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 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [10.1.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added `orders: RampsOrder[]` to controller state with persistence, along with crud methods([#8045](https://github.com/MetaMask/core/pull/8045))
|
|
15
|
+
- Added `apiMessage` property to `TransakApiError` to surface human-readable error messages from the Transak API (e.g. OTP rate-limit cooldown) ([#8072](https://github.com/MetaMask/core/pull/8072))
|
|
16
|
+
- Added `RampsController:orderStatusChanged` event, published when a polled order's status transitions ([#8045](https://github.com/MetaMask/core/pull/8045))
|
|
17
|
+
- Add messenger actions for `RampsController:setSelectedToken`, `RampsController:getQuotes`, and `RampsController:getOrder`, register their handlers in `RampsController`, and export the action types from the package index ([#8081](https://github.com/MetaMask/core/pull/8081))
|
|
18
|
+
|
|
19
|
+
## [10.0.0]
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **BREAKING:** Remove `state.quotes` and `state.widgetUrl` from RampsController state. Quote and widget URL data are now managed by consuming components ([#8013](https://github.com/MetaMask/core/pull/8013))
|
|
24
|
+
- **BREAKING:** Remove `fetchQuotesForSelection()` and `setSelectedQuote()`. Components call `getQuotes()` directly and manage selection locally ([#8013](https://github.com/MetaMask/core/pull/8013))
|
|
25
|
+
- Simplify `getWidgetUrl()` to a pure fetch-and-return API; it no longer reads or writes controller state ([#8013](https://github.com/MetaMask/core/pull/8013))
|
|
26
|
+
- Improve `TransakService` error handling ([#8010](https://github.com/MetaMask/core/pull/8010))
|
|
27
|
+
- **BREAKING:** Replace `startQuotePolling()`/`stopQuotePolling()` with `fetchQuotesForSelection()` — quotes are now fetched once per call instead of polling on a 15-second interval ([#7999](https://github.com/MetaMask/core/pull/7999))
|
|
28
|
+
|
|
29
|
+
### Removed
|
|
30
|
+
|
|
31
|
+
- Remove `stopQuotePolling()` method (no interval to stop) ([#7999](https://github.com/MetaMask/core/pull/7999))
|
|
32
|
+
- Remove internal polling restart logic (`#restartPollingIfActive`) from `setSelectedProvider`, `setSelectedToken`, and `setSelectedPaymentMethod` ([#7999](https://github.com/MetaMask/core/pull/7999))
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Fix RampsController flaky test ([#8018](https://github.com/MetaMask/core/pull/8018))
|
|
37
|
+
|
|
10
38
|
## [9.0.0]
|
|
11
39
|
|
|
12
40
|
### Added
|
|
@@ -164,7 +192,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
164
192
|
- Add `OnRampService` for interacting with the OnRamp API
|
|
165
193
|
- Add geolocation detection via IP address lookup
|
|
166
194
|
|
|
167
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@
|
|
195
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@10.1.0...HEAD
|
|
196
|
+
[10.1.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@10.0.0...@metamask/ramps-controller@10.1.0
|
|
197
|
+
[10.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@9.0.0...@metamask/ramps-controller@10.0.0
|
|
168
198
|
[9.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@8.1.0...@metamask/ramps-controller@9.0.0
|
|
169
199
|
[8.1.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@8.0.0...@metamask/ramps-controller@8.1.0
|
|
170
200
|
[8.0.0]: https://github.com/MetaMask/core/compare/@metamask/ramps-controller@7.1.0...@metamask/ramps-controller@8.0.0
|
package/dist/RampsController.cjs
CHANGED
|
@@ -10,10 +10,11 @@ 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,
|
|
13
|
+
var _RampsController_instances, _RampsController_requestCacheTTL, _RampsController_requestCacheMaxSize, _RampsController_pendingRequests, _RampsController_pendingResourceCount, _RampsController_orderPollingMeta, _RampsController_orderPollingTimer, _RampsController_isPolling, _RampsController_clearPendingResourceCountForDependentResources, _RampsController_abortDependentRequests, _RampsController_registerActionHandlers, _RampsController_mutateRequests, _RampsController_removeRequestState, _RampsController_cleanupState, _RampsController_fireAndForget, _RampsController_requireRegion, _RampsController_isRegionCurrent, _RampsController_isTokenCurrent, _RampsController_isProviderCurrent, _RampsController_updateResourceField, _RampsController_setResourceLoading, _RampsController_setResourceError, _RampsController_updateRequestState, _RampsController_refreshOrder, _RampsController_pollPendingOrders, _RampsController_syncTransakAuthOnError;
|
|
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");
|
|
17
|
+
const RampsService_1 = require("./RampsService.cjs");
|
|
17
18
|
const RequestCache_1 = require("./RequestCache.cjs");
|
|
18
19
|
// === GENERAL ===
|
|
19
20
|
/**
|
|
@@ -101,28 +102,22 @@ const rampsControllerMetadata = {
|
|
|
101
102
|
includeInStateLogs: true,
|
|
102
103
|
usedInUi: true,
|
|
103
104
|
},
|
|
104
|
-
|
|
105
|
-
persist: false,
|
|
106
|
-
includeInDebugSnapshot: true,
|
|
107
|
-
includeInStateLogs: false,
|
|
108
|
-
usedInUi: true,
|
|
109
|
-
},
|
|
110
|
-
widgetUrl: {
|
|
105
|
+
requests: {
|
|
111
106
|
persist: false,
|
|
112
107
|
includeInDebugSnapshot: true,
|
|
113
108
|
includeInStateLogs: false,
|
|
114
109
|
usedInUi: true,
|
|
115
110
|
},
|
|
116
|
-
|
|
111
|
+
nativeProviders: {
|
|
117
112
|
persist: false,
|
|
118
113
|
includeInDebugSnapshot: true,
|
|
119
114
|
includeInStateLogs: false,
|
|
120
115
|
usedInUi: true,
|
|
121
116
|
},
|
|
122
|
-
|
|
123
|
-
persist:
|
|
117
|
+
orders: {
|
|
118
|
+
persist: true,
|
|
124
119
|
includeInDebugSnapshot: true,
|
|
125
|
-
includeInStateLogs:
|
|
120
|
+
includeInStateLogs: true,
|
|
126
121
|
usedInUi: true,
|
|
127
122
|
},
|
|
128
123
|
};
|
|
@@ -158,8 +153,6 @@ function getDefaultRampsControllerState() {
|
|
|
158
153
|
providers: createDefaultResourceState([], null),
|
|
159
154
|
tokens: createDefaultResourceState(null, null),
|
|
160
155
|
paymentMethods: createDefaultResourceState([], null),
|
|
161
|
-
quotes: createDefaultResourceState(null, null),
|
|
162
|
-
widgetUrl: createDefaultResourceState(null),
|
|
163
156
|
requests: {},
|
|
164
157
|
nativeProviders: {
|
|
165
158
|
transak: {
|
|
@@ -169,6 +162,7 @@ function getDefaultRampsControllerState() {
|
|
|
169
162
|
kycRequirement: createDefaultResourceState(null),
|
|
170
163
|
},
|
|
171
164
|
},
|
|
165
|
+
orders: [],
|
|
172
166
|
};
|
|
173
167
|
}
|
|
174
168
|
exports.getDefaultRampsControllerState = getDefaultRampsControllerState;
|
|
@@ -176,7 +170,6 @@ const DEPENDENT_RESOURCE_KEYS = [
|
|
|
176
170
|
'providers',
|
|
177
171
|
'tokens',
|
|
178
172
|
'paymentMethods',
|
|
179
|
-
'quotes',
|
|
180
173
|
];
|
|
181
174
|
const DEPENDENT_RESOURCE_KEYS_SET = new Set(DEPENDENT_RESOURCE_KEYS);
|
|
182
175
|
function resetResource(state, resourceType, defaultResource) {
|
|
@@ -188,20 +181,7 @@ function resetResource(state, resourceType, defaultResource) {
|
|
|
188
181
|
resource.error = def.error;
|
|
189
182
|
}
|
|
190
183
|
/**
|
|
191
|
-
* Resets
|
|
192
|
-
* Mutates state in place; use from within controller update() for atomic updates.
|
|
193
|
-
*
|
|
194
|
-
* @param state - The state object to mutate.
|
|
195
|
-
*/
|
|
196
|
-
function resetWidgetUrl(state) {
|
|
197
|
-
const def = getDefaultRampsControllerState().widgetUrl;
|
|
198
|
-
state.widgetUrl.data = def.data;
|
|
199
|
-
state.widgetUrl.selected = def.selected;
|
|
200
|
-
state.widgetUrl.isLoading = def.isLoading;
|
|
201
|
-
state.widgetUrl.error = def.error;
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Resets region-dependent resources (userRegion, providers, tokens, paymentMethods, quotes).
|
|
184
|
+
* Resets region-dependent resources (userRegion, providers, tokens, paymentMethods).
|
|
205
185
|
* Mutates state in place; use from within controller update() for atomic updates.
|
|
206
186
|
*
|
|
207
187
|
* @param state - The state object to mutate.
|
|
@@ -216,7 +196,6 @@ function resetDependentResources(state, options) {
|
|
|
216
196
|
for (const key of DEPENDENT_RESOURCE_KEYS) {
|
|
217
197
|
resetResource(state, key, defaultState[key]);
|
|
218
198
|
}
|
|
219
|
-
resetWidgetUrl(state);
|
|
220
199
|
}
|
|
221
200
|
// === HELPER FUNCTIONS ===
|
|
222
201
|
/**
|
|
@@ -271,6 +250,21 @@ function findRegionFromCode(regionCode, countries) {
|
|
|
271
250
|
regionCode: normalizedCode,
|
|
272
251
|
};
|
|
273
252
|
}
|
|
253
|
+
// === ORDER POLLING CONSTANTS ===
|
|
254
|
+
const TERMINAL_ORDER_STATUSES = new Set([
|
|
255
|
+
RampsService_1.RampsOrderStatus.Completed,
|
|
256
|
+
RampsService_1.RampsOrderStatus.Failed,
|
|
257
|
+
RampsService_1.RampsOrderStatus.Cancelled,
|
|
258
|
+
RampsService_1.RampsOrderStatus.IdExpired,
|
|
259
|
+
]);
|
|
260
|
+
const PENDING_ORDER_STATUSES = new Set([
|
|
261
|
+
RampsService_1.RampsOrderStatus.Pending,
|
|
262
|
+
RampsService_1.RampsOrderStatus.Created,
|
|
263
|
+
RampsService_1.RampsOrderStatus.Unknown,
|
|
264
|
+
RampsService_1.RampsOrderStatus.Precreated,
|
|
265
|
+
]);
|
|
266
|
+
const DEFAULT_POLLING_INTERVAL_MS = 30000;
|
|
267
|
+
const MAX_ERROR_COUNT = 5;
|
|
274
268
|
// === CONTROLLER DEFINITION ===
|
|
275
269
|
/**
|
|
276
270
|
* Manages cryptocurrency on/off ramps functionality.
|
|
@@ -326,18 +320,12 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
326
320
|
* Used so isLoading is only cleared when the last request for that resource finishes.
|
|
327
321
|
*/
|
|
328
322
|
_RampsController_pendingResourceCount.set(this, new Map());
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
*/
|
|
333
|
-
_RampsController_quotePollingInterval.set(this, null);
|
|
334
|
-
/**
|
|
335
|
-
* Options used for quote polling (walletAddress, amount, redirectUrl).
|
|
336
|
-
* Stored so polling can be restarted when dependencies change.
|
|
337
|
-
*/
|
|
338
|
-
_RampsController_quotePollingOptions.set(this, null);
|
|
323
|
+
_RampsController_orderPollingMeta.set(this, new Map());
|
|
324
|
+
_RampsController_orderPollingTimer.set(this, null);
|
|
325
|
+
_RampsController_isPolling.set(this, false);
|
|
339
326
|
__classPrivateFieldSet(this, _RampsController_requestCacheTTL, requestCacheTTL, "f");
|
|
340
327
|
__classPrivateFieldSet(this, _RampsController_requestCacheMaxSize, requestCacheMaxSize, "f");
|
|
328
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_registerActionHandlers).call(this);
|
|
341
329
|
}
|
|
342
330
|
/**
|
|
343
331
|
* Executes a request with caching, deduplication, and at most one in-flight
|
|
@@ -506,7 +494,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
506
494
|
if (regionChanged) {
|
|
507
495
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
|
|
508
496
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
509
|
-
this.stopQuotePolling();
|
|
510
497
|
}
|
|
511
498
|
this.update((state) => {
|
|
512
499
|
if (regionChanged) {
|
|
@@ -543,7 +530,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
543
530
|
*/
|
|
544
531
|
setSelectedProvider(providerId) {
|
|
545
532
|
if (providerId === null) {
|
|
546
|
-
this.stopQuotePolling();
|
|
547
533
|
this.update((state) => {
|
|
548
534
|
state.providers.selected = null;
|
|
549
535
|
resetResource(state, 'paymentMethods');
|
|
@@ -562,14 +548,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
562
548
|
this.update((state) => {
|
|
563
549
|
state.providers.selected = provider;
|
|
564
550
|
resetResource(state, 'paymentMethods');
|
|
565
|
-
state.quotes.selected = null;
|
|
566
|
-
resetWidgetUrl(state);
|
|
567
551
|
});
|
|
568
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { provider: provider.id })
|
|
569
|
-
// Restart quote polling after payment methods are fetched
|
|
570
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_restartPollingIfActive).call(this);
|
|
571
|
-
return undefined;
|
|
572
|
-
}));
|
|
552
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { provider: provider.id }));
|
|
573
553
|
}
|
|
574
554
|
/**
|
|
575
555
|
* Initializes the controller by fetching the user's region from geolocation.
|
|
@@ -658,7 +638,6 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
658
638
|
*/
|
|
659
639
|
setSelectedToken(assetId) {
|
|
660
640
|
if (!assetId) {
|
|
661
|
-
this.stopQuotePolling();
|
|
662
641
|
this.update((state) => {
|
|
663
642
|
state.tokens.selected = null;
|
|
664
643
|
resetResource(state, 'paymentMethods');
|
|
@@ -678,13 +657,8 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
678
657
|
this.update((state) => {
|
|
679
658
|
state.tokens.selected = token;
|
|
680
659
|
resetResource(state, 'paymentMethods');
|
|
681
|
-
state.quotes.selected = null;
|
|
682
|
-
resetWidgetUrl(state);
|
|
683
660
|
});
|
|
684
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { assetId: token.assetId })
|
|
685
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_restartPollingIfActive).call(this);
|
|
686
|
-
return undefined;
|
|
687
|
-
}));
|
|
661
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getPaymentMethods(regionCode, { assetId: token.assetId }));
|
|
688
662
|
}
|
|
689
663
|
/**
|
|
690
664
|
* Fetches the list of providers for a given region.
|
|
@@ -816,12 +790,10 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
816
790
|
this.update((state) => {
|
|
817
791
|
state.paymentMethods.selected = paymentMethod;
|
|
818
792
|
});
|
|
819
|
-
// Restart quote polling if active
|
|
820
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_restartPollingIfActive).call(this);
|
|
821
793
|
}
|
|
822
794
|
/**
|
|
823
795
|
* Fetches quotes from all providers for a given set of parameters.
|
|
824
|
-
*
|
|
796
|
+
* Uses the controller's request cache; callers manage the response in local state.
|
|
825
797
|
*
|
|
826
798
|
* @param options - The parameters for fetching quotes.
|
|
827
799
|
* @param options.region - User's region code. If not provided, uses userRegion from state.
|
|
@@ -890,135 +862,85 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
890
862
|
redirectUrl: options.redirectUrl,
|
|
891
863
|
action,
|
|
892
864
|
};
|
|
893
|
-
|
|
865
|
+
return this.executeRequest(cacheKey, async () => {
|
|
894
866
|
return this.messenger.call('RampsService:getQuotes', params);
|
|
895
867
|
}, {
|
|
896
868
|
forceRefresh: options.forceRefresh,
|
|
897
869
|
ttl: options.ttl ?? DEFAULT_QUOTES_TTL,
|
|
898
|
-
resourceType: 'quotes',
|
|
899
|
-
isResultCurrent: () => __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_isRegionCurrent).call(this, normalizedRegion),
|
|
900
870
|
});
|
|
871
|
+
}
|
|
872
|
+
// === ORDER MANAGEMENT ===
|
|
873
|
+
/**
|
|
874
|
+
* Adds or updates a V2 order in controller state.
|
|
875
|
+
* If an order with the same providerOrderId already exists, the incoming
|
|
876
|
+
* fields are merged on top of the existing order so that fields not present
|
|
877
|
+
* in the update (e.g. paymentDetails from the Transak API) are preserved.
|
|
878
|
+
*
|
|
879
|
+
* @param order - The RampsOrder to add or update.
|
|
880
|
+
*/
|
|
881
|
+
addOrder(order) {
|
|
901
882
|
this.update((state) => {
|
|
902
|
-
const
|
|
903
|
-
if (
|
|
904
|
-
state.
|
|
883
|
+
const idx = state.orders.findIndex((existing) => existing.providerOrderId === order.providerOrderId);
|
|
884
|
+
if (idx === -1) {
|
|
885
|
+
state.orders.push(order);
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
state.orders[idx] = {
|
|
889
|
+
...state.orders[idx],
|
|
890
|
+
...order,
|
|
891
|
+
};
|
|
905
892
|
}
|
|
906
893
|
});
|
|
907
|
-
return response;
|
|
908
894
|
}
|
|
909
895
|
/**
|
|
910
|
-
*
|
|
911
|
-
* Fetches quotes immediately and then every 15 seconds.
|
|
912
|
-
* If the response contains exactly one quote, it is auto-selected.
|
|
913
|
-
* If multiple quotes are returned, the existing selection is preserved if still valid.
|
|
914
|
-
*
|
|
915
|
-
* Returns early (no-op) if the selected payment method is not yet set,
|
|
916
|
-
* allowing callers to invoke this before payment-method selection is finalized.
|
|
896
|
+
* Removes a V2 order from controller state by providerOrderId.
|
|
917
897
|
*
|
|
918
|
-
* @param
|
|
919
|
-
* @param options.walletAddress - The destination wallet address.
|
|
920
|
-
* @param options.amount - The amount (in fiat for buy, crypto for sell).
|
|
921
|
-
* @param options.redirectUrl - Optional redirect URL after order completion.
|
|
922
|
-
* @throws If required dependencies (region, token, provider) are not set.
|
|
898
|
+
* @param providerOrderId - The provider order ID to remove.
|
|
923
899
|
*/
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
if (!token) {
|
|
930
|
-
throw new Error('Token is required. Cannot start quote polling without a selected token.');
|
|
931
|
-
}
|
|
932
|
-
if (!provider) {
|
|
933
|
-
throw new Error('Provider is required. Cannot start quote polling without a selected provider.');
|
|
934
|
-
}
|
|
935
|
-
if (!paymentMethod) {
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
// Stop any existing polling first
|
|
939
|
-
this.stopQuotePolling();
|
|
940
|
-
// Store options for restarts (must be after stop to avoid being cleared)
|
|
941
|
-
__classPrivateFieldSet(this, _RampsController_quotePollingOptions, options, "f");
|
|
942
|
-
// Define the fetch function
|
|
943
|
-
const fetchQuotes = () => {
|
|
944
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.getQuotes({
|
|
945
|
-
assetId: token.assetId,
|
|
946
|
-
amount: options.amount,
|
|
947
|
-
walletAddress: options.walletAddress,
|
|
948
|
-
redirectUrl: options.redirectUrl,
|
|
949
|
-
paymentMethods: [paymentMethod.id],
|
|
950
|
-
providers: [provider.id],
|
|
951
|
-
forceRefresh: true,
|
|
952
|
-
}).then((response) => {
|
|
953
|
-
let newSelectedQuote = null;
|
|
954
|
-
// Auto-select logic: only when exactly one quote is returned
|
|
955
|
-
this.update((state) => {
|
|
956
|
-
if (response.success.length === 1) {
|
|
957
|
-
newSelectedQuote = response.success[0];
|
|
958
|
-
state.quotes.selected = newSelectedQuote;
|
|
959
|
-
}
|
|
960
|
-
else {
|
|
961
|
-
// Keep existing selection if still valid, but update with fresh data
|
|
962
|
-
const currentSelection = state.quotes.selected;
|
|
963
|
-
if (currentSelection) {
|
|
964
|
-
const freshQuote = response.success.find((quote) => quote.provider === currentSelection.provider &&
|
|
965
|
-
quote.quote.paymentMethod ===
|
|
966
|
-
currentSelection.quote.paymentMethod);
|
|
967
|
-
newSelectedQuote = freshQuote ?? null;
|
|
968
|
-
state.quotes.selected = newSelectedQuote;
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
});
|
|
972
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_syncWidgetUrl).call(this, newSelectedQuote);
|
|
973
|
-
return undefined;
|
|
974
|
-
}));
|
|
975
|
-
};
|
|
976
|
-
// Fetch immediately
|
|
977
|
-
fetchQuotes();
|
|
978
|
-
// Set up 15-second polling
|
|
979
|
-
__classPrivateFieldSet(this, _RampsController_quotePollingInterval, setInterval(fetchQuotes, 15000), "f");
|
|
900
|
+
removeOrder(providerOrderId) {
|
|
901
|
+
this.update((state) => {
|
|
902
|
+
state.orders = state.orders.filter((order) => order.providerOrderId !== providerOrderId);
|
|
903
|
+
});
|
|
904
|
+
__classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").delete(providerOrderId);
|
|
980
905
|
}
|
|
981
906
|
/**
|
|
982
|
-
*
|
|
983
|
-
*
|
|
907
|
+
* Starts polling all pending V2 orders at a fixed interval.
|
|
908
|
+
* Each poll cycle iterates orders with non-terminal statuses,
|
|
909
|
+
* respects pollingSecondsMinimum and backoff from error count.
|
|
984
910
|
*/
|
|
985
|
-
|
|
986
|
-
if (__classPrivateFieldGet(this,
|
|
987
|
-
|
|
988
|
-
__classPrivateFieldSet(this, _RampsController_quotePollingInterval, null, "f");
|
|
911
|
+
startOrderPolling() {
|
|
912
|
+
if (__classPrivateFieldGet(this, _RampsController_orderPollingTimer, "f")) {
|
|
913
|
+
return;
|
|
989
914
|
}
|
|
990
|
-
__classPrivateFieldSet(this,
|
|
915
|
+
__classPrivateFieldSet(this, _RampsController_orderPollingTimer, setInterval(() => {
|
|
916
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_pollPendingOrders).call(this).catch(() => undefined);
|
|
917
|
+
}, DEFAULT_POLLING_INTERVAL_MS), "f");
|
|
918
|
+
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_pollPendingOrders).call(this).catch(() => undefined);
|
|
991
919
|
}
|
|
992
920
|
/**
|
|
993
|
-
*
|
|
994
|
-
* Automatically triggers a widget URL fetch for the new quote.
|
|
995
|
-
*
|
|
996
|
-
* @param quote - The quote to select, or null to clear the selection.
|
|
921
|
+
* Stops order polling and clears the interval.
|
|
997
922
|
*/
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
923
|
+
stopOrderPolling() {
|
|
924
|
+
if (__classPrivateFieldGet(this, _RampsController_orderPollingTimer, "f")) {
|
|
925
|
+
clearInterval(__classPrivateFieldGet(this, _RampsController_orderPollingTimer, "f"));
|
|
926
|
+
__classPrivateFieldSet(this, _RampsController_orderPollingTimer, null, "f");
|
|
927
|
+
}
|
|
1003
928
|
}
|
|
1004
929
|
/**
|
|
1005
930
|
* Cleans up controller resources.
|
|
1006
|
-
* Stops any active quote polling to prevent memory leaks.
|
|
1007
931
|
* Should be called when the controller is no longer needed.
|
|
1008
932
|
*/
|
|
1009
933
|
destroy() {
|
|
1010
|
-
this.
|
|
934
|
+
this.stopOrderPolling();
|
|
1011
935
|
super.destroy();
|
|
1012
936
|
}
|
|
1013
937
|
/**
|
|
1014
938
|
* Fetches the widget URL from a quote for redirect providers.
|
|
1015
939
|
* Makes a request to the buyURL endpoint via the RampsService to get the
|
|
1016
|
-
* actual provider widget URL
|
|
940
|
+
* actual provider widget URL.
|
|
1017
941
|
*
|
|
1018
942
|
* @param quote - The quote to fetch the widget URL from.
|
|
1019
943
|
* @returns Promise resolving to the widget URL string, or null if not available.
|
|
1020
|
-
* @deprecated Read `state.widgetUrl` instead. The widget URL is now automatically
|
|
1021
|
-
* fetched and stored in state whenever the selected quote changes.
|
|
1022
944
|
*/
|
|
1023
945
|
async getWidgetUrl(quote) {
|
|
1024
946
|
const buyUrl = quote.quote?.buyURL;
|
|
@@ -1043,7 +965,17 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
1043
965
|
* @returns The unified order data.
|
|
1044
966
|
*/
|
|
1045
967
|
async getOrder(providerCode, orderCode, wallet) {
|
|
1046
|
-
|
|
968
|
+
const order = await this.messenger.call('RampsService:getOrder', providerCode, orderCode, wallet);
|
|
969
|
+
this.update((state) => {
|
|
970
|
+
const idx = state.orders.findIndex((existing) => existing.providerOrderId === orderCode);
|
|
971
|
+
if (idx !== -1) {
|
|
972
|
+
state.orders[idx] = {
|
|
973
|
+
...state.orders[idx],
|
|
974
|
+
...order,
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
return order;
|
|
1047
979
|
}
|
|
1048
980
|
/**
|
|
1049
981
|
* Extracts an order from a provider callback URL.
|
|
@@ -1455,7 +1387,7 @@ class RampsController extends base_controller_1.BaseController {
|
|
|
1455
1387
|
}
|
|
1456
1388
|
}
|
|
1457
1389
|
exports.RampsController = RampsController;
|
|
1458
|
-
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_pendingResourceCount = new WeakMap(),
|
|
1390
|
+
_RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheMaxSize = new WeakMap(), _RampsController_pendingRequests = new WeakMap(), _RampsController_pendingResourceCount = new WeakMap(), _RampsController_orderPollingMeta = new WeakMap(), _RampsController_orderPollingTimer = new WeakMap(), _RampsController_isPolling = new WeakMap(), _RampsController_instances = new WeakSet(), _RampsController_clearPendingResourceCountForDependentResources = function _RampsController_clearPendingResourceCountForDependentResources() {
|
|
1459
1391
|
for (const resourceType of DEPENDENT_RESOURCE_KEYS) {
|
|
1460
1392
|
__classPrivateFieldGet(this, _RampsController_pendingResourceCount, "f").delete(resourceType);
|
|
1461
1393
|
}
|
|
@@ -1468,6 +1400,10 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1468
1400
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_removeRequestState).call(this, cacheKey);
|
|
1469
1401
|
}
|
|
1470
1402
|
}
|
|
1403
|
+
}, _RampsController_registerActionHandlers = function _RampsController_registerActionHandlers() {
|
|
1404
|
+
this.messenger.registerActionHandler('RampsController:getOrder', this.getOrder.bind(this));
|
|
1405
|
+
this.messenger.registerActionHandler('RampsController:getQuotes', this.getQuotes.bind(this));
|
|
1406
|
+
this.messenger.registerActionHandler('RampsController:setSelectedToken', this.setSelectedToken.bind(this));
|
|
1471
1407
|
}, _RampsController_mutateRequests = function _RampsController_mutateRequests(fn) {
|
|
1472
1408
|
this.update((state) => {
|
|
1473
1409
|
const requests = state.requests;
|
|
@@ -1478,24 +1414,11 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1478
1414
|
delete requests[cacheKey];
|
|
1479
1415
|
});
|
|
1480
1416
|
}, _RampsController_cleanupState = function _RampsController_cleanupState() {
|
|
1481
|
-
this.stopQuotePolling();
|
|
1482
1417
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_abortDependentRequests).call(this);
|
|
1483
1418
|
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_clearPendingResourceCountForDependentResources).call(this);
|
|
1484
1419
|
this.update((state) => resetDependentResources(state, { clearUserRegionData: true }));
|
|
1485
1420
|
}, _RampsController_fireAndForget = function _RampsController_fireAndForget(promise) {
|
|
1486
1421
|
promise.catch((_error) => undefined);
|
|
1487
|
-
}, _RampsController_restartPollingIfActive = function _RampsController_restartPollingIfActive() {
|
|
1488
|
-
if (__classPrivateFieldGet(this, _RampsController_quotePollingInterval, "f") !== null && __classPrivateFieldGet(this, _RampsController_quotePollingOptions, "f")) {
|
|
1489
|
-
const options = __classPrivateFieldGet(this, _RampsController_quotePollingOptions, "f");
|
|
1490
|
-
this.stopQuotePolling();
|
|
1491
|
-
try {
|
|
1492
|
-
this.startQuotePolling(options);
|
|
1493
|
-
}
|
|
1494
|
-
catch {
|
|
1495
|
-
// Dependencies not met yet, polling will need to be manually restarted
|
|
1496
|
-
// when dependencies are available
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
1422
|
}, _RampsController_requireRegion = function _RampsController_requireRegion() {
|
|
1500
1423
|
const regionCode = this.state.userRegion?.regionCode;
|
|
1501
1424
|
if (!regionCode) {
|
|
@@ -1551,39 +1474,81 @@ _RampsController_requestCacheTTL = new WeakMap(), _RampsController_requestCacheM
|
|
|
1551
1474
|
}
|
|
1552
1475
|
}
|
|
1553
1476
|
});
|
|
1554
|
-
},
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1477
|
+
}, _RampsController_refreshOrder =
|
|
1478
|
+
/**
|
|
1479
|
+
* Refreshes a single order via the V2 API and updates it in state.
|
|
1480
|
+
* Publishes orderStatusChanged if the status transitioned.
|
|
1481
|
+
*
|
|
1482
|
+
* @param order - The order to refresh (needs provider and providerOrderId).
|
|
1483
|
+
*/
|
|
1484
|
+
async function _RampsController_refreshOrder(order) {
|
|
1485
|
+
const providerCode = order.provider?.id ?? '';
|
|
1486
|
+
if (!providerCode || !order.providerOrderId || !order.walletAddress) {
|
|
1560
1487
|
return;
|
|
1561
1488
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1489
|
+
const providerCodeSegment = providerCode.replace('/providers/', '');
|
|
1490
|
+
const previousStatus = order.status;
|
|
1491
|
+
try {
|
|
1492
|
+
const updatedOrder = await this.getOrder(providerCodeSegment, order.providerOrderId, order.walletAddress);
|
|
1493
|
+
const meta = __classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").get(order.providerOrderId) ?? {
|
|
1494
|
+
lastTimeFetched: 0,
|
|
1495
|
+
errorCount: 0,
|
|
1496
|
+
};
|
|
1497
|
+
if (updatedOrder.status === RampsService_1.RampsOrderStatus.Unknown) {
|
|
1498
|
+
meta.errorCount = Math.min(meta.errorCount + 1, MAX_ERROR_COUNT);
|
|
1499
|
+
}
|
|
1500
|
+
else {
|
|
1501
|
+
meta.errorCount = 0;
|
|
1502
|
+
}
|
|
1503
|
+
meta.lastTimeFetched = Date.now();
|
|
1504
|
+
__classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").set(order.providerOrderId, meta);
|
|
1505
|
+
if (previousStatus !== updatedOrder.status &&
|
|
1506
|
+
previousStatus !== undefined) {
|
|
1507
|
+
this.messenger.publish('RampsController:orderStatusChanged', {
|
|
1508
|
+
order: updatedOrder,
|
|
1509
|
+
previousStatus,
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
if (TERMINAL_ORDER_STATUSES.has(updatedOrder.status)) {
|
|
1513
|
+
__classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").delete(order.providerOrderId);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
catch {
|
|
1517
|
+
const meta = __classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").get(order.providerOrderId) ?? {
|
|
1518
|
+
lastTimeFetched: 0,
|
|
1519
|
+
errorCount: 0,
|
|
1520
|
+
};
|
|
1521
|
+
meta.errorCount = Math.min(meta.errorCount + 1, MAX_ERROR_COUNT);
|
|
1522
|
+
meta.lastTimeFetched = Date.now();
|
|
1523
|
+
__classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").set(order.providerOrderId, meta);
|
|
1524
|
+
}
|
|
1525
|
+
}, _RampsController_pollPendingOrders = async function _RampsController_pollPendingOrders() {
|
|
1526
|
+
if (__classPrivateFieldGet(this, _RampsController_isPolling, "f")) {
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
__classPrivateFieldSet(this, _RampsController_isPolling, true, "f");
|
|
1530
|
+
try {
|
|
1531
|
+
const pendingOrders = this.state.orders.filter((order) => PENDING_ORDER_STATUSES.has(order.status));
|
|
1532
|
+
const now = Date.now();
|
|
1533
|
+
await Promise.allSettled(pendingOrders.map(async (order) => {
|
|
1534
|
+
const meta = __classPrivateFieldGet(this, _RampsController_orderPollingMeta, "f").get(order.providerOrderId);
|
|
1535
|
+
if (meta) {
|
|
1536
|
+
const backoffMs = meta.errorCount > 0
|
|
1537
|
+
? Math.min(DEFAULT_POLLING_INTERVAL_MS *
|
|
1538
|
+
Math.pow(2, meta.errorCount - 1), 5 * 60 * 1000)
|
|
1539
|
+
: 0;
|
|
1540
|
+
const pollingMinMs = (order.pollingSecondsMinimum ?? 0) * 1000;
|
|
1541
|
+
const minWait = Math.max(backoffMs, pollingMinMs);
|
|
1542
|
+
if (now - meta.lastTimeFetched < minWait) {
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
await __classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_refreshOrder).call(this, order);
|
|
1547
|
+
}));
|
|
1548
|
+
}
|
|
1549
|
+
finally {
|
|
1550
|
+
__classPrivateFieldSet(this, _RampsController_isPolling, false, "f");
|
|
1567
1551
|
}
|
|
1568
|
-
__classPrivateFieldGet(this, _RampsController_instances, "m", _RampsController_fireAndForget).call(this, this.messenger
|
|
1569
|
-
.call('RampsService:getBuyWidgetUrl', buyUrl)
|
|
1570
|
-
.then((buyWidget) => {
|
|
1571
|
-
this.update((state) => {
|
|
1572
|
-
state.widgetUrl.data = buyWidget;
|
|
1573
|
-
state.widgetUrl.isLoading = false;
|
|
1574
|
-
state.widgetUrl.error = null;
|
|
1575
|
-
});
|
|
1576
|
-
return undefined;
|
|
1577
|
-
})
|
|
1578
|
-
.catch((error) => {
|
|
1579
|
-
this.update((state) => {
|
|
1580
|
-
state.widgetUrl.isLoading = false;
|
|
1581
|
-
state.widgetUrl.error =
|
|
1582
|
-
error instanceof Error
|
|
1583
|
-
? error.message
|
|
1584
|
-
: 'Failed to fetch widget URL';
|
|
1585
|
-
});
|
|
1586
|
-
}));
|
|
1587
1552
|
}, _RampsController_syncTransakAuthOnError = function _RampsController_syncTransakAuthOnError(error) {
|
|
1588
1553
|
if (error instanceof Error &&
|
|
1589
1554
|
'httpStatus' in error &&
|