@metamask/ramps-controller 20.2.0 → 20.3.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 +14 -1
- package/dist/NeoBankService-method-action-types.cjs +7 -0
- package/dist/NeoBankService-method-action-types.cjs.map +1 -0
- package/dist/NeoBankService-method-action-types.d.cts +126 -0
- package/dist/NeoBankService-method-action-types.d.cts.map +1 -0
- package/dist/NeoBankService-method-action-types.d.mts +126 -0
- package/dist/NeoBankService-method-action-types.d.mts.map +1 -0
- package/dist/NeoBankService-method-action-types.mjs +6 -0
- package/dist/NeoBankService-method-action-types.mjs.map +1 -0
- package/dist/NeoBankService.cjs +387 -0
- package/dist/NeoBankService.cjs.map +1 -0
- package/dist/NeoBankService.d.cts +191 -0
- package/dist/NeoBankService.d.cts.map +1 -0
- package/dist/NeoBankService.d.mts +191 -0
- package/dist/NeoBankService.d.mts.map +1 -0
- package/dist/NeoBankService.mjs +379 -0
- package/dist/NeoBankService.mjs.map +1 -0
- package/dist/RampsController-method-action-types.cjs.map +1 -1
- package/dist/RampsController-method-action-types.d.cts +91 -1
- package/dist/RampsController-method-action-types.d.cts.map +1 -1
- package/dist/RampsController-method-action-types.d.mts +91 -1
- package/dist/RampsController-method-action-types.d.mts.map +1 -1
- package/dist/RampsController-method-action-types.mjs.map +1 -1
- package/dist/RampsController.cjs +372 -2
- package/dist/RampsController.cjs.map +1 -1
- package/dist/RampsController.d.cts +152 -5
- package/dist/RampsController.d.cts.map +1 -1
- package/dist/RampsController.d.mts +152 -5
- package/dist/RampsController.d.mts.map +1 -1
- package/dist/RampsController.mjs +371 -1
- package/dist/RampsController.mjs.map +1 -1
- package/dist/autoramp-types.cjs +3 -0
- package/dist/autoramp-types.cjs.map +1 -0
- package/dist/autoramp-types.d.cts +22 -0
- package/dist/autoramp-types.d.cts.map +1 -0
- package/dist/autoramp-types.d.mts +22 -0
- package/dist/autoramp-types.d.mts.map +1 -0
- package/dist/autoramp-types.mjs +2 -0
- package/dist/autoramp-types.mjs.map +1 -0
- package/dist/autorampAccount.cjs +155 -0
- package/dist/autorampAccount.cjs.map +1 -0
- package/dist/autorampAccount.d.cts +126 -0
- package/dist/autorampAccount.d.cts.map +1 -0
- package/dist/autorampAccount.d.mts +126 -0
- package/dist/autorampAccount.d.mts.map +1 -0
- package/dist/autorampAccount.mjs +147 -0
- package/dist/autorampAccount.mjs.map +1 -0
- package/dist/index.cjs +20 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +12 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/dist/ownership-message.cjs +26 -0
- package/dist/ownership-message.cjs.map +1 -0
- package/dist/ownership-message.d.cts +22 -0
- package/dist/ownership-message.d.cts.map +1 -0
- package/dist/ownership-message.d.mts +22 -0
- package/dist/ownership-message.d.mts.map +1 -0
- package/dist/ownership-message.mjs +22 -0
- package/dist/ownership-message.mjs.map +1 -0
- package/dist/wallet-registration-machine.cjs +144 -0
- package/dist/wallet-registration-machine.cjs.map +1 -0
- package/dist/wallet-registration-machine.d.cts +81 -0
- package/dist/wallet-registration-machine.d.cts.map +1 -0
- package/dist/wallet-registration-machine.d.mts +81 -0
- package/dist/wallet-registration-machine.d.mts.map +1 -0
- package/dist/wallet-registration-machine.mjs +139 -0
- package/dist/wallet-registration-machine.mjs.map +1 -0
- package/dist/wallet-registration-service.cjs +351 -0
- package/dist/wallet-registration-service.cjs.map +1 -0
- package/dist/wallet-registration-service.d.cts +144 -0
- package/dist/wallet-registration-service.d.cts.map +1 -0
- package/dist/wallet-registration-service.d.mts +144 -0
- package/dist/wallet-registration-service.d.mts.map +1 -0
- package/dist/wallet-registration-service.mjs +344 -0
- package/dist/wallet-registration-service.mjs.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _WalletRegistrationService_instances, _WalletRegistrationService_fetch, _WalletRegistrationService_baseUrl, _WalletRegistrationService_getAuthToken, _WalletRegistrationService_getExternalId, _WalletRegistrationService_toHttpError, _WalletRegistrationService_toRegistration;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.WalletRegistrationService = exports.extractErrorBody = exports.createIdempotencyKey = exports.WalletRegistrationError = void 0;
|
|
16
|
+
/** Typed error carrying enough context for state transitions. */
|
|
17
|
+
class WalletRegistrationError extends Error {
|
|
18
|
+
constructor(kind, options) {
|
|
19
|
+
super(options.message ?? `wallet registration failed: ${kind}`);
|
|
20
|
+
this.name = 'WalletRegistrationError';
|
|
21
|
+
this.kind = kind;
|
|
22
|
+
this.httpStatus = options.httpStatus;
|
|
23
|
+
this.body = options.body;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.WalletRegistrationError = WalletRegistrationError;
|
|
27
|
+
/**
|
|
28
|
+
* Normalizes a Monad EVM address for case-insensitive comparison.
|
|
29
|
+
*
|
|
30
|
+
* @param address - Raw address string.
|
|
31
|
+
* @returns The comparison key for the address.
|
|
32
|
+
*/
|
|
33
|
+
function normalizeAddress(address) {
|
|
34
|
+
return address.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Maps an HTTP status to the typed error kind the state machine reacts to.
|
|
38
|
+
*
|
|
39
|
+
* @param status - HTTP status code from the proxy/Iron response.
|
|
40
|
+
* @returns The corresponding error kind.
|
|
41
|
+
*/
|
|
42
|
+
function mapStatusToKind(status) {
|
|
43
|
+
switch (status) {
|
|
44
|
+
case 400:
|
|
45
|
+
return 'validation';
|
|
46
|
+
case 401:
|
|
47
|
+
return 'unauthorized';
|
|
48
|
+
case 403:
|
|
49
|
+
return 'forbidden';
|
|
50
|
+
case 404:
|
|
51
|
+
return 'notFound';
|
|
52
|
+
case 409:
|
|
53
|
+
return 'conflict';
|
|
54
|
+
case 429:
|
|
55
|
+
return 'rateLimited';
|
|
56
|
+
default:
|
|
57
|
+
return status >= 500 ? 'transient' : 'validation';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Builds a client-side Idempotency-Key for MoonPay POSTs. Prefer a stable
|
|
62
|
+
* caller-supplied key across retries of the same proof.
|
|
63
|
+
*
|
|
64
|
+
* @returns A random UUID when available, otherwise a timestamped fallback.
|
|
65
|
+
*/
|
|
66
|
+
function createIdempotencyKey() {
|
|
67
|
+
const cryptoObj = globalThis.crypto;
|
|
68
|
+
if (typeof cryptoObj?.randomUUID === 'function') {
|
|
69
|
+
return cryptoObj.randomUUID();
|
|
70
|
+
}
|
|
71
|
+
return `wallet-reg-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
72
|
+
}
|
|
73
|
+
exports.createIdempotencyKey = createIdempotencyKey;
|
|
74
|
+
/**
|
|
75
|
+
* Extracts a human-readable error body from a transparent neobank-proxy
|
|
76
|
+
* response. Upstream may return a plain string or a JSON value; both are
|
|
77
|
+
* mirrored 1:1 (no `{ code: 'iron_error' }` envelope).
|
|
78
|
+
*
|
|
79
|
+
* @param raw - Raw response text.
|
|
80
|
+
* @returns Normalized body string for {@link WalletRegistrationError}.
|
|
81
|
+
*/
|
|
82
|
+
function extractErrorBody(raw) {
|
|
83
|
+
const trimmed = raw.trim();
|
|
84
|
+
if (!trimmed) {
|
|
85
|
+
return raw;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(trimmed);
|
|
89
|
+
if (typeof parsed === 'string') {
|
|
90
|
+
return parsed;
|
|
91
|
+
}
|
|
92
|
+
if (parsed && typeof parsed === 'object') {
|
|
93
|
+
const { message } = parsed;
|
|
94
|
+
if (typeof message === 'string') {
|
|
95
|
+
return message;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return trimmed;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return trimmed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.extractErrorBody = extractErrorBody;
|
|
105
|
+
/**
|
|
106
|
+
* Data service that talks to the Money Movement neobank-proxy for MoonPay Iron
|
|
107
|
+
* self-hosted wallet registration. It never calls Iron directly, so the Iron
|
|
108
|
+
* API key never ships in the client.
|
|
109
|
+
*/
|
|
110
|
+
class WalletRegistrationService {
|
|
111
|
+
constructor(options) {
|
|
112
|
+
_WalletRegistrationService_instances.add(this);
|
|
113
|
+
_WalletRegistrationService_fetch.set(this, void 0);
|
|
114
|
+
_WalletRegistrationService_baseUrl.set(this, void 0);
|
|
115
|
+
_WalletRegistrationService_getAuthToken.set(this, void 0);
|
|
116
|
+
_WalletRegistrationService_getExternalId.set(this, void 0);
|
|
117
|
+
__classPrivateFieldSet(this, _WalletRegistrationService_fetch, options.fetch, "f");
|
|
118
|
+
__classPrivateFieldSet(this, _WalletRegistrationService_baseUrl, options.baseUrl.replace(/\/$/u, ''), "f");
|
|
119
|
+
__classPrivateFieldSet(this, _WalletRegistrationService_getAuthToken, options.getAuthToken, "f");
|
|
120
|
+
__classPrivateFieldSet(this, _WalletRegistrationService_getExternalId, options.getExternalId, "f");
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Resolves Iron's internal customer id via
|
|
124
|
+
* `GET /neobank/customers/{external_id}/external`, using the MetaMask
|
|
125
|
+
* profile/canonical id as `external_id`.
|
|
126
|
+
*
|
|
127
|
+
* @returns Iron's internal customer id.
|
|
128
|
+
*/
|
|
129
|
+
async getMoonpayCustomerId() {
|
|
130
|
+
const [token, externalId] = await Promise.all([
|
|
131
|
+
__classPrivateFieldGet(this, _WalletRegistrationService_getAuthToken, "f").call(this),
|
|
132
|
+
__classPrivateFieldGet(this, _WalletRegistrationService_getExternalId, "f").call(this),
|
|
133
|
+
]);
|
|
134
|
+
if (!externalId) {
|
|
135
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
136
|
+
message: 'MetaMask external id (canonical profile id) is empty',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const response = await __classPrivateFieldGet(this, _WalletRegistrationService_fetch, "f").call(this, `${__classPrivateFieldGet(this, _WalletRegistrationService_baseUrl, "f")}/neobank/customers/${encodeURIComponent(externalId)}/external`, {
|
|
140
|
+
method: 'GET',
|
|
141
|
+
headers: {
|
|
142
|
+
accept: 'application/json',
|
|
143
|
+
authorization: `Bearer ${token}`,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw await __classPrivateFieldGet(this, _WalletRegistrationService_instances, "m", _WalletRegistrationService_toHttpError).call(this, response);
|
|
148
|
+
}
|
|
149
|
+
let payload;
|
|
150
|
+
try {
|
|
151
|
+
payload = await response.json();
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
155
|
+
message: 'MoonPay customer body was not valid JSON',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
const { id } = payload;
|
|
159
|
+
if (typeof id !== 'string' || id.length === 0) {
|
|
160
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
161
|
+
message: 'MoonPay customer body missing id',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Reconciles a wallet against the customer's registered self-hosted addresses
|
|
168
|
+
* via `GET /neobank/addresses/crypto/{customer_id}?filter=SelfHosted`.
|
|
169
|
+
* Upstream returns all self-hosted chains; Monad filtering stays client-side
|
|
170
|
+
* for the POC. Transport failures are reported as `lookupUnavailable`;
|
|
171
|
+
* malformed successful responses are reported as `malformedResponse`.
|
|
172
|
+
* Neither is downgraded to `absent`.
|
|
173
|
+
*
|
|
174
|
+
* @param request - Customer id and Monad address to reconcile.
|
|
175
|
+
* @returns The active / disabled / absent status for the address.
|
|
176
|
+
*/
|
|
177
|
+
async getRegistrationStatus(request) {
|
|
178
|
+
const { customerId, address, blockchain } = request;
|
|
179
|
+
let response;
|
|
180
|
+
try {
|
|
181
|
+
const token = await __classPrivateFieldGet(this, _WalletRegistrationService_getAuthToken, "f").call(this);
|
|
182
|
+
const url = new URL(`${__classPrivateFieldGet(this, _WalletRegistrationService_baseUrl, "f")}/neobank/addresses/crypto/${encodeURIComponent(customerId)}`);
|
|
183
|
+
url.searchParams.set('filter', 'SelfHosted');
|
|
184
|
+
response = await __classPrivateFieldGet(this, _WalletRegistrationService_fetch, "f").call(this, url.toString(), {
|
|
185
|
+
method: 'GET',
|
|
186
|
+
headers: {
|
|
187
|
+
accept: 'application/json',
|
|
188
|
+
authorization: `Bearer ${token}`,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
throw new WalletRegistrationError('lookupUnavailable', {
|
|
194
|
+
message: 'self-hosted address lookup failed',
|
|
195
|
+
body: error instanceof Error ? error.message : undefined,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (!response.ok) {
|
|
199
|
+
let body;
|
|
200
|
+
try {
|
|
201
|
+
body = extractErrorBody(await response.text());
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
body = error instanceof Error ? error.message : '';
|
|
205
|
+
}
|
|
206
|
+
throw new WalletRegistrationError('lookupUnavailable', {
|
|
207
|
+
httpStatus: response.status,
|
|
208
|
+
body,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
let payload;
|
|
212
|
+
try {
|
|
213
|
+
payload = await response.json();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
217
|
+
message: 'self-hosted address list body was not valid JSON',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (!Array.isArray(payload)) {
|
|
221
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
222
|
+
message: 'expected an array of registered addresses',
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const target = normalizeAddress(address);
|
|
226
|
+
const match = payload.find((entry) => {
|
|
227
|
+
const record = entry;
|
|
228
|
+
const walletAddress = record.wallet_address;
|
|
229
|
+
if (typeof walletAddress !== 'string') {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
return (normalizeAddress(walletAddress) === target &&
|
|
233
|
+
record.blockchain === blockchain);
|
|
234
|
+
});
|
|
235
|
+
if (!match) {
|
|
236
|
+
return { type: 'absent' };
|
|
237
|
+
}
|
|
238
|
+
const registration = __classPrivateFieldGet(this, _WalletRegistrationService_instances, "m", _WalletRegistrationService_toRegistration).call(this, match);
|
|
239
|
+
return registration.disabled
|
|
240
|
+
? { type: 'disabled', registration }
|
|
241
|
+
: { type: 'active', registration };
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Registers a self-hosted wallet through neobank-proxy
|
|
245
|
+
* `POST /neobank/addresses/crypto/selfhosted`. The client supplies
|
|
246
|
+
* `customer_id` and an `Idempotency-Key` (generated when omitted). Every
|
|
247
|
+
* non-2xx response is mapped to a typed error; `409` is deliberately
|
|
248
|
+
* surfaced as an ambiguous `conflict` that the caller must reconcile with a
|
|
249
|
+
* follow-up status lookup.
|
|
250
|
+
*
|
|
251
|
+
* @param request - Customer id, address, blockchain, message, and signature.
|
|
252
|
+
* @returns The registered outcome on success.
|
|
253
|
+
*/
|
|
254
|
+
async registerSelfHostedWallet(request) {
|
|
255
|
+
const idempotencyKey = request.idempotencyKey ?? createIdempotencyKey();
|
|
256
|
+
let response;
|
|
257
|
+
try {
|
|
258
|
+
const token = await __classPrivateFieldGet(this, _WalletRegistrationService_getAuthToken, "f").call(this);
|
|
259
|
+
response = await __classPrivateFieldGet(this, _WalletRegistrationService_fetch, "f").call(this, `${__classPrivateFieldGet(this, _WalletRegistrationService_baseUrl, "f")}/neobank/addresses/crypto/selfhosted`, {
|
|
260
|
+
method: 'POST',
|
|
261
|
+
headers: {
|
|
262
|
+
accept: 'application/json',
|
|
263
|
+
'content-type': 'application/json',
|
|
264
|
+
authorization: `Bearer ${token}`,
|
|
265
|
+
'Idempotency-Key': idempotencyKey,
|
|
266
|
+
},
|
|
267
|
+
body: JSON.stringify({
|
|
268
|
+
customer_id: request.customerId,
|
|
269
|
+
address: request.address,
|
|
270
|
+
blockchain: request.blockchain,
|
|
271
|
+
message: request.message,
|
|
272
|
+
signature: request.signature,
|
|
273
|
+
}),
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
throw new WalletRegistrationError('transient', {
|
|
278
|
+
message: 'self-hosted registration request failed',
|
|
279
|
+
body: error instanceof Error ? error.message : undefined,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
throw await __classPrivateFieldGet(this, _WalletRegistrationService_instances, "m", _WalletRegistrationService_toHttpError).call(this, response);
|
|
284
|
+
}
|
|
285
|
+
let payload;
|
|
286
|
+
try {
|
|
287
|
+
payload = await response.json();
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
291
|
+
message: 'registration success body was not valid JSON',
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
const record = payload;
|
|
295
|
+
if (typeof record.id !== 'string' || typeof record.address !== 'string') {
|
|
296
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
297
|
+
message: 'registration success body missing id/address',
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
type: 'registered',
|
|
302
|
+
registration: {
|
|
303
|
+
id: record.id,
|
|
304
|
+
address: record.address,
|
|
305
|
+
blockchain: request.blockchain,
|
|
306
|
+
disabled: Boolean(record.disabled),
|
|
307
|
+
isSelf: true,
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
exports.WalletRegistrationService = WalletRegistrationService;
|
|
313
|
+
_WalletRegistrationService_fetch = new WeakMap(), _WalletRegistrationService_baseUrl = new WeakMap(), _WalletRegistrationService_getAuthToken = new WeakMap(), _WalletRegistrationService_getExternalId = new WeakMap(), _WalletRegistrationService_instances = new WeakSet(), _WalletRegistrationService_toHttpError = async function _WalletRegistrationService_toHttpError(response) {
|
|
314
|
+
let raw = '';
|
|
315
|
+
try {
|
|
316
|
+
raw = await response.text();
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return new WalletRegistrationError('malformedResponse', {
|
|
320
|
+
httpStatus: response.status,
|
|
321
|
+
message: 'error body could not be read',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
const { status } = response;
|
|
325
|
+
const kind = mapStatusToKind(status);
|
|
326
|
+
const body = extractErrorBody(raw);
|
|
327
|
+
return new WalletRegistrationError(kind, {
|
|
328
|
+
httpStatus: status,
|
|
329
|
+
body,
|
|
330
|
+
message: body || undefined,
|
|
331
|
+
});
|
|
332
|
+
}, _WalletRegistrationService_toRegistration = function _WalletRegistrationService_toRegistration(record) {
|
|
333
|
+
if (typeof record.id !== 'string' || record.id.length === 0) {
|
|
334
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
335
|
+
message: 'registered address missing id',
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
if (typeof record.wallet_address !== 'string') {
|
|
339
|
+
throw new WalletRegistrationError('malformedResponse', {
|
|
340
|
+
message: 'registered address missing wallet_address',
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
id: record.id,
|
|
345
|
+
address: record.wallet_address,
|
|
346
|
+
blockchain: 'Monad',
|
|
347
|
+
disabled: Boolean(record.disabled),
|
|
348
|
+
isSelf: Boolean(record.is_self),
|
|
349
|
+
};
|
|
350
|
+
};
|
|
351
|
+
//# sourceMappingURL=wallet-registration-service.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet-registration-service.cjs","sourceRoot":"","sources":["../src/wallet-registration-service.ts"],"names":[],"mappings":"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","sourcesContent":["/** The only blockchain supported by the Money Account POC. */\nexport type Blockchain = 'Monad';\n\n/** Normalized view of a single registered self-hosted address. */\nexport type SelfHostedRegistration = {\n id: string;\n address: string;\n blockchain: Blockchain;\n disabled: boolean;\n isSelf: boolean;\n};\n\n/** Result of reconciling a wallet against the customer's registered addresses. */\nexport type RegistrationStatus =\n | { type: 'active'; registration: SelfHostedRegistration }\n | { type: 'disabled'; registration: SelfHostedRegistration }\n | { type: 'absent' };\n\n/**\n * Discriminated error kinds surfaced to the state machine. Every non-success\n * path maps to exactly one of these so the machine can decide deterministically.\n */\nexport type WalletRegistrationErrorKind =\n | 'validation'\n | 'unauthorized'\n | 'forbidden'\n | 'notFound'\n | 'conflict'\n | 'rateLimited'\n | 'transient'\n | 'lookupUnavailable'\n | 'malformedResponse';\n\n/** Minimal HTTP response shape, so the service is environment-agnostic. */\ntype HttpResponse = {\n ok: boolean;\n status: number;\n json: () => Promise<unknown>;\n text: () => Promise<string>;\n};\n\n/** Minimal `fetch`-like function the service depends on. */\ntype FetchLike = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n) => Promise<HttpResponse>;\n\n/** Typed error carrying enough context for state transitions. */\nexport class WalletRegistrationError extends Error {\n readonly kind: WalletRegistrationErrorKind;\n\n readonly httpStatus?: number;\n\n readonly body?: string;\n\n constructor(\n kind: WalletRegistrationErrorKind,\n options: {\n message?: string;\n httpStatus?: number;\n body?: string;\n },\n ) {\n super(options.message ?? `wallet registration failed: ${kind}`);\n this.name = 'WalletRegistrationError';\n this.kind = kind;\n this.httpStatus = options.httpStatus;\n this.body = options.body;\n }\n}\n\nexport type WalletRegistrationServiceOptions = {\n fetch: FetchLike;\n /**\n * Base URL of the Money Movement neobank-proxy host\n * (e.g. `https://on-ramp.dev-api.cx.metamask.io`). Paths are under `/neobank`.\n */\n baseUrl: string;\n getAuthToken: () => Promise<string>;\n /**\n * MetaMask profile / partner external id used as MoonPay `external_id`\n * (typically `AuthenticationController:getSessionProfile().canonicalProfileId`).\n */\n getExternalId: () => Promise<string>;\n};\n\nexport type GetRegistrationStatusRequest = {\n customerId: string;\n address: string;\n blockchain: Blockchain;\n};\n\nexport type RegisterSelfHostedWalletRequest = {\n customerId: string;\n address: string;\n blockchain: Blockchain;\n message: string;\n signature: string;\n /**\n * Stable key reused across retries of the same ownership proof. Generated\n * when omitted.\n */\n idempotencyKey?: string;\n};\n\n/** Successful registration outcome. */\nexport type RegistrationOutcome = {\n type: 'registered';\n registration: SelfHostedRegistration;\n};\n\n/**\n * Normalizes a Monad EVM address for case-insensitive comparison.\n *\n * @param address - Raw address string.\n * @returns The comparison key for the address.\n */\nfunction normalizeAddress(address: string): string {\n return address.toLowerCase();\n}\n\n/**\n * Maps an HTTP status to the typed error kind the state machine reacts to.\n *\n * @param status - HTTP status code from the proxy/Iron response.\n * @returns The corresponding error kind.\n */\nfunction mapStatusToKind(status: number): WalletRegistrationErrorKind {\n switch (status) {\n case 400:\n return 'validation';\n case 401:\n return 'unauthorized';\n case 403:\n return 'forbidden';\n case 404:\n return 'notFound';\n case 409:\n return 'conflict';\n case 429:\n return 'rateLimited';\n default:\n return status >= 500 ? 'transient' : 'validation';\n }\n}\n\n/**\n * Builds a client-side Idempotency-Key for MoonPay POSTs. Prefer a stable\n * caller-supplied key across retries of the same proof.\n *\n * @returns A random UUID when available, otherwise a timestamped fallback.\n */\nexport function createIdempotencyKey(): string {\n const cryptoObj = globalThis.crypto as\n | { randomUUID?: () => string }\n | undefined;\n if (typeof cryptoObj?.randomUUID === 'function') {\n return cryptoObj.randomUUID();\n }\n return `wallet-reg-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n}\n\n/**\n * Extracts a human-readable error body from a transparent neobank-proxy\n * response. Upstream may return a plain string or a JSON value; both are\n * mirrored 1:1 (no `{ code: 'iron_error' }` envelope).\n *\n * @param raw - Raw response text.\n * @returns Normalized body string for {@link WalletRegistrationError}.\n */\nexport function extractErrorBody(raw: string): string {\n const trimmed = raw.trim();\n if (!trimmed) {\n return raw;\n }\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (typeof parsed === 'string') {\n return parsed;\n }\n if (parsed && typeof parsed === 'object') {\n const { message } = parsed as { message?: unknown };\n if (typeof message === 'string') {\n return message;\n }\n }\n return trimmed;\n } catch {\n return trimmed;\n }\n}\n\n/**\n * Data service that talks to the Money Movement neobank-proxy for MoonPay Iron\n * self-hosted wallet registration. It never calls Iron directly, so the Iron\n * API key never ships in the client.\n */\nexport class WalletRegistrationService {\n readonly #fetch: FetchLike;\n\n readonly #baseUrl: string;\n\n readonly #getAuthToken: () => Promise<string>;\n\n readonly #getExternalId: () => Promise<string>;\n\n constructor(options: WalletRegistrationServiceOptions) {\n this.#fetch = options.fetch;\n this.#baseUrl = options.baseUrl.replace(/\\/$/u, '');\n this.#getAuthToken = options.getAuthToken;\n this.#getExternalId = options.getExternalId;\n }\n\n /**\n * Resolves Iron's internal customer id via\n * `GET /neobank/customers/{external_id}/external`, using the MetaMask\n * profile/canonical id as `external_id`.\n *\n * @returns Iron's internal customer id.\n */\n async getMoonpayCustomerId(): Promise<string> {\n const [token, externalId] = await Promise.all([\n this.#getAuthToken(),\n this.#getExternalId(),\n ]);\n if (!externalId) {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'MetaMask external id (canonical profile id) is empty',\n });\n }\n\n const response = await this.#fetch(\n `${this.#baseUrl}/neobank/customers/${encodeURIComponent(externalId)}/external`,\n {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${token}`,\n },\n },\n );\n\n if (!response.ok) {\n throw await this.#toHttpError(response);\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'MoonPay customer body was not valid JSON',\n });\n }\n\n const { id } = payload as { id?: unknown };\n if (typeof id !== 'string' || id.length === 0) {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'MoonPay customer body missing id',\n });\n }\n return id;\n }\n\n /**\n * Reconciles a wallet against the customer's registered self-hosted addresses\n * via `GET /neobank/addresses/crypto/{customer_id}?filter=SelfHosted`.\n * Upstream returns all self-hosted chains; Monad filtering stays client-side\n * for the POC. Transport failures are reported as `lookupUnavailable`;\n * malformed successful responses are reported as `malformedResponse`.\n * Neither is downgraded to `absent`.\n *\n * @param request - Customer id and Monad address to reconcile.\n * @returns The active / disabled / absent status for the address.\n */\n async getRegistrationStatus(\n request: GetRegistrationStatusRequest,\n ): Promise<RegistrationStatus> {\n const { customerId, address, blockchain } = request;\n\n let response: HttpResponse;\n try {\n const token = await this.#getAuthToken();\n const url = new URL(\n `${this.#baseUrl}/neobank/addresses/crypto/${encodeURIComponent(customerId)}`,\n );\n url.searchParams.set('filter', 'SelfHosted');\n response = await this.#fetch(url.toString(), {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${token}`,\n },\n });\n } catch (error) {\n throw new WalletRegistrationError('lookupUnavailable', {\n message: 'self-hosted address lookup failed',\n body: error instanceof Error ? error.message : undefined,\n });\n }\n\n if (!response.ok) {\n let body: string;\n try {\n body = extractErrorBody(await response.text());\n } catch (error) {\n body = error instanceof Error ? error.message : '';\n }\n throw new WalletRegistrationError('lookupUnavailable', {\n httpStatus: response.status,\n body,\n });\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'self-hosted address list body was not valid JSON',\n });\n }\n if (!Array.isArray(payload)) {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'expected an array of registered addresses',\n });\n }\n\n const target = normalizeAddress(address);\n const match = payload.find((entry) => {\n const record = entry as Record<string, unknown>;\n const walletAddress = record.wallet_address;\n if (typeof walletAddress !== 'string') {\n return false;\n }\n return (\n normalizeAddress(walletAddress) === target &&\n record.blockchain === blockchain\n );\n }) as Record<string, unknown> | undefined;\n\n if (!match) {\n return { type: 'absent' };\n }\n\n const registration = this.#toRegistration(match);\n return registration.disabled\n ? { type: 'disabled', registration }\n : { type: 'active', registration };\n }\n\n /**\n * Registers a self-hosted wallet through neobank-proxy\n * `POST /neobank/addresses/crypto/selfhosted`. The client supplies\n * `customer_id` and an `Idempotency-Key` (generated when omitted). Every\n * non-2xx response is mapped to a typed error; `409` is deliberately\n * surfaced as an ambiguous `conflict` that the caller must reconcile with a\n * follow-up status lookup.\n *\n * @param request - Customer id, address, blockchain, message, and signature.\n * @returns The registered outcome on success.\n */\n async registerSelfHostedWallet(\n request: RegisterSelfHostedWalletRequest,\n ): Promise<RegistrationOutcome> {\n const idempotencyKey = request.idempotencyKey ?? createIdempotencyKey();\n let response: HttpResponse;\n try {\n const token = await this.#getAuthToken();\n response = await this.#fetch(\n `${this.#baseUrl}/neobank/addresses/crypto/selfhosted`,\n {\n method: 'POST',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n authorization: `Bearer ${token}`,\n 'Idempotency-Key': idempotencyKey,\n },\n body: JSON.stringify({\n customer_id: request.customerId,\n address: request.address,\n blockchain: request.blockchain,\n message: request.message,\n signature: request.signature,\n }),\n },\n );\n } catch (error) {\n throw new WalletRegistrationError('transient', {\n message: 'self-hosted registration request failed',\n body: error instanceof Error ? error.message : undefined,\n });\n }\n\n if (!response.ok) {\n throw await this.#toHttpError(response);\n }\n\n let payload: unknown;\n try {\n payload = await response.json();\n } catch {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'registration success body was not valid JSON',\n });\n }\n\n const record = payload as Record<string, unknown>;\n if (typeof record.id !== 'string' || typeof record.address !== 'string') {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'registration success body missing id/address',\n });\n }\n\n return {\n type: 'registered',\n registration: {\n id: record.id,\n address: record.address,\n blockchain: request.blockchain,\n disabled: Boolean(record.disabled),\n isSelf: true,\n },\n };\n }\n\n async #toHttpError(response: HttpResponse): Promise<WalletRegistrationError> {\n let raw = '';\n try {\n raw = await response.text();\n } catch {\n return new WalletRegistrationError('malformedResponse', {\n httpStatus: response.status,\n message: 'error body could not be read',\n });\n }\n\n const { status } = response;\n const kind = mapStatusToKind(status);\n const body = extractErrorBody(raw);\n return new WalletRegistrationError(kind, {\n httpStatus: status,\n body,\n message: body || undefined,\n });\n }\n\n #toRegistration(record: Record<string, unknown>): SelfHostedRegistration {\n if (typeof record.id !== 'string' || record.id.length === 0) {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'registered address missing id',\n });\n }\n if (typeof record.wallet_address !== 'string') {\n throw new WalletRegistrationError('malformedResponse', {\n message: 'registered address missing wallet_address',\n });\n }\n return {\n id: record.id,\n address: record.wallet_address,\n blockchain: 'Monad',\n disabled: Boolean(record.disabled),\n isSelf: Boolean(record.is_self),\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/** The only blockchain supported by the Money Account POC. */
|
|
2
|
+
export type Blockchain = 'Monad';
|
|
3
|
+
/** Normalized view of a single registered self-hosted address. */
|
|
4
|
+
export type SelfHostedRegistration = {
|
|
5
|
+
id: string;
|
|
6
|
+
address: string;
|
|
7
|
+
blockchain: Blockchain;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
isSelf: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Result of reconciling a wallet against the customer's registered addresses. */
|
|
12
|
+
export type RegistrationStatus = {
|
|
13
|
+
type: 'active';
|
|
14
|
+
registration: SelfHostedRegistration;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'disabled';
|
|
17
|
+
registration: SelfHostedRegistration;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'absent';
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Discriminated error kinds surfaced to the state machine. Every non-success
|
|
23
|
+
* path maps to exactly one of these so the machine can decide deterministically.
|
|
24
|
+
*/
|
|
25
|
+
export type WalletRegistrationErrorKind = 'validation' | 'unauthorized' | 'forbidden' | 'notFound' | 'conflict' | 'rateLimited' | 'transient' | 'lookupUnavailable' | 'malformedResponse';
|
|
26
|
+
/** Minimal HTTP response shape, so the service is environment-agnostic. */
|
|
27
|
+
type HttpResponse = {
|
|
28
|
+
ok: boolean;
|
|
29
|
+
status: number;
|
|
30
|
+
json: () => Promise<unknown>;
|
|
31
|
+
text: () => Promise<string>;
|
|
32
|
+
};
|
|
33
|
+
/** Minimal `fetch`-like function the service depends on. */
|
|
34
|
+
type FetchLike = (url: string, init?: {
|
|
35
|
+
method?: string;
|
|
36
|
+
headers?: Record<string, string>;
|
|
37
|
+
body?: string;
|
|
38
|
+
}) => Promise<HttpResponse>;
|
|
39
|
+
/** Typed error carrying enough context for state transitions. */
|
|
40
|
+
export declare class WalletRegistrationError extends Error {
|
|
41
|
+
readonly kind: WalletRegistrationErrorKind;
|
|
42
|
+
readonly httpStatus?: number;
|
|
43
|
+
readonly body?: string;
|
|
44
|
+
constructor(kind: WalletRegistrationErrorKind, options: {
|
|
45
|
+
message?: string;
|
|
46
|
+
httpStatus?: number;
|
|
47
|
+
body?: string;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export type WalletRegistrationServiceOptions = {
|
|
51
|
+
fetch: FetchLike;
|
|
52
|
+
/**
|
|
53
|
+
* Base URL of the Money Movement neobank-proxy host
|
|
54
|
+
* (e.g. `https://on-ramp.dev-api.cx.metamask.io`). Paths are under `/neobank`.
|
|
55
|
+
*/
|
|
56
|
+
baseUrl: string;
|
|
57
|
+
getAuthToken: () => Promise<string>;
|
|
58
|
+
/**
|
|
59
|
+
* MetaMask profile / partner external id used as MoonPay `external_id`
|
|
60
|
+
* (typically `AuthenticationController:getSessionProfile().canonicalProfileId`).
|
|
61
|
+
*/
|
|
62
|
+
getExternalId: () => Promise<string>;
|
|
63
|
+
};
|
|
64
|
+
export type GetRegistrationStatusRequest = {
|
|
65
|
+
customerId: string;
|
|
66
|
+
address: string;
|
|
67
|
+
blockchain: Blockchain;
|
|
68
|
+
};
|
|
69
|
+
export type RegisterSelfHostedWalletRequest = {
|
|
70
|
+
customerId: string;
|
|
71
|
+
address: string;
|
|
72
|
+
blockchain: Blockchain;
|
|
73
|
+
message: string;
|
|
74
|
+
signature: string;
|
|
75
|
+
/**
|
|
76
|
+
* Stable key reused across retries of the same ownership proof. Generated
|
|
77
|
+
* when omitted.
|
|
78
|
+
*/
|
|
79
|
+
idempotencyKey?: string;
|
|
80
|
+
};
|
|
81
|
+
/** Successful registration outcome. */
|
|
82
|
+
export type RegistrationOutcome = {
|
|
83
|
+
type: 'registered';
|
|
84
|
+
registration: SelfHostedRegistration;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Builds a client-side Idempotency-Key for MoonPay POSTs. Prefer a stable
|
|
88
|
+
* caller-supplied key across retries of the same proof.
|
|
89
|
+
*
|
|
90
|
+
* @returns A random UUID when available, otherwise a timestamped fallback.
|
|
91
|
+
*/
|
|
92
|
+
export declare function createIdempotencyKey(): string;
|
|
93
|
+
/**
|
|
94
|
+
* Extracts a human-readable error body from a transparent neobank-proxy
|
|
95
|
+
* response. Upstream may return a plain string or a JSON value; both are
|
|
96
|
+
* mirrored 1:1 (no `{ code: 'iron_error' }` envelope).
|
|
97
|
+
*
|
|
98
|
+
* @param raw - Raw response text.
|
|
99
|
+
* @returns Normalized body string for {@link WalletRegistrationError}.
|
|
100
|
+
*/
|
|
101
|
+
export declare function extractErrorBody(raw: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* Data service that talks to the Money Movement neobank-proxy for MoonPay Iron
|
|
104
|
+
* self-hosted wallet registration. It never calls Iron directly, so the Iron
|
|
105
|
+
* API key never ships in the client.
|
|
106
|
+
*/
|
|
107
|
+
export declare class WalletRegistrationService {
|
|
108
|
+
#private;
|
|
109
|
+
constructor(options: WalletRegistrationServiceOptions);
|
|
110
|
+
/**
|
|
111
|
+
* Resolves Iron's internal customer id via
|
|
112
|
+
* `GET /neobank/customers/{external_id}/external`, using the MetaMask
|
|
113
|
+
* profile/canonical id as `external_id`.
|
|
114
|
+
*
|
|
115
|
+
* @returns Iron's internal customer id.
|
|
116
|
+
*/
|
|
117
|
+
getMoonpayCustomerId(): Promise<string>;
|
|
118
|
+
/**
|
|
119
|
+
* Reconciles a wallet against the customer's registered self-hosted addresses
|
|
120
|
+
* via `GET /neobank/addresses/crypto/{customer_id}?filter=SelfHosted`.
|
|
121
|
+
* Upstream returns all self-hosted chains; Monad filtering stays client-side
|
|
122
|
+
* for the POC. Transport failures are reported as `lookupUnavailable`;
|
|
123
|
+
* malformed successful responses are reported as `malformedResponse`.
|
|
124
|
+
* Neither is downgraded to `absent`.
|
|
125
|
+
*
|
|
126
|
+
* @param request - Customer id and Monad address to reconcile.
|
|
127
|
+
* @returns The active / disabled / absent status for the address.
|
|
128
|
+
*/
|
|
129
|
+
getRegistrationStatus(request: GetRegistrationStatusRequest): Promise<RegistrationStatus>;
|
|
130
|
+
/**
|
|
131
|
+
* Registers a self-hosted wallet through neobank-proxy
|
|
132
|
+
* `POST /neobank/addresses/crypto/selfhosted`. The client supplies
|
|
133
|
+
* `customer_id` and an `Idempotency-Key` (generated when omitted). Every
|
|
134
|
+
* non-2xx response is mapped to a typed error; `409` is deliberately
|
|
135
|
+
* surfaced as an ambiguous `conflict` that the caller must reconcile with a
|
|
136
|
+
* follow-up status lookup.
|
|
137
|
+
*
|
|
138
|
+
* @param request - Customer id, address, blockchain, message, and signature.
|
|
139
|
+
* @returns The registered outcome on success.
|
|
140
|
+
*/
|
|
141
|
+
registerSelfHostedWallet(request: RegisterSelfHostedWalletRequest): Promise<RegistrationOutcome>;
|
|
142
|
+
}
|
|
143
|
+
export {};
|
|
144
|
+
//# sourceMappingURL=wallet-registration-service.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet-registration-service.d.cts","sourceRoot":"","sources":["../src/wallet-registration-service.ts"],"names":[],"mappings":"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"}
|