@metamask/ramps-controller 21.0.0 → 22.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 +26 -1
- package/dist/RampsController-method-action-types.d.ts +16 -1
- package/dist/RampsController-method-action-types.d.ts.map +1 -1
- package/dist/RampsController-method-action-types.js.map +1 -1
- package/dist/RampsController.d.ts +69 -6
- package/dist/RampsController.d.ts.map +1 -1
- package/dist/RampsController.js +251 -21
- package/dist/RampsController.js.map +1 -1
- package/dist/RampsService.d.ts +6 -0
- package/dist/RampsService.d.ts.map +1 -1
- package/dist/RampsService.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/order-syncing/constants.d.ts +23 -0
- package/dist/order-syncing/constants.d.ts.map +1 -0
- package/dist/order-syncing/constants.js +23 -0
- package/dist/order-syncing/constants.js.map +1 -0
- package/dist/order-syncing/controller-integration.d.ts +91 -0
- package/dist/order-syncing/controller-integration.d.ts.map +1 -0
- package/dist/order-syncing/controller-integration.js +364 -0
- package/dist/order-syncing/controller-integration.js.map +1 -0
- package/dist/order-syncing/index.d.ts +7 -0
- package/dist/order-syncing/index.d.ts.map +1 -0
- package/dist/order-syncing/index.js +5 -0
- package/dist/order-syncing/index.js.map +1 -0
- package/dist/order-syncing/sync-utils.d.ts +9 -0
- package/dist/order-syncing/sync-utils.d.ts.map +1 -0
- package/dist/order-syncing/sync-utils.js +27 -0
- package/dist/order-syncing/sync-utils.js.map +1 -0
- package/dist/order-syncing/types.d.ts +66 -0
- package/dist/order-syncing/types.d.ts.map +1 -0
- package/dist/order-syncing/types.js +2 -0
- package/dist/order-syncing/types.js.map +1 -0
- package/dist/order-syncing/utils.d.ts +83 -0
- package/dist/order-syncing/utils.d.ts.map +1 -0
- package/dist/order-syncing/utils.js +144 -0
- package/dist/order-syncing/utils.js.map +1 -0
- package/package.json +4 -3
package/dist/RampsController.js
CHANGED
|
@@ -2,6 +2,7 @@ import { BaseController } from '@metamask/base-controller';
|
|
|
2
2
|
import { BrokenCircuitError } from '@metamask/controller-utils';
|
|
3
3
|
import { applyAutorampRemoteStatus, createAutorampAccount, markAutorampNotified, } from './autorampAccount.js';
|
|
4
4
|
import { getHeadlessProviderAllowlist, isHeadlessAllProvidersEnabled, normalizeHeadlessProviderId, } from './featureFlags.js';
|
|
5
|
+
import { areOrdersEqual, deleteOrderInUserStorage, syncOrdersWithUserStorage as syncOrdersWithUserStorageInternal, updateOrderInUserStorage, } from './order-syncing/index.js';
|
|
5
6
|
import { PENDING_ORDER_STATUSES, TERMINAL_ORDER_STATUSES, } from './orderStatus.js';
|
|
6
7
|
import { buildOwnershipMessage } from './ownership-message.js';
|
|
7
8
|
import { mergePaymentMethodsById, pickPaymentMethod, } from './paymentMethodMerge.js';
|
|
@@ -71,12 +72,17 @@ export const RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS = [
|
|
|
71
72
|
* `AuthenticationController:getSessionProfile` resolves the vendor customer
|
|
72
73
|
* identity from Profile Sync, and `KeyringController:signPersonalMessage` signs
|
|
73
74
|
* the EIP-191 ownership proof for Money Account self-hosted wallet
|
|
74
|
-
* registration; both are only exercised by the autoramp paths.
|
|
75
|
+
* registration; both are only exercised by the autoramp paths. User Storage
|
|
76
|
+
* and authentication actions support cross-client order syncing.
|
|
75
77
|
*/
|
|
76
78
|
export const RAMPS_CONTROLLER_REQUIRED_CONTROLLER_ACTIONS = [
|
|
77
79
|
'AuthenticationController:getSessionProfile',
|
|
80
|
+
'AuthenticationController:isSignedIn',
|
|
78
81
|
'KeyringController:signPersonalMessage',
|
|
79
82
|
'RemoteFeatureFlagController:getState',
|
|
83
|
+
'UserStorageController:getState',
|
|
84
|
+
'UserStorageController:performGetStorageAllFeatureEntries',
|
|
85
|
+
'UserStorageController:performBatchSetStorage',
|
|
80
86
|
];
|
|
81
87
|
/**
|
|
82
88
|
* Distinguishes an already-materialized {@link AutorampAccount} from the
|
|
@@ -369,15 +375,19 @@ function findRegionFromCode(regionCode, countries) {
|
|
|
369
375
|
*/
|
|
370
376
|
export function getInternalOrderCode(orderOrId) {
|
|
371
377
|
if (typeof orderOrId === 'string') {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
378
|
+
if (orderOrId.includes('/orders/')) {
|
|
379
|
+
return orderOrId.split('/orders/')[1]?.trim() || '';
|
|
380
|
+
}
|
|
381
|
+
return orderOrId.trim();
|
|
375
382
|
}
|
|
376
383
|
const { id, providerOrderId } = orderOrId;
|
|
377
384
|
if (id?.includes('/orders/')) {
|
|
378
|
-
|
|
385
|
+
const code = id.split('/orders/')[1]?.trim();
|
|
386
|
+
if (code) {
|
|
387
|
+
return code;
|
|
388
|
+
}
|
|
379
389
|
}
|
|
380
|
-
return providerOrderId;
|
|
390
|
+
return providerOrderId?.trim() ?? '';
|
|
381
391
|
}
|
|
382
392
|
// === ORDER POLLING CONSTANTS ===
|
|
383
393
|
const DEFAULT_POLLING_INTERVAL_MS = 30_000;
|
|
@@ -442,6 +452,7 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
442
452
|
'transakCancelOrder',
|
|
443
453
|
'transakCancelAllActiveOrders',
|
|
444
454
|
'transakGetActiveOrders',
|
|
455
|
+
'syncOrdersWithUserStorage',
|
|
445
456
|
];
|
|
446
457
|
/**
|
|
447
458
|
* Whether controller state still describes the context a payment-method
|
|
@@ -472,6 +483,8 @@ export class RampsController extends BaseController {
|
|
|
472
483
|
* Maximum number of entries in the request cache.
|
|
473
484
|
*/
|
|
474
485
|
#requestCacheMaxSize;
|
|
486
|
+
#onOrderSyncErroneousSituation;
|
|
487
|
+
#trace;
|
|
475
488
|
/**
|
|
476
489
|
* Map of pending requests for deduplication.
|
|
477
490
|
* Key is the cache key, value is the pending request with abort controller.
|
|
@@ -491,6 +504,75 @@ export class RampsController extends BaseController {
|
|
|
491
504
|
#orderPollingTimer = null;
|
|
492
505
|
#isPolling = false;
|
|
493
506
|
#initPromise = null;
|
|
507
|
+
/**
|
|
508
|
+
* Semaphore that prevents sync feedback loops while applying remote order changes.
|
|
509
|
+
*/
|
|
510
|
+
#isOrderSyncingInProgress = false;
|
|
511
|
+
/**
|
|
512
|
+
* Whether the full sync is applying its own local state changes.
|
|
513
|
+
*/
|
|
514
|
+
#isApplyingOrderSyncChanges = false;
|
|
515
|
+
/**
|
|
516
|
+
* Orders deleted locally while a full sync held the semaphore.
|
|
517
|
+
*/
|
|
518
|
+
#pendingRemoteDeletes = new Map();
|
|
519
|
+
/**
|
|
520
|
+
* Coalesces overlapping `syncOrdersWithUserStorage` calls into a follow-up run.
|
|
521
|
+
*/
|
|
522
|
+
#orderSyncQueued = false;
|
|
523
|
+
#orderSyncPromise = null;
|
|
524
|
+
/**
|
|
525
|
+
* Whether a full order sync is currently applying remote changes.
|
|
526
|
+
*
|
|
527
|
+
* @returns Whether order sync is in progress.
|
|
528
|
+
*/
|
|
529
|
+
get isOrderSyncingInProgress() {
|
|
530
|
+
return this.#isOrderSyncingInProgress;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Sets the order-syncing-in-progress semaphore.
|
|
534
|
+
* Used by the order-syncing module; hosts should not call this.
|
|
535
|
+
*
|
|
536
|
+
* @param value - Whether sync is in progress.
|
|
537
|
+
* @internal
|
|
538
|
+
*/
|
|
539
|
+
setIsOrderSyncingInProgress(value) {
|
|
540
|
+
this.#isOrderSyncingInProgress = value;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Distinguishes full sync's own state changes from external mutations.
|
|
544
|
+
*
|
|
545
|
+
* @param value - Whether sync changes are being applied.
|
|
546
|
+
* @internal
|
|
547
|
+
*/
|
|
548
|
+
setIsApplyingOrderSyncChanges(value) {
|
|
549
|
+
this.#isApplyingOrderSyncChanges = value;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Returns orders deleted while a full sync was in progress.
|
|
553
|
+
* Used by the order-syncing module.
|
|
554
|
+
*
|
|
555
|
+
* @returns Pending deletes for remote tombstone upload.
|
|
556
|
+
* @internal
|
|
557
|
+
*/
|
|
558
|
+
getPendingRemoteDeletes() {
|
|
559
|
+
return [...this.#pendingRemoteDeletes.values()];
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Clears deletes whose tombstones were successfully persisted. A delete
|
|
563
|
+
* replaced while a write was in flight remains pending.
|
|
564
|
+
*
|
|
565
|
+
* @param orders - Deletes included in a successful remote write.
|
|
566
|
+
* @internal
|
|
567
|
+
*/
|
|
568
|
+
acknowledgePendingRemoteDeletes(orders) {
|
|
569
|
+
for (const order of orders) {
|
|
570
|
+
const key = getInternalOrderCode(order);
|
|
571
|
+
if (key && this.#pendingRemoteDeletes.get(key) === order) {
|
|
572
|
+
this.#pendingRemoteDeletes.delete(key);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
494
576
|
/**
|
|
495
577
|
* Clears the pending resource count map. Used only in tests to exercise the
|
|
496
578
|
* defensive path when get() returns undefined in the finally block.
|
|
@@ -526,8 +608,10 @@ export class RampsController extends BaseController {
|
|
|
526
608
|
* controller. Missing properties will be filled in with defaults.
|
|
527
609
|
* @param args.requestCacheTTL - Time to live for cached requests in milliseconds.
|
|
528
610
|
* @param args.requestCacheMaxSize - Maximum number of entries in the request cache.
|
|
611
|
+
* @param args.onOrderSyncErroneousSituation - Optional order-sync error reporter.
|
|
612
|
+
* @param args.trace - Optional performance tracing callback for order sync.
|
|
529
613
|
*/
|
|
530
|
-
constructor({ messenger, state = {}, requestCacheTTL = DEFAULT_REQUEST_CACHE_TTL, requestCacheMaxSize = DEFAULT_REQUEST_CACHE_MAX_SIZE, }) {
|
|
614
|
+
constructor({ messenger, state = {}, requestCacheTTL = DEFAULT_REQUEST_CACHE_TTL, requestCacheMaxSize = DEFAULT_REQUEST_CACHE_MAX_SIZE, onOrderSyncErroneousSituation, trace, }) {
|
|
531
615
|
super({
|
|
532
616
|
messenger,
|
|
533
617
|
metadata: rampsControllerMetadata,
|
|
@@ -541,6 +625,8 @@ export class RampsController extends BaseController {
|
|
|
541
625
|
});
|
|
542
626
|
this.#requestCacheTTL = requestCacheTTL;
|
|
543
627
|
this.#requestCacheMaxSize = requestCacheMaxSize;
|
|
628
|
+
this.#onOrderSyncErroneousSituation = onOrderSyncErroneousSituation;
|
|
629
|
+
this.#trace = trace;
|
|
544
630
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
545
631
|
}
|
|
546
632
|
/**
|
|
@@ -1841,17 +1927,59 @@ export class RampsController extends BaseController {
|
|
|
1841
1927
|
* If an order with the same internal order code already exists, the incoming
|
|
1842
1928
|
* fields are merged on top of the existing order so that fields not present
|
|
1843
1929
|
* in the update (e.g. paymentDetails from the Transak API) are preserved.
|
|
1930
|
+
* Unchanged syncable payloads (including unchanged poll results) are ignored
|
|
1931
|
+
* so `lastUpdatedAt` is not bumped and User Storage is not rewritten.
|
|
1844
1932
|
*
|
|
1845
1933
|
* @param order - The RampsOrder to add or update.
|
|
1846
1934
|
*/
|
|
1847
1935
|
addOrder(order) {
|
|
1848
1936
|
const internalOrderCode = getInternalOrderCode(order);
|
|
1937
|
+
if (!internalOrderCode) {
|
|
1938
|
+
this.#onOrderSyncErroneousSituation?.('Unable to derive internal order code for addOrder', {});
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
const existing = this.state.orders.find((existingOrder) => getInternalOrderCode(existingOrder) === internalOrderCode);
|
|
1942
|
+
if (existing &&
|
|
1943
|
+
!this.#isApplyingOrderSyncChanges &&
|
|
1944
|
+
areOrdersEqual(existing, order)) {
|
|
1945
|
+
// Syncable payload unchanged; check if paymentDetails differ.
|
|
1946
|
+
// paymentDetails is local-only (never synced remotely), so if it's the
|
|
1947
|
+
// only change we merge it without bumping lastUpdatedAt or writing remotely.
|
|
1948
|
+
if (order.paymentDetails &&
|
|
1949
|
+
JSON.stringify(existing.paymentDetails) !==
|
|
1950
|
+
JSON.stringify(order.paymentDetails)) {
|
|
1951
|
+
this.update((state) => {
|
|
1952
|
+
const idx = state.orders.findIndex((stateOrder) => getInternalOrderCode(stateOrder) === internalOrderCode);
|
|
1953
|
+
if (idx !== -1) {
|
|
1954
|
+
state.orders[idx] = {
|
|
1955
|
+
...state.orders[idx],
|
|
1956
|
+
paymentDetails: order.paymentDetails,
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
const incomingLastUpdatedAt = order.lastUpdatedAt;
|
|
1964
|
+
// Local edits always bump lastUpdatedAt so full-sync LWW can prefer them
|
|
1965
|
+
// over stale remote copies when an incremental push was skipped/failed.
|
|
1966
|
+
// This includes external edits mid-sync (e.g. polling via `getOrder`),
|
|
1967
|
+
// which the queued follow-up sync must not lose under LWW.
|
|
1968
|
+
// Only when sync applies its own imported orders do we preserve the remote
|
|
1969
|
+
// `lu` / `createdAt` (never invent "now" for missing `lu`, or stale remotes
|
|
1970
|
+
// win later LWW comparisons).
|
|
1849
1971
|
const healedOrder = {
|
|
1850
1972
|
...order,
|
|
1851
1973
|
providerOrderId: internalOrderCode,
|
|
1974
|
+
lastUpdatedAt: this.#isApplyingOrderSyncChanges
|
|
1975
|
+
? (incomingLastUpdatedAt ?? order.createdAt ?? 0)
|
|
1976
|
+
: Date.now(),
|
|
1852
1977
|
};
|
|
1978
|
+
if (!this.#isApplyingOrderSyncChanges) {
|
|
1979
|
+
this.#pendingRemoteDeletes.delete(internalOrderCode);
|
|
1980
|
+
}
|
|
1853
1981
|
this.update((state) => {
|
|
1854
|
-
const idx = state.orders.findIndex((
|
|
1982
|
+
const idx = state.orders.findIndex((stateOrder) => getInternalOrderCode(stateOrder) === internalOrderCode);
|
|
1855
1983
|
if (idx === -1) {
|
|
1856
1984
|
state.orders.push(healedOrder);
|
|
1857
1985
|
}
|
|
@@ -1862,6 +1990,22 @@ export class RampsController extends BaseController {
|
|
|
1862
1990
|
};
|
|
1863
1991
|
}
|
|
1864
1992
|
});
|
|
1993
|
+
if (this.#isOrderSyncingInProgress && !this.#isApplyingOrderSyncChanges) {
|
|
1994
|
+
// Incremental push is suppressed during full sync; queue another full
|
|
1995
|
+
// sync pass so mutations during the upload await are not dropped.
|
|
1996
|
+
this.#orderSyncQueued = true;
|
|
1997
|
+
}
|
|
1998
|
+
else if (!this.#isOrderSyncingInProgress) {
|
|
1999
|
+
updateOrderInUserStorage(healedOrder, {
|
|
2000
|
+
getRampsControllerInstance: () => this,
|
|
2001
|
+
getMessenger: () => this.messenger,
|
|
2002
|
+
}, {
|
|
2003
|
+
onOrderSyncErroneousSituation: this.#onOrderSyncErroneousSituation,
|
|
2004
|
+
}).catch((error) => {
|
|
2005
|
+
console.error('Error updating ramps order in remote storage:', error);
|
|
2006
|
+
this.#onOrderSyncErroneousSituation?.('Error updating ramps order in remote storage', { error });
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
1865
2009
|
}
|
|
1866
2010
|
/**
|
|
1867
2011
|
* Removes a V2 order from controller state by providerOrderId.
|
|
@@ -1869,10 +2013,101 @@ export class RampsController extends BaseController {
|
|
|
1869
2013
|
* @param providerOrderId - The provider order ID to remove.
|
|
1870
2014
|
*/
|
|
1871
2015
|
removeOrder(providerOrderId) {
|
|
2016
|
+
const orderToRemove = this.state.orders.find((order) => order.providerOrderId === providerOrderId ||
|
|
2017
|
+
getInternalOrderCode(order) === providerOrderId);
|
|
1872
2018
|
this.update((state) => {
|
|
1873
|
-
state.orders = state.orders.filter((order) => order.providerOrderId !== providerOrderId
|
|
2019
|
+
state.orders = state.orders.filter((order) => order.providerOrderId !== providerOrderId &&
|
|
2020
|
+
getInternalOrderCode(order) !== providerOrderId);
|
|
1874
2021
|
});
|
|
1875
2022
|
this.#orderPollingMeta.delete(providerOrderId);
|
|
2023
|
+
if (orderToRemove) {
|
|
2024
|
+
if (orderToRemove.providerOrderId) {
|
|
2025
|
+
this.#orderPollingMeta.delete(orderToRemove.providerOrderId);
|
|
2026
|
+
}
|
|
2027
|
+
const internalOrderCode = getInternalOrderCode(orderToRemove);
|
|
2028
|
+
this.#orderPollingMeta.delete(internalOrderCode);
|
|
2029
|
+
}
|
|
2030
|
+
if (orderToRemove) {
|
|
2031
|
+
const deleteKey = getInternalOrderCode(orderToRemove);
|
|
2032
|
+
const isLocalDeletion = !this.#isApplyingOrderSyncChanges;
|
|
2033
|
+
if (isLocalDeletion && deleteKey) {
|
|
2034
|
+
// Retain the delete until a full sync confirms its tombstone was
|
|
2035
|
+
// persisted. This prevents a failed incremental write from allowing
|
|
2036
|
+
// the still-active remote copy to be imported again.
|
|
2037
|
+
this.#pendingRemoteDeletes.set(deleteKey, orderToRemove);
|
|
2038
|
+
}
|
|
2039
|
+
if (this.#isOrderSyncingInProgress) {
|
|
2040
|
+
if (isLocalDeletion) {
|
|
2041
|
+
// Incremental remote deletes are gated off during full sync; queue a
|
|
2042
|
+
// tombstone write and another full sync pass so deletes during the
|
|
2043
|
+
// upload await are not dropped.
|
|
2044
|
+
this.#orderSyncQueued = true;
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
else if (isLocalDeletion) {
|
|
2048
|
+
deleteOrderInUserStorage(orderToRemove, {
|
|
2049
|
+
getRampsControllerInstance: () => this,
|
|
2050
|
+
getMessenger: () => this.messenger,
|
|
2051
|
+
}, {
|
|
2052
|
+
onOrderSyncErroneousSituation: this.#onOrderSyncErroneousSituation,
|
|
2053
|
+
}).catch((error) => {
|
|
2054
|
+
console.error('Error deleting ramps order from remote storage:', error);
|
|
2055
|
+
this.#onOrderSyncErroneousSituation?.('Error deleting ramps order from remote storage', { error });
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* Bidirectionally syncs V2 ramps orders with User Storage.
|
|
2062
|
+
* Hosts should call this on unlock / when ramps syncing is enabled.
|
|
2063
|
+
*
|
|
2064
|
+
* Overlapping calls are coalesced into the in-flight worker. After the worker
|
|
2065
|
+
* settles, this method loops when `#orderSyncQueued` is still set so a
|
|
2066
|
+
* request that arrived between the worker's last loop check and promise
|
|
2067
|
+
* resolution is not dropped.
|
|
2068
|
+
*/
|
|
2069
|
+
async syncOrdersWithUserStorage() {
|
|
2070
|
+
this.#orderSyncQueued = true;
|
|
2071
|
+
let syncError;
|
|
2072
|
+
while (this.#orderSyncQueued || this.#orderSyncPromise) {
|
|
2073
|
+
if (this.#orderSyncPromise) {
|
|
2074
|
+
try {
|
|
2075
|
+
await this.#orderSyncPromise;
|
|
2076
|
+
}
|
|
2077
|
+
catch (error) {
|
|
2078
|
+
syncError ??=
|
|
2079
|
+
error instanceof Error ? error : new Error(String(error));
|
|
2080
|
+
}
|
|
2081
|
+
continue;
|
|
2082
|
+
}
|
|
2083
|
+
this.#orderSyncPromise = (async () => {
|
|
2084
|
+
while (this.#orderSyncQueued) {
|
|
2085
|
+
this.#orderSyncQueued = false;
|
|
2086
|
+
await syncOrdersWithUserStorageInternal({
|
|
2087
|
+
onOrderSyncErroneousSituation: this.#onOrderSyncErroneousSituation,
|
|
2088
|
+
}, {
|
|
2089
|
+
getRampsControllerInstance: () => this,
|
|
2090
|
+
getMessenger: () => this.messenger,
|
|
2091
|
+
trace: this.#trace,
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
// Yield so a caller can set `#orderSyncQueued` after the inner while
|
|
2095
|
+
// check and still be observed by the outer loop.
|
|
2096
|
+
await Promise.resolve();
|
|
2097
|
+
})();
|
|
2098
|
+
try {
|
|
2099
|
+
await this.#orderSyncPromise;
|
|
2100
|
+
}
|
|
2101
|
+
catch (error) {
|
|
2102
|
+
syncError ??= error instanceof Error ? error : new Error(String(error));
|
|
2103
|
+
}
|
|
2104
|
+
finally {
|
|
2105
|
+
this.#orderSyncPromise = null;
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
if (syncError) {
|
|
2109
|
+
throw syncError;
|
|
2110
|
+
}
|
|
1876
2111
|
}
|
|
1877
2112
|
/**
|
|
1878
2113
|
* Adds or updates a local autoramp last-seen cursor (e.g. after create).
|
|
@@ -2393,6 +2628,7 @@ export class RampsController extends BaseController {
|
|
|
2393
2628
|
* @returns The unified order data.
|
|
2394
2629
|
*/
|
|
2395
2630
|
async getOrder(providerCode, orderCode, wallet) {
|
|
2631
|
+
const hadOrderAtRequestStart = this.state.orders.some((existingOrder) => getInternalOrderCode(existingOrder) === orderCode);
|
|
2396
2632
|
const order = await this.messenger.call('RampsService:getOrder', providerCode, orderCode, wallet);
|
|
2397
2633
|
const healedWalletAddress = order.walletAddress || wallet;
|
|
2398
2634
|
const internalOrderCode = getInternalOrderCode({
|
|
@@ -2404,18 +2640,12 @@ export class RampsController extends BaseController {
|
|
|
2404
2640
|
walletAddress: healedWalletAddress,
|
|
2405
2641
|
providerOrderId: internalOrderCode,
|
|
2406
2642
|
};
|
|
2407
|
-
this.
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
state.orders[idx] = {
|
|
2414
|
-
...state.orders[idx],
|
|
2415
|
-
...healedOrder,
|
|
2416
|
-
};
|
|
2417
|
-
}
|
|
2418
|
-
});
|
|
2643
|
+
const orderStillExists = this.state.orders.some((existingOrder) => getInternalOrderCode(existingOrder) === internalOrderCode);
|
|
2644
|
+
// A polling request can finish after removeOrder. Do not let that stale
|
|
2645
|
+
// response recreate the local order and overwrite its remote tombstone.
|
|
2646
|
+
if (!hadOrderAtRequestStart || orderStillExists) {
|
|
2647
|
+
this.addOrder(healedOrder);
|
|
2648
|
+
}
|
|
2419
2649
|
return healedOrder;
|
|
2420
2650
|
}
|
|
2421
2651
|
/**
|