@metamask/ramps-controller 24.0.0 → 25.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 +23 -1
- package/dist/RampsController-method-action-types.d.ts +22 -4
- 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 +54 -21
- package/dist/RampsController.d.ts.map +1 -1
- package/dist/RampsController.js +136 -67
- package/dist/RampsController.js.map +1 -1
- package/dist/RampsService.d.ts +20 -0
- package/dist/RampsService.d.ts.map +1 -1
- package/dist/RampsService.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/quoteClassification.d.ts +9 -1
- package/dist/quoteClassification.d.ts.map +1 -1
- package/dist/quoteClassification.js +11 -0
- package/dist/quoteClassification.js.map +1 -1
- package/package.json +7 -7
package/dist/RampsController.js
CHANGED
|
@@ -85,24 +85,56 @@ export const RAMPS_CONTROLLER_REQUIRED_CONTROLLER_ACTIONS = [
|
|
|
85
85
|
'KycController:refreshSessionStatus',
|
|
86
86
|
'KycController:hasCompletedVendorDisclaimers',
|
|
87
87
|
'KycController:hasCompletedSessionDisclaimers',
|
|
88
|
+
'KycController:clearState',
|
|
88
89
|
'RemoteFeatureFlagController:getState',
|
|
89
90
|
'UserStorageController:getState',
|
|
90
91
|
'UserStorageController:performGetStorageAllFeatureEntries',
|
|
91
92
|
'UserStorageController:performBatchSetStorage',
|
|
92
93
|
];
|
|
93
94
|
/**
|
|
94
|
-
*
|
|
95
|
+
* KYC vocabulary used on the VBA onboarding snapshot. `'none'` means no
|
|
96
|
+
* session exists yet (or the vendor returned an unrecognized status).
|
|
95
97
|
*/
|
|
96
|
-
export
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
export const VBA_KYC_STATUSES = [
|
|
99
|
+
'none',
|
|
100
|
+
'new',
|
|
101
|
+
'retry',
|
|
102
|
+
'pending',
|
|
103
|
+
'approved',
|
|
104
|
+
'rejected',
|
|
105
|
+
];
|
|
106
|
+
/**
|
|
107
|
+
* Autoramp setup progress after KYC has been approved.
|
|
108
|
+
* `'in_progress'` is reserved for hosts that observe an in-flight hydrate;
|
|
109
|
+
* {@link RampsController.hydrateVbaOnboarding} itself returns `'ready'` or
|
|
110
|
+
* `'retryable_failure'` once the coalesced run settles.
|
|
111
|
+
*/
|
|
112
|
+
export const VBA_AUTORAMP_STATUSES = [
|
|
113
|
+
'not_ready',
|
|
114
|
+
'in_progress',
|
|
115
|
+
'ready',
|
|
116
|
+
'retryable_failure',
|
|
117
|
+
];
|
|
118
|
+
const EMPTY_VBA_ONBOARDING_SNAPSHOT = {
|
|
119
|
+
sessionExists: false,
|
|
120
|
+
vendorDisclaimersComplete: false,
|
|
121
|
+
sessionDisclaimersComplete: false,
|
|
122
|
+
kycStatus: 'none',
|
|
123
|
+
autorampStatus: 'not_ready',
|
|
124
|
+
};
|
|
125
|
+
const VBA_KYC_STATUS_SET = new Set(VBA_KYC_STATUSES);
|
|
126
|
+
/**
|
|
127
|
+
* Maps a vendor status string onto the snapshot vocabulary.
|
|
128
|
+
*
|
|
129
|
+
* @param value - Raw KYC status from the session.
|
|
130
|
+
* @returns A known {@link VbaKycStatus}, or `'none'` when missing/unknown.
|
|
131
|
+
*/
|
|
132
|
+
function toVbaKycStatus(value) {
|
|
133
|
+
if (value && VBA_KYC_STATUS_SET.has(value)) {
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
return 'none';
|
|
137
|
+
}
|
|
106
138
|
/**
|
|
107
139
|
* Distinguishes an already-materialized {@link AutorampAccount} from the
|
|
108
140
|
* create-fields shape accepted by {@link RampsController.addAutoramp}.
|
|
@@ -237,12 +269,6 @@ const rampsControllerMetadata = {
|
|
|
237
269
|
includeInStateLogs: true,
|
|
238
270
|
usedInUi: true,
|
|
239
271
|
},
|
|
240
|
-
vbaOnboardingStage: {
|
|
241
|
-
persist: true,
|
|
242
|
-
includeInDebugSnapshot: true,
|
|
243
|
-
includeInStateLogs: true,
|
|
244
|
-
usedInUi: true,
|
|
245
|
-
},
|
|
246
272
|
};
|
|
247
273
|
/**
|
|
248
274
|
* Creates a default resource state object.
|
|
@@ -288,7 +314,6 @@ export function getDefaultRampsControllerState() {
|
|
|
288
314
|
orders: [],
|
|
289
315
|
autoramps: [],
|
|
290
316
|
providerAutoSelected: false,
|
|
291
|
-
vbaOnboardingStage: null,
|
|
292
317
|
};
|
|
293
318
|
}
|
|
294
319
|
const DEPENDENT_RESOURCE_KEYS = [
|
|
@@ -450,6 +475,7 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
450
475
|
'startOrderPolling',
|
|
451
476
|
'stopOrderPolling',
|
|
452
477
|
'getBuyWidgetData',
|
|
478
|
+
'getFallbackBuyWidgetData',
|
|
453
479
|
'addPrecreatedOrder',
|
|
454
480
|
'getOrder',
|
|
455
481
|
'getOrderFromCallback',
|
|
@@ -2500,15 +2526,17 @@ export class RampsController extends BaseController {
|
|
|
2500
2526
|
}
|
|
2501
2527
|
}
|
|
2502
2528
|
/**
|
|
2503
|
-
*
|
|
2504
|
-
*
|
|
2529
|
+
* Refreshes KYC session facts and, when Iron has approved KYC, activates the
|
|
2530
|
+
* Money Account (wallet registration + autoramp). Hosts map the returned
|
|
2531
|
+
* {@link VbaOnboardingSnapshot} onto their own funnel; this method does not
|
|
2532
|
+
* name screens.
|
|
2505
2533
|
*
|
|
2506
2534
|
* Overlapping calls share one run so polling cannot trigger duplicate wallet
|
|
2507
2535
|
* signatures or autoramp creation.
|
|
2508
2536
|
*
|
|
2509
2537
|
* @param params - VBA onboarding parameters.
|
|
2510
2538
|
* @param params.walletAddress - Monad Money Account wallet address.
|
|
2511
|
-
* @returns
|
|
2539
|
+
* @returns Independent KYC and autoramp facts for the current customer.
|
|
2512
2540
|
*/
|
|
2513
2541
|
async hydrateVbaOnboarding({ walletAddress, }) {
|
|
2514
2542
|
if (this.#vbaOnboardingHydrationPromise) {
|
|
@@ -2526,20 +2554,21 @@ export class RampsController extends BaseController {
|
|
|
2526
2554
|
}
|
|
2527
2555
|
}
|
|
2528
2556
|
async #hydrateVbaOnboarding(walletAddress) {
|
|
2529
|
-
// Fetch the customer's latest session from the vendor account so each stage
|
|
2530
|
-
// reflects backend truth (e.g. re-verification required after a new
|
|
2531
|
-
// document) rather than only device-local state. A `null` session means no
|
|
2532
|
-
// customer/session exists yet, so onboarding starts at the email step.
|
|
2533
2557
|
// Prefer the in-memory/persisted session status over the backend
|
|
2534
|
-
// latest-status endpoint: after SumSub the backend endpoint
|
|
2535
|
-
// reports kycStatus 'new' right after an 'approved' applicant result), while
|
|
2558
|
+
// latest-status endpoint: after SumSub the backend endpoint can lag, while
|
|
2536
2559
|
// the controller state reflects the journey/SDK outcome. Fall back to a
|
|
2537
2560
|
// backend fetch only when the controller has no session in state (e.g. a
|
|
2538
2561
|
// reinstall/cleared state resuming an existing customer, or a brand-new user
|
|
2539
2562
|
// with no session at all).
|
|
2540
2563
|
let session = null;
|
|
2564
|
+
// Whether `session` came from persisted controller state (as opposed to a
|
|
2565
|
+
// fresh backend fetch, which is always scoped to the current user). Only a
|
|
2566
|
+
// persisted session can belong to a previous identity, so only that path
|
|
2567
|
+
// needs the ownership check below.
|
|
2568
|
+
let sessionFromCache = false;
|
|
2541
2569
|
try {
|
|
2542
2570
|
session = this.messenger.call('KycController:refreshSessionStatus');
|
|
2571
|
+
sessionFromCache = true;
|
|
2543
2572
|
}
|
|
2544
2573
|
catch {
|
|
2545
2574
|
try {
|
|
@@ -2547,48 +2576,43 @@ export class RampsController extends BaseController {
|
|
|
2547
2576
|
}
|
|
2548
2577
|
catch {
|
|
2549
2578
|
// No session exists for this customer yet: the backend returns 404
|
|
2550
|
-
// ("KYC session not found"), which surfaces as a rejection here.
|
|
2551
|
-
// it as "start onboarding at the email step" rather than an error.
|
|
2579
|
+
// ("KYC session not found"), which surfaces as a rejection here.
|
|
2552
2580
|
session = null;
|
|
2553
2581
|
}
|
|
2554
2582
|
}
|
|
2555
2583
|
if (!session) {
|
|
2556
|
-
return
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
}
|
|
2580
|
-
// Submitted; vendor is finalizing → "verification in progress".
|
|
2581
|
-
return this.#setVbaOnboardingStage(VbaOnboardingStage.KycPending);
|
|
2584
|
+
return { ...EMPTY_VBA_ONBOARDING_SNAPSHOT };
|
|
2585
|
+
}
|
|
2586
|
+
// A persisted session can outlive the identity that created it — e.g. a new
|
|
2587
|
+
// wallet created over an install that still holds a previous customer's
|
|
2588
|
+
// session. Reusing it makes the backend reject every session-scoped call
|
|
2589
|
+
// (owner mismatch), dead-ending the user. Verify ownership up front against
|
|
2590
|
+
// the signed-in profile and, on a mismatch, discard the stale session.
|
|
2591
|
+
if (sessionFromCache &&
|
|
2592
|
+
!(await this.#isVbaSessionOwnedByCurrentProfile(session))) {
|
|
2593
|
+
this.messenger.call('KycController:clearState');
|
|
2594
|
+
return { ...EMPTY_VBA_ONBOARDING_SNAPSHOT };
|
|
2595
|
+
}
|
|
2596
|
+
const vendorDisclaimersComplete = await this.messenger.call('KycController:hasCompletedVendorDisclaimers');
|
|
2597
|
+
const sessionDisclaimersComplete = await this.messenger.call('KycController:hasCompletedSessionDisclaimers');
|
|
2598
|
+
const snapshot = {
|
|
2599
|
+
sessionExists: true,
|
|
2600
|
+
vendorDisclaimersComplete,
|
|
2601
|
+
sessionDisclaimersComplete,
|
|
2602
|
+
kycStatus: toVbaKycStatus(session.finalStatus),
|
|
2603
|
+
autorampStatus: 'not_ready',
|
|
2604
|
+
};
|
|
2605
|
+
if (snapshot.kycStatus !== 'approved') {
|
|
2606
|
+
return snapshot;
|
|
2582
2607
|
}
|
|
2583
2608
|
if (!walletAddress.trim()) {
|
|
2584
2609
|
throw new Error('walletAddress is required after KYC acceptance.');
|
|
2585
2610
|
}
|
|
2586
2611
|
// KYC is approved; the remaining work activates the Money account (register
|
|
2587
2612
|
// the wallet + ensure an autoramp). Those calls hit the neobank backend and
|
|
2588
|
-
// can fail transiently (e.g. an address-list lookup timeout).
|
|
2589
|
-
// keep the user on
|
|
2590
|
-
//
|
|
2591
|
-
// error screen — the KYC decision itself already succeeded.
|
|
2613
|
+
// can fail transiently (e.g. an address-list lookup timeout). Surface
|
|
2614
|
+
// `retryable_failure` so the host can keep the user on a pending screen
|
|
2615
|
+
// rather than a fatal error — the KYC decision itself already succeeded.
|
|
2592
2616
|
try {
|
|
2593
2617
|
const registration = await this.registerMoneyAccountWallet({
|
|
2594
2618
|
address: walletAddress,
|
|
@@ -2613,17 +2637,31 @@ export class RampsController extends BaseController {
|
|
|
2613
2637
|
}
|
|
2614
2638
|
}
|
|
2615
2639
|
catch {
|
|
2616
|
-
return
|
|
2640
|
+
return { ...snapshot, autorampStatus: 'retryable_failure' };
|
|
2617
2641
|
}
|
|
2618
|
-
return
|
|
2642
|
+
return { ...snapshot, autorampStatus: 'ready' };
|
|
2619
2643
|
}
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2644
|
+
/**
|
|
2645
|
+
* Whether a persisted KYC session belongs to the currently signed-in profile.
|
|
2646
|
+
* A session's `externalUserId` is the canonical profile id captured when the
|
|
2647
|
+
* session was created, so it must match the current profile for the session
|
|
2648
|
+
* to be reused. When the current identity cannot be resolved, err on the side
|
|
2649
|
+
* of keeping the session (return `true`) so a transient profile-read failure
|
|
2650
|
+
* never discards a valid session.
|
|
2651
|
+
*
|
|
2652
|
+
* @param session - The persisted KYC session status to check.
|
|
2653
|
+
* @returns Whether the session is owned by the current profile.
|
|
2654
|
+
*/
|
|
2655
|
+
async #isVbaSessionOwnedByCurrentProfile(session) {
|
|
2656
|
+
const profile = await this.messenger.call('AuthenticationController:getSessionProfile');
|
|
2657
|
+
const canonicalId = typeof profile?.canonicalProfileId === 'string' &&
|
|
2658
|
+
profile.canonicalProfileId.length > 0
|
|
2659
|
+
? profile.canonicalProfileId
|
|
2660
|
+
: profile?.profileId;
|
|
2661
|
+
if (typeof canonicalId !== 'string' || canonicalId.length === 0) {
|
|
2662
|
+
return true;
|
|
2625
2663
|
}
|
|
2626
|
-
return
|
|
2664
|
+
return session.externalUserId === canonicalId;
|
|
2627
2665
|
}
|
|
2628
2666
|
/**
|
|
2629
2667
|
* Removes a local autoramp last-seen cursor by id.
|
|
@@ -2852,6 +2890,37 @@ export class RampsController extends BaseController {
|
|
|
2852
2890
|
if (!buyUrl) {
|
|
2853
2891
|
return null;
|
|
2854
2892
|
}
|
|
2893
|
+
return this.#fetchBuyWidget(buyUrl);
|
|
2894
|
+
}
|
|
2895
|
+
/**
|
|
2896
|
+
* Fetches the widget data for a quote's hosted-flow fallback (see
|
|
2897
|
+
* `getBuyWidgetFallback`), used when an embedded checkout turns the user away.
|
|
2898
|
+
*
|
|
2899
|
+
* @param fallback - The buy-widget fallback attached to the quote.
|
|
2900
|
+
* @param options - Optional request options.
|
|
2901
|
+
* @param options.redirectUrl - Where the hosted flow returns to; set as the
|
|
2902
|
+
* `redirectUrl` query parameter, replacing any existing value.
|
|
2903
|
+
* @returns Promise resolving to the hosted BuyWidget, or null if the fallback has no URL or the response has an empty url.
|
|
2904
|
+
* @throws TypeError if the fallback URL is not a valid URL.
|
|
2905
|
+
* @throws Rethrows errors from the RampsService (e.g. HttpError, network failures) so clients can react to fetch failures.
|
|
2906
|
+
*/
|
|
2907
|
+
async getFallbackBuyWidgetData(fallback, options) {
|
|
2908
|
+
if (!fallback?.url) {
|
|
2909
|
+
return null;
|
|
2910
|
+
}
|
|
2911
|
+
const buyUrl = new URL(fallback.url);
|
|
2912
|
+
if (options?.redirectUrl) {
|
|
2913
|
+
buyUrl.searchParams.set('redirectUrl', options.redirectUrl);
|
|
2914
|
+
}
|
|
2915
|
+
return this.#fetchBuyWidget(buyUrl.toString());
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* Resolves a buy-widget request URL into the provider widget via the RampsService.
|
|
2919
|
+
*
|
|
2920
|
+
* @param buyUrl - The buy-widget request URL.
|
|
2921
|
+
* @returns Promise resolving to the BuyWidget, or null if the response has an empty url.
|
|
2922
|
+
*/
|
|
2923
|
+
async #fetchBuyWidget(buyUrl) {
|
|
2855
2924
|
const buyWidget = await this.messenger.call('RampsService:getBuyWidgetUrl', buyUrl);
|
|
2856
2925
|
if (!buyWidget?.url) {
|
|
2857
2926
|
return null;
|