@usdctofiat/offramp 3.0.2 → 3.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 +21 -0
- package/README.md +129 -24
- package/dist/{chunk-F7DVUBMT.js → chunk-XQ7HOLLB.js} +390 -445
- package/dist/chunk-XQ7HOLLB.js.map +1 -0
- package/dist/errors-DzTiflBh.d.cts +409 -0
- package/dist/errors-DzTiflBh.d.ts +409 -0
- package/dist/index.cjs +408 -441
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +481 -460
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +23 -13
- package/dist/react.d.ts +23 -13
- package/dist/react.js +106 -27
- package/dist/react.js.map +1 -1
- package/package.json +5 -3
- package/dist/chunk-F7DVUBMT.js.map +0 -1
- package/dist/errors-DKQ2N_ih.d.cts +0 -217
- package/dist/errors-DKQ2N_ih.d.ts +0 -217
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -28,9 +38,11 @@ __export(index_exports, {
|
|
|
28
38
|
OfframpError: () => OfframpError,
|
|
29
39
|
PEER_EXTENSION_CHROME_URL: () => import_sdk7.PEER_EXTENSION_CHROME_URL,
|
|
30
40
|
PLATFORMS: () => PLATFORMS,
|
|
41
|
+
PLATFORM_LIMITS: () => PLATFORM_LIMITS,
|
|
31
42
|
close: () => close,
|
|
43
|
+
completePeerExtensionRegistration: () => completePeerExtensionRegistration,
|
|
32
44
|
createOfframp: () => createOfframp,
|
|
33
|
-
createPeerExtensionSdk: () =>
|
|
45
|
+
createPeerExtensionSdk: () => createPeerExtensionSdk,
|
|
34
46
|
createTelemetryContext: () => createTelemetryContext,
|
|
35
47
|
delegate: () => delegate,
|
|
36
48
|
deposits: () => deposits,
|
|
@@ -38,14 +50,18 @@ __export(index_exports, {
|
|
|
38
50
|
emitEvent: () => emitEvent,
|
|
39
51
|
enableOtc: () => enableOtc,
|
|
40
52
|
getOtcLink: () => getOtcLink,
|
|
53
|
+
getPeerExtensionRegistrationAuthParams: () => getPeerExtensionRegistrationAuthParams,
|
|
41
54
|
getPeerExtensionRegistrationInfo: () => getPeerExtensionRegistrationInfo,
|
|
42
|
-
getPeerExtensionState: () =>
|
|
43
|
-
|
|
55
|
+
getPeerExtensionState: () => getPeerExtensionState,
|
|
56
|
+
getPlatformLimits: () => getPlatformLimits,
|
|
57
|
+
isPeerExtensionAvailable: () => isPeerExtensionAvailable,
|
|
58
|
+
isPeerExtensionMetadataBridgeAvailable: () => isPeerExtensionMetadataBridgeAvailable,
|
|
44
59
|
isPeerExtensionRegistrationError: () => isPeerExtensionRegistrationError,
|
|
60
|
+
isValidIBAN: () => isValidIBAN,
|
|
45
61
|
normalizePaypalMeUsername: () => normalizePaypalMeUsername,
|
|
46
62
|
offramp: () => offramp,
|
|
47
|
-
openPeerExtensionInstallPage: () =>
|
|
48
|
-
peerExtensionSdk: () =>
|
|
63
|
+
openPeerExtensionInstallPage: () => openPeerExtensionInstallPage,
|
|
64
|
+
peerExtensionSdk: () => peerExtensionSdk,
|
|
49
65
|
sanitizeAttributionId: () => sanitizeAttributionId,
|
|
50
66
|
sendTelemetryEvent: () => sendTelemetryEvent,
|
|
51
67
|
undelegate: () => undelegate
|
|
@@ -59,11 +75,15 @@ var import_sdk5 = require("@zkp2p/sdk");
|
|
|
59
75
|
|
|
60
76
|
// src/config.ts
|
|
61
77
|
var import_sdk = require("@zkp2p/sdk");
|
|
62
|
-
var SDK_VERSION = "3.0
|
|
78
|
+
var SDK_VERSION = "3.1.0";
|
|
63
79
|
var BASE_CHAIN_ID = 8453;
|
|
64
80
|
var RUNTIME_ENV = "production";
|
|
65
81
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
66
82
|
var BASE_RPC_URL = "https://mainnet.base.org";
|
|
83
|
+
function resolveApiBaseUrl(override) {
|
|
84
|
+
const trimmed = override?.trim();
|
|
85
|
+
return (trimmed && trimmed.length > 0 ? trimmed : API_BASE_URL).replace(/\/$/, "");
|
|
86
|
+
}
|
|
67
87
|
var contracts = (0, import_sdk.getContracts)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
68
88
|
var addresses = contracts.addresses;
|
|
69
89
|
var addrs = addresses;
|
|
@@ -80,6 +100,7 @@ var DEFAULT_INTENT_GUARDIAN_ADDRESS = "0xe29a5BD4D0CEbA6C125A0361E7D20ab4eA275C2
|
|
|
80
100
|
var REFERRER = "galleonlabs";
|
|
81
101
|
var MIN_DEPOSIT_USDC = 1;
|
|
82
102
|
var MIN_ORDER_USDC = 1;
|
|
103
|
+
var MAX_ORDER_USDC = 2500;
|
|
83
104
|
var INDEXER_MAX_ATTEMPTS = 12;
|
|
84
105
|
var INDEXER_INITIAL_DELAY_MS = 1e3;
|
|
85
106
|
var INDEXER_MAX_DELAY_MS = 1e4;
|
|
@@ -88,6 +109,41 @@ var PAYEE_REGISTRATION_TIMEOUT_MS = 8e3;
|
|
|
88
109
|
// src/platforms.ts
|
|
89
110
|
var import_sdk2 = require("@zkp2p/sdk");
|
|
90
111
|
var import_zod = require("zod");
|
|
112
|
+
|
|
113
|
+
// src/errors.ts
|
|
114
|
+
var MakersCreateError = class extends Error {
|
|
115
|
+
status;
|
|
116
|
+
body;
|
|
117
|
+
constructor(message, status, body) {
|
|
118
|
+
super(message);
|
|
119
|
+
this.name = "MakersCreateError";
|
|
120
|
+
this.status = status;
|
|
121
|
+
this.body = body;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var OfframpError = class extends Error {
|
|
125
|
+
code;
|
|
126
|
+
step;
|
|
127
|
+
cause;
|
|
128
|
+
txHash;
|
|
129
|
+
depositId;
|
|
130
|
+
constructor(message, code, step, cause, details) {
|
|
131
|
+
super(message);
|
|
132
|
+
this.name = "OfframpError";
|
|
133
|
+
this.code = code;
|
|
134
|
+
this.step = step;
|
|
135
|
+
this.cause = cause;
|
|
136
|
+
this.txHash = details?.txHash;
|
|
137
|
+
this.depositId = details?.depositId;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
function isUserCancellation(error) {
|
|
141
|
+
if (!(error instanceof Error)) return false;
|
|
142
|
+
const msg = error.message.toLowerCase();
|
|
143
|
+
return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("user cancelled") || msg.includes("rejected the request") || msg.includes("action_rejected");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/platforms.ts
|
|
91
147
|
var PAYMENT_CATALOG = (0, import_sdk2.getPaymentMethodsCatalog)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
92
148
|
var ZELLE_HASH_LOOKUP_NAMES = ["zelle", "zelle-bofa", "zelle-chase", "zelle-citi"];
|
|
93
149
|
var FALLBACK_CURRENCIES = {
|
|
@@ -156,7 +212,8 @@ var FALLBACK_CURRENCIES = {
|
|
|
156
212
|
zelle: ["USD"],
|
|
157
213
|
paypal: ["USD", "EUR", "GBP", "SGD", "NZD", "AUD", "CAD"],
|
|
158
214
|
monzo: ["GBP"],
|
|
159
|
-
n26: ["EUR"]
|
|
215
|
+
n26: ["EUR"],
|
|
216
|
+
luxon: ["USD", "EUR", "GBP"]
|
|
160
217
|
};
|
|
161
218
|
function gatherCatalogHashes(platform) {
|
|
162
219
|
if (platform === "zelle") {
|
|
@@ -180,13 +237,39 @@ function resolveSupportedCurrencies(platform) {
|
|
|
180
237
|
function normalizePaypalMeUsername(value) {
|
|
181
238
|
const trimmed = value.trim();
|
|
182
239
|
if (!trimmed) return "";
|
|
183
|
-
const withoutProtocol = trimmed.replace(/^https?:\/\//i, "")
|
|
184
|
-
|
|
185
|
-
const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
|
|
186
|
-
const [pathWithoutQuery] = withoutDomain.split(/[?#]/, 1);
|
|
240
|
+
const withoutProtocol = trimmed.replace(/^https?:\/\//i, "");
|
|
241
|
+
const [pathWithoutQuery] = withoutProtocol.split(/[?#]/, 1);
|
|
187
242
|
const sanitizedPath = pathWithoutQuery.replace(/^\/+/, "");
|
|
188
|
-
const [
|
|
189
|
-
|
|
243
|
+
const [host = "", ...pathSegments] = sanitizedPath.split("/");
|
|
244
|
+
const normalizedHost = host.toLowerCase().replace(/^www\./i, "");
|
|
245
|
+
if (normalizedHost === "paypal.me") {
|
|
246
|
+
const [username = ""] = pathSegments;
|
|
247
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
248
|
+
}
|
|
249
|
+
if (normalizedHost === "paypal.com") {
|
|
250
|
+
const [paypalMePath = "", username = ""] = pathSegments;
|
|
251
|
+
if (paypalMePath.toLowerCase() !== "paypalme") return "";
|
|
252
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
253
|
+
}
|
|
254
|
+
if (/^https?:\/\//i.test(trimmed) || trimmed.includes("/")) {
|
|
255
|
+
return trimmed;
|
|
256
|
+
}
|
|
257
|
+
return trimmed.replace(/^@+/, "").toLowerCase();
|
|
258
|
+
}
|
|
259
|
+
function isValidIBAN(iban) {
|
|
260
|
+
const upper = iban.toUpperCase();
|
|
261
|
+
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(upper)) return false;
|
|
262
|
+
const rearranged = upper.slice(4) + upper.slice(0, 4);
|
|
263
|
+
const numeric = rearranged.split("").map((ch) => {
|
|
264
|
+
const code = ch.charCodeAt(0);
|
|
265
|
+
return code >= 65 && code <= 90 ? (code - 55).toString() : ch;
|
|
266
|
+
}).join("");
|
|
267
|
+
let remainder = numeric;
|
|
268
|
+
while (remainder.length > 2) {
|
|
269
|
+
const block = remainder.slice(0, 9);
|
|
270
|
+
remainder = (parseInt(block, 10) % 97).toString() + remainder.slice(block.length);
|
|
271
|
+
}
|
|
272
|
+
return parseInt(remainder, 10) % 97 === 1;
|
|
190
273
|
}
|
|
191
274
|
var BLUEPRINTS = {
|
|
192
275
|
venmo: {
|
|
@@ -238,9 +321,9 @@ var BLUEPRINTS = {
|
|
|
238
321
|
transform: (v) => v.replace(/^@+/, "").trim(),
|
|
239
322
|
extensionRegistration: {
|
|
240
323
|
providerId: "wise",
|
|
241
|
-
requiredPrompt: "This Wisetag is not registered yet.
|
|
242
|
-
ctaLabel: "
|
|
243
|
-
minExtensionVersion: "0.
|
|
324
|
+
requiredPrompt: "This Wisetag is not registered yet. Register it with PeerAuth, then retry with the same Wisetag.",
|
|
325
|
+
ctaLabel: "Register Wisetag",
|
|
326
|
+
minExtensionVersion: "0.5.0"
|
|
244
327
|
}
|
|
245
328
|
},
|
|
246
329
|
mercadopago: {
|
|
@@ -257,7 +340,11 @@ var BLUEPRINTS = {
|
|
|
257
340
|
identifierLabel: "Email",
|
|
258
341
|
placeholder: "email",
|
|
259
342
|
helperText: "Registered Zelle email",
|
|
260
|
-
|
|
343
|
+
// Curator's ZelleProcessor rejects any non-lowercase email outright, so
|
|
344
|
+
// canonicalize to lowercase here rather than letting /v2/makers/create
|
|
345
|
+
// reject after the maker has already approved USDC + paid gas.
|
|
346
|
+
validation: import_zod.z.string().email(),
|
|
347
|
+
transform: (v) => v.trim().toLowerCase()
|
|
261
348
|
},
|
|
262
349
|
paypal: {
|
|
263
350
|
id: "paypal",
|
|
@@ -265,14 +352,17 @@ var BLUEPRINTS = {
|
|
|
265
352
|
identifierLabel: "PayPal.me Username",
|
|
266
353
|
placeholder: "paypal.me/your-username",
|
|
267
354
|
helperText: "Your PayPal.me username, not your email",
|
|
268
|
-
validation: import_zod.z.string().min(1, "PayPal.me username is required").regex(
|
|
355
|
+
validation: import_zod.z.string().min(1, "PayPal.me username is required").regex(
|
|
356
|
+
/^[a-z0-9._-]+$/i,
|
|
357
|
+
"Use your PayPal.me username or paypal.me link, not a shortened URL."
|
|
358
|
+
),
|
|
269
359
|
transform: normalizePaypalMeUsername,
|
|
270
360
|
extensionRegistration: {
|
|
271
361
|
providerId: "paypal",
|
|
272
|
-
requiredPrompt: "This PayPal username is not registered yet.
|
|
273
|
-
ctaLabel: "
|
|
362
|
+
requiredPrompt: "This PayPal username is not registered yet. Register it with PeerAuth, then retry with the same PayPal username.",
|
|
363
|
+
ctaLabel: "Register PayPal Username",
|
|
274
364
|
ctaSubtext: "PayPal Business accounts are not supported at this time.",
|
|
275
|
-
minExtensionVersion: "0.
|
|
365
|
+
minExtensionVersion: "0.5.0"
|
|
276
366
|
}
|
|
277
367
|
},
|
|
278
368
|
monzo: {
|
|
@@ -289,8 +379,20 @@ var BLUEPRINTS = {
|
|
|
289
379
|
identifierLabel: "IBAN",
|
|
290
380
|
placeholder: "IBAN (e.g. DE89...)",
|
|
291
381
|
helperText: "Your IBAN (spaces will be removed)",
|
|
292
|
-
validation
|
|
382
|
+
// transform runs before validation, so the schema sees a spaceless,
|
|
383
|
+
// uppercased IBAN. The MOD-97 refine mirrors curator's N26Processor so a
|
|
384
|
+
// typo'd-but-well-formed IBAN fails here, not after USDC approval + gas.
|
|
385
|
+
validation: import_zod.z.string().regex(/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/, "Enter a valid IBAN").refine(isValidIBAN, "That IBAN looks wrong \u2014 its checksum doesn't match"),
|
|
293
386
|
transform: (v) => v.replace(/\s/g, "").toUpperCase()
|
|
387
|
+
},
|
|
388
|
+
luxon: {
|
|
389
|
+
id: "luxon",
|
|
390
|
+
name: "Luxon",
|
|
391
|
+
identifierLabel: "Email",
|
|
392
|
+
placeholder: "your-luxon-email@example.com",
|
|
393
|
+
helperText: "Email address registered with Luxon",
|
|
394
|
+
validation: import_zod.z.string().email(),
|
|
395
|
+
transform: (v) => v.trim()
|
|
294
396
|
}
|
|
295
397
|
};
|
|
296
398
|
function buildPlatformEntry(bp) {
|
|
@@ -329,8 +431,18 @@ var PLATFORMS = {
|
|
|
329
431
|
ZELLE: buildPlatformEntry(BLUEPRINTS.zelle),
|
|
330
432
|
PAYPAL: buildPlatformEntry(BLUEPRINTS.paypal),
|
|
331
433
|
MONZO: buildPlatformEntry(BLUEPRINTS.monzo),
|
|
332
|
-
N26: buildPlatformEntry(BLUEPRINTS.n26)
|
|
434
|
+
N26: buildPlatformEntry(BLUEPRINTS.n26),
|
|
435
|
+
LUXON: buildPlatformEntry(BLUEPRINTS.luxon)
|
|
333
436
|
};
|
|
437
|
+
var PLATFORM_LIMITS = {
|
|
438
|
+
venmo: { platformCapUsd: 5e3 },
|
|
439
|
+
n26: { platformCapUsd: 1e4 },
|
|
440
|
+
paypal: { minTier: "PLUS" }
|
|
441
|
+
};
|
|
442
|
+
function getPlatformLimits(platform) {
|
|
443
|
+
const id = platform.trim().toLowerCase();
|
|
444
|
+
return PLATFORM_LIMITS[id] ?? null;
|
|
445
|
+
}
|
|
334
446
|
function getPlatformById(id) {
|
|
335
447
|
return Object.values(PLATFORMS).find((p) => p.id === id) ?? null;
|
|
336
448
|
}
|
|
@@ -392,6 +504,146 @@ function getPeerExtensionRegistrationInfo(platform) {
|
|
|
392
504
|
const entry = getPlatformById(platform);
|
|
393
505
|
return entry?.extensionRegistration ?? null;
|
|
394
506
|
}
|
|
507
|
+
function getPeerExtensionRegistrationAuthParams(platform, options = {}) {
|
|
508
|
+
const info = getPeerExtensionRegistrationInfo(platform);
|
|
509
|
+
if (!info) return null;
|
|
510
|
+
const params = {
|
|
511
|
+
actionType: `transfer_${info.providerId}`,
|
|
512
|
+
captureMode: "sellerCredential",
|
|
513
|
+
platform: info.providerId
|
|
514
|
+
};
|
|
515
|
+
if (options.attestationServiceUrl) {
|
|
516
|
+
params.attestationServiceUrl = options.attestationServiceUrl;
|
|
517
|
+
}
|
|
518
|
+
return params;
|
|
519
|
+
}
|
|
520
|
+
function resolveRegistrationProcessor(platform) {
|
|
521
|
+
const info = getPeerExtensionRegistrationInfo(platform.id);
|
|
522
|
+
if (info?.providerId === "paypal" || info?.providerId === "wise") {
|
|
523
|
+
return info.providerId;
|
|
524
|
+
}
|
|
525
|
+
throw new Error(`${platform.name} does not require Peer extension registration.`);
|
|
526
|
+
}
|
|
527
|
+
async function postMakerCreate(processorName, payload, apiBaseUrl = API_BASE_URL) {
|
|
528
|
+
const controller = new AbortController();
|
|
529
|
+
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
530
|
+
try {
|
|
531
|
+
const res = await fetch(`${apiBaseUrl}/v2/makers/create`, {
|
|
532
|
+
method: "POST",
|
|
533
|
+
headers: { "Content-Type": "application/json" },
|
|
534
|
+
body: JSON.stringify({ processorName, ...payload }),
|
|
535
|
+
signal: controller.signal
|
|
536
|
+
});
|
|
537
|
+
if (!res.ok) {
|
|
538
|
+
const txt = await res.text().catch(() => "");
|
|
539
|
+
let detail = txt || res.statusText;
|
|
540
|
+
try {
|
|
541
|
+
const parsed = JSON.parse(txt);
|
|
542
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
543
|
+
detail = parsed.message;
|
|
544
|
+
}
|
|
545
|
+
} catch {
|
|
546
|
+
}
|
|
547
|
+
throw new MakersCreateError(
|
|
548
|
+
`makers/create failed (${res.status}): ${detail}`,
|
|
549
|
+
res.status,
|
|
550
|
+
txt
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
const json = await res.json();
|
|
554
|
+
if (!json.success || !json.responseObject?.hashedOnchainId) {
|
|
555
|
+
throw new MakersCreateError(
|
|
556
|
+
json.message || "makers/create returned no hashedOnchainId",
|
|
557
|
+
json.statusCode ?? null,
|
|
558
|
+
""
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
return json.responseObject.hashedOnchainId;
|
|
562
|
+
} finally {
|
|
563
|
+
clearTimeout(timeout);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function requireSarCredentialCapture(capturedMetadata) {
|
|
567
|
+
const capture = capturedMetadata.sarCredentialCapture;
|
|
568
|
+
if (!capture?.credentialBundle) {
|
|
569
|
+
throw new Error("Peer metadata did not include a seller credential bundle.");
|
|
570
|
+
}
|
|
571
|
+
if (!capture.offchainId) {
|
|
572
|
+
throw new Error("Peer metadata did not include seller credential offchainId.");
|
|
573
|
+
}
|
|
574
|
+
return capture;
|
|
575
|
+
}
|
|
576
|
+
async function uploadSellerCredentialBundle(processorName, payeeHash, bundle) {
|
|
577
|
+
const controller = new AbortController();
|
|
578
|
+
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
579
|
+
try {
|
|
580
|
+
const res = await fetch(
|
|
581
|
+
`${API_BASE_URL}/v2/makers/${encodeURIComponent(processorName)}/${encodeURIComponent(payeeHash)}/seller-credential`,
|
|
582
|
+
{
|
|
583
|
+
method: "POST",
|
|
584
|
+
headers: { "Content-Type": "application/json" },
|
|
585
|
+
body: JSON.stringify(bundle),
|
|
586
|
+
signal: controller.signal
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
if (!res.ok) {
|
|
590
|
+
const txt = await res.text().catch(() => "");
|
|
591
|
+
let detail = txt || res.statusText;
|
|
592
|
+
try {
|
|
593
|
+
const parsed = JSON.parse(txt);
|
|
594
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
595
|
+
detail = parsed.message;
|
|
596
|
+
}
|
|
597
|
+
} catch {
|
|
598
|
+
}
|
|
599
|
+
throw new Error(`seller-credential upload failed (${res.status}): ${detail}`);
|
|
600
|
+
}
|
|
601
|
+
const json = await res.json();
|
|
602
|
+
if (!json.success || !json.responseObject) {
|
|
603
|
+
throw new Error(json.message || "seller-credential upload returned no status.");
|
|
604
|
+
}
|
|
605
|
+
return json;
|
|
606
|
+
} finally {
|
|
607
|
+
clearTimeout(timeout);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
async function completePeerExtensionRegistration(input) {
|
|
611
|
+
const processorName = resolveRegistrationProcessor(input.platform);
|
|
612
|
+
if (input.capturedMetadata.platform !== processorName) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
`Peer metadata is for ${input.capturedMetadata.platform}, not ${processorName}.`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
const sarCredentialCapture = requireSarCredentialCapture(input.capturedMetadata);
|
|
618
|
+
const bundle = sarCredentialCapture.credentialBundle;
|
|
619
|
+
if (bundle.platform.toLowerCase() !== processorName) {
|
|
620
|
+
throw new Error("Seller credential bundle platform does not match registration platform.");
|
|
621
|
+
}
|
|
622
|
+
const validation = input.platform.validate(input.identifier);
|
|
623
|
+
if (!validation.valid) {
|
|
624
|
+
throw new Error(validation.error || "Invalid identifier");
|
|
625
|
+
}
|
|
626
|
+
const payload = buildDepositData(processorName, sarCredentialCapture.offchainId);
|
|
627
|
+
if (payload.offchainId !== validation.normalized) {
|
|
628
|
+
throw new Error("Seller credential offchainId does not match registration identifier.");
|
|
629
|
+
}
|
|
630
|
+
const hashedOnchainId = processorName === "wise" ? bundle.payeeIdHash : await postMakerCreate(processorName, payload);
|
|
631
|
+
if (hashedOnchainId.toLowerCase() !== bundle.payeeIdHash.toLowerCase()) {
|
|
632
|
+
throw new Error("Seller credential payee hash does not match registered payee details.");
|
|
633
|
+
}
|
|
634
|
+
const sellerCredentialResponse = await uploadSellerCredentialBundle(
|
|
635
|
+
processorName,
|
|
636
|
+
hashedOnchainId,
|
|
637
|
+
bundle
|
|
638
|
+
);
|
|
639
|
+
return {
|
|
640
|
+
processorName,
|
|
641
|
+
offchainId: payload.offchainId,
|
|
642
|
+
hashedOnchainId,
|
|
643
|
+
sellerCredentialResponse,
|
|
644
|
+
sellerCredentialStatus: sellerCredentialResponse.responseObject
|
|
645
|
+
};
|
|
646
|
+
}
|
|
395
647
|
var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
|
|
396
648
|
function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
397
649
|
const info = getPeerExtensionRegistrationInfo(platform);
|
|
@@ -403,401 +655,26 @@ function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
|
403
655
|
return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
|
|
404
656
|
}
|
|
405
657
|
|
|
406
|
-
// src/errors.ts
|
|
407
|
-
var MakersCreateError = class extends Error {
|
|
408
|
-
status;
|
|
409
|
-
body;
|
|
410
|
-
constructor(message, status, body) {
|
|
411
|
-
super(message);
|
|
412
|
-
this.name = "MakersCreateError";
|
|
413
|
-
this.status = status;
|
|
414
|
-
this.body = body;
|
|
415
|
-
}
|
|
416
|
-
};
|
|
417
|
-
var OfframpError = class extends Error {
|
|
418
|
-
code;
|
|
419
|
-
step;
|
|
420
|
-
cause;
|
|
421
|
-
txHash;
|
|
422
|
-
depositId;
|
|
423
|
-
constructor(message, code, step, cause, details) {
|
|
424
|
-
super(message);
|
|
425
|
-
this.name = "OfframpError";
|
|
426
|
-
this.code = code;
|
|
427
|
-
this.step = step;
|
|
428
|
-
this.cause = cause;
|
|
429
|
-
this.txHash = details?.txHash;
|
|
430
|
-
this.depositId = details?.depositId;
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
function isUserCancellation(error) {
|
|
434
|
-
if (!(error instanceof Error)) return false;
|
|
435
|
-
const msg = error.message.toLowerCase();
|
|
436
|
-
return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("user cancelled") || msg.includes("rejected the request") || msg.includes("action_rejected");
|
|
437
|
-
}
|
|
438
|
-
|
|
439
658
|
// src/otc.ts
|
|
440
659
|
var import_viem = require("viem");
|
|
441
660
|
var import_chains = require("viem/chains");
|
|
442
|
-
|
|
443
|
-
// ../../node_modules/@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json
|
|
444
|
-
var WhitelistPreIntentHook_default = [
|
|
445
|
-
{
|
|
446
|
-
inputs: [
|
|
447
|
-
{
|
|
448
|
-
internalType: "address",
|
|
449
|
-
name: "_orchestratorRegistry",
|
|
450
|
-
type: "address"
|
|
451
|
-
}
|
|
452
|
-
],
|
|
453
|
-
stateMutability: "nonpayable",
|
|
454
|
-
type: "constructor"
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
inputs: [],
|
|
458
|
-
name: "EmptyArray",
|
|
459
|
-
type: "error"
|
|
460
|
-
},
|
|
461
|
-
{
|
|
462
|
-
inputs: [
|
|
463
|
-
{
|
|
464
|
-
internalType: "address",
|
|
465
|
-
name: "taker",
|
|
466
|
-
type: "address"
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
internalType: "address",
|
|
470
|
-
name: "escrow",
|
|
471
|
-
type: "address"
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
internalType: "uint256",
|
|
475
|
-
name: "depositId",
|
|
476
|
-
type: "uint256"
|
|
477
|
-
}
|
|
478
|
-
],
|
|
479
|
-
name: "TakerNotInWhitelist",
|
|
480
|
-
type: "error"
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
inputs: [
|
|
484
|
-
{
|
|
485
|
-
internalType: "address",
|
|
486
|
-
name: "taker",
|
|
487
|
-
type: "address"
|
|
488
|
-
},
|
|
489
|
-
{
|
|
490
|
-
internalType: "address",
|
|
491
|
-
name: "escrow",
|
|
492
|
-
type: "address"
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
internalType: "uint256",
|
|
496
|
-
name: "depositId",
|
|
497
|
-
type: "uint256"
|
|
498
|
-
}
|
|
499
|
-
],
|
|
500
|
-
name: "TakerNotWhitelisted",
|
|
501
|
-
type: "error"
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
inputs: [
|
|
505
|
-
{
|
|
506
|
-
internalType: "address",
|
|
507
|
-
name: "caller",
|
|
508
|
-
type: "address"
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
internalType: "address",
|
|
512
|
-
name: "owner",
|
|
513
|
-
type: "address"
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
internalType: "address",
|
|
517
|
-
name: "delegate",
|
|
518
|
-
type: "address"
|
|
519
|
-
}
|
|
520
|
-
],
|
|
521
|
-
name: "UnauthorizedCallerOrDelegate",
|
|
522
|
-
type: "error"
|
|
523
|
-
},
|
|
524
|
-
{
|
|
525
|
-
inputs: [
|
|
526
|
-
{
|
|
527
|
-
internalType: "address",
|
|
528
|
-
name: "caller",
|
|
529
|
-
type: "address"
|
|
530
|
-
}
|
|
531
|
-
],
|
|
532
|
-
name: "UnauthorizedOrchestratorCaller",
|
|
533
|
-
type: "error"
|
|
534
|
-
},
|
|
535
|
-
{
|
|
536
|
-
inputs: [],
|
|
537
|
-
name: "ZeroAddress",
|
|
538
|
-
type: "error"
|
|
539
|
-
},
|
|
540
|
-
{
|
|
541
|
-
anonymous: false,
|
|
542
|
-
inputs: [
|
|
543
|
-
{
|
|
544
|
-
indexed: true,
|
|
545
|
-
internalType: "address",
|
|
546
|
-
name: "escrow",
|
|
547
|
-
type: "address"
|
|
548
|
-
},
|
|
549
|
-
{
|
|
550
|
-
indexed: true,
|
|
551
|
-
internalType: "uint256",
|
|
552
|
-
name: "depositId",
|
|
553
|
-
type: "uint256"
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
indexed: true,
|
|
557
|
-
internalType: "address",
|
|
558
|
-
name: "taker",
|
|
559
|
-
type: "address"
|
|
560
|
-
}
|
|
561
|
-
],
|
|
562
|
-
name: "TakerRemovedFromWhitelist",
|
|
563
|
-
type: "event"
|
|
564
|
-
},
|
|
565
|
-
{
|
|
566
|
-
anonymous: false,
|
|
567
|
-
inputs: [
|
|
568
|
-
{
|
|
569
|
-
indexed: true,
|
|
570
|
-
internalType: "address",
|
|
571
|
-
name: "escrow",
|
|
572
|
-
type: "address"
|
|
573
|
-
},
|
|
574
|
-
{
|
|
575
|
-
indexed: true,
|
|
576
|
-
internalType: "uint256",
|
|
577
|
-
name: "depositId",
|
|
578
|
-
type: "uint256"
|
|
579
|
-
},
|
|
580
|
-
{
|
|
581
|
-
indexed: true,
|
|
582
|
-
internalType: "address",
|
|
583
|
-
name: "taker",
|
|
584
|
-
type: "address"
|
|
585
|
-
}
|
|
586
|
-
],
|
|
587
|
-
name: "TakerWhitelisted",
|
|
588
|
-
type: "event"
|
|
589
|
-
},
|
|
590
|
-
{
|
|
591
|
-
inputs: [
|
|
592
|
-
{
|
|
593
|
-
internalType: "address",
|
|
594
|
-
name: "_escrow",
|
|
595
|
-
type: "address"
|
|
596
|
-
},
|
|
597
|
-
{
|
|
598
|
-
internalType: "uint256",
|
|
599
|
-
name: "_depositId",
|
|
600
|
-
type: "uint256"
|
|
601
|
-
},
|
|
602
|
-
{
|
|
603
|
-
internalType: "address[]",
|
|
604
|
-
name: "_takers",
|
|
605
|
-
type: "address[]"
|
|
606
|
-
}
|
|
607
|
-
],
|
|
608
|
-
name: "addToWhitelist",
|
|
609
|
-
outputs: [],
|
|
610
|
-
stateMutability: "nonpayable",
|
|
611
|
-
type: "function"
|
|
612
|
-
},
|
|
613
|
-
{
|
|
614
|
-
inputs: [
|
|
615
|
-
{
|
|
616
|
-
internalType: "address",
|
|
617
|
-
name: "_escrow",
|
|
618
|
-
type: "address"
|
|
619
|
-
},
|
|
620
|
-
{
|
|
621
|
-
internalType: "uint256",
|
|
622
|
-
name: "_depositId",
|
|
623
|
-
type: "uint256"
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
internalType: "address",
|
|
627
|
-
name: "_taker",
|
|
628
|
-
type: "address"
|
|
629
|
-
}
|
|
630
|
-
],
|
|
631
|
-
name: "isWhitelisted",
|
|
632
|
-
outputs: [
|
|
633
|
-
{
|
|
634
|
-
internalType: "bool",
|
|
635
|
-
name: "",
|
|
636
|
-
type: "bool"
|
|
637
|
-
}
|
|
638
|
-
],
|
|
639
|
-
stateMutability: "view",
|
|
640
|
-
type: "function"
|
|
641
|
-
},
|
|
642
|
-
{
|
|
643
|
-
inputs: [],
|
|
644
|
-
name: "orchestratorRegistry",
|
|
645
|
-
outputs: [
|
|
646
|
-
{
|
|
647
|
-
internalType: "contract IOrchestratorRegistry",
|
|
648
|
-
name: "",
|
|
649
|
-
type: "address"
|
|
650
|
-
}
|
|
651
|
-
],
|
|
652
|
-
stateMutability: "view",
|
|
653
|
-
type: "function"
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
inputs: [
|
|
657
|
-
{
|
|
658
|
-
internalType: "address",
|
|
659
|
-
name: "_escrow",
|
|
660
|
-
type: "address"
|
|
661
|
-
},
|
|
662
|
-
{
|
|
663
|
-
internalType: "uint256",
|
|
664
|
-
name: "_depositId",
|
|
665
|
-
type: "uint256"
|
|
666
|
-
},
|
|
667
|
-
{
|
|
668
|
-
internalType: "address[]",
|
|
669
|
-
name: "_takers",
|
|
670
|
-
type: "address[]"
|
|
671
|
-
}
|
|
672
|
-
],
|
|
673
|
-
name: "removeFromWhitelist",
|
|
674
|
-
outputs: [],
|
|
675
|
-
stateMutability: "nonpayable",
|
|
676
|
-
type: "function"
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
inputs: [
|
|
680
|
-
{
|
|
681
|
-
components: [
|
|
682
|
-
{
|
|
683
|
-
internalType: "address",
|
|
684
|
-
name: "taker",
|
|
685
|
-
type: "address"
|
|
686
|
-
},
|
|
687
|
-
{
|
|
688
|
-
internalType: "address",
|
|
689
|
-
name: "escrow",
|
|
690
|
-
type: "address"
|
|
691
|
-
},
|
|
692
|
-
{
|
|
693
|
-
internalType: "uint256",
|
|
694
|
-
name: "depositId",
|
|
695
|
-
type: "uint256"
|
|
696
|
-
},
|
|
697
|
-
{
|
|
698
|
-
internalType: "uint256",
|
|
699
|
-
name: "amount",
|
|
700
|
-
type: "uint256"
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
internalType: "address",
|
|
704
|
-
name: "to",
|
|
705
|
-
type: "address"
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
internalType: "bytes32",
|
|
709
|
-
name: "paymentMethod",
|
|
710
|
-
type: "bytes32"
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
internalType: "bytes32",
|
|
714
|
-
name: "fiatCurrency",
|
|
715
|
-
type: "bytes32"
|
|
716
|
-
},
|
|
717
|
-
{
|
|
718
|
-
internalType: "uint256",
|
|
719
|
-
name: "conversionRate",
|
|
720
|
-
type: "uint256"
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
components: [
|
|
724
|
-
{
|
|
725
|
-
internalType: "address",
|
|
726
|
-
name: "recipient",
|
|
727
|
-
type: "address"
|
|
728
|
-
},
|
|
729
|
-
{
|
|
730
|
-
internalType: "uint256",
|
|
731
|
-
name: "fee",
|
|
732
|
-
type: "uint256"
|
|
733
|
-
}
|
|
734
|
-
],
|
|
735
|
-
internalType: "struct IReferralFee.ReferralFee[]",
|
|
736
|
-
name: "referralFees",
|
|
737
|
-
type: "tuple[]"
|
|
738
|
-
},
|
|
739
|
-
{
|
|
740
|
-
internalType: "bytes",
|
|
741
|
-
name: "preIntentHookData",
|
|
742
|
-
type: "bytes"
|
|
743
|
-
}
|
|
744
|
-
],
|
|
745
|
-
internalType: "struct IPreIntentHook.PreIntentContext",
|
|
746
|
-
name: "_ctx",
|
|
747
|
-
type: "tuple"
|
|
748
|
-
}
|
|
749
|
-
],
|
|
750
|
-
name: "validateSignalIntent",
|
|
751
|
-
outputs: [],
|
|
752
|
-
stateMutability: "view",
|
|
753
|
-
type: "function"
|
|
754
|
-
},
|
|
755
|
-
{
|
|
756
|
-
inputs: [
|
|
757
|
-
{
|
|
758
|
-
internalType: "address",
|
|
759
|
-
name: "",
|
|
760
|
-
type: "address"
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
internalType: "uint256",
|
|
764
|
-
name: "",
|
|
765
|
-
type: "uint256"
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
internalType: "address",
|
|
769
|
-
name: "",
|
|
770
|
-
type: "address"
|
|
771
|
-
}
|
|
772
|
-
],
|
|
773
|
-
name: "whitelist",
|
|
774
|
-
outputs: [
|
|
775
|
-
{
|
|
776
|
-
internalType: "bool",
|
|
777
|
-
name: "",
|
|
778
|
-
type: "bool"
|
|
779
|
-
}
|
|
780
|
-
],
|
|
781
|
-
stateMutability: "view",
|
|
782
|
-
type: "function"
|
|
783
|
-
}
|
|
784
|
-
];
|
|
661
|
+
var import_WhitelistPreIntentHook = __toESM(require("@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json"), 1);
|
|
785
662
|
|
|
786
663
|
// src/sdk-client.ts
|
|
787
664
|
var import_sdk3 = require("@zkp2p/sdk");
|
|
788
|
-
function createSdkClient(walletClient) {
|
|
665
|
+
function createSdkClient(walletClient, apiBaseUrl = API_BASE_URL) {
|
|
789
666
|
return new import_sdk3.OfframpClient({
|
|
790
667
|
walletClient,
|
|
791
668
|
chainId: BASE_CHAIN_ID,
|
|
792
669
|
runtimeEnv: RUNTIME_ENV,
|
|
793
670
|
rpcUrl: BASE_RPC_URL,
|
|
794
|
-
baseApiUrl:
|
|
671
|
+
baseApiUrl: apiBaseUrl
|
|
795
672
|
});
|
|
796
673
|
}
|
|
797
674
|
|
|
798
675
|
// src/otc.ts
|
|
799
676
|
var WHITELIST_HOOK_ADDRESS = "0xda023Ea0d789A41BcF5866F7B6BBd2CaDF9b79B8";
|
|
800
|
-
var OTC_BASE_URL = "https://usdctofiat.xyz";
|
|
677
|
+
var OTC_BASE_URL = "https://otc.usdctofiat.xyz";
|
|
801
678
|
async function enableOtc(walletClient, depositId, takerAddress, escrowAddress) {
|
|
802
679
|
if (!(0, import_viem.isAddress)(takerAddress)) {
|
|
803
680
|
throw new OfframpError("Invalid taker address", "VALIDATION");
|
|
@@ -869,7 +746,7 @@ async function disableOtc(walletClient, depositId, escrowAddress) {
|
|
|
869
746
|
}
|
|
870
747
|
function getOtcLink(depositId, escrowAddress) {
|
|
871
748
|
const escrow = escrowAddress || ESCROW_ADDRESS;
|
|
872
|
-
return `${OTC_BASE_URL}/
|
|
749
|
+
return `${OTC_BASE_URL}/d/${escrow}/${depositId}`;
|
|
873
750
|
}
|
|
874
751
|
async function writeContract(walletClient, functionName, args) {
|
|
875
752
|
const wc = walletClient;
|
|
@@ -878,7 +755,7 @@ async function writeContract(walletClient, functionName, args) {
|
|
|
878
755
|
}
|
|
879
756
|
await wc.writeContract({
|
|
880
757
|
address: WHITELIST_HOOK_ADDRESS,
|
|
881
|
-
abi:
|
|
758
|
+
abi: import_WhitelistPreIntentHook.default,
|
|
882
759
|
functionName,
|
|
883
760
|
args
|
|
884
761
|
});
|
|
@@ -887,7 +764,7 @@ async function readIsWhitelisted(escrow, depositId, taker) {
|
|
|
887
764
|
const publicClient = (0, import_viem.createPublicClient)({ chain: import_chains.base, transport: (0, import_viem.http)(BASE_RPC_URL) });
|
|
888
765
|
return publicClient.readContract({
|
|
889
766
|
address: WHITELIST_HOOK_ADDRESS,
|
|
890
|
-
abi:
|
|
767
|
+
abi: import_WhitelistPreIntentHook.default,
|
|
891
768
|
functionName: "isWhitelisted",
|
|
892
769
|
args: [escrow, depositId, taker]
|
|
893
770
|
});
|
|
@@ -1202,41 +1079,24 @@ function extractDepositIdFromLogs(logs) {
|
|
|
1202
1079
|
}
|
|
1203
1080
|
return null;
|
|
1204
1081
|
}
|
|
1205
|
-
async function
|
|
1082
|
+
async function validatePayeeDetails(processorName, offchainId, apiBaseUrl = API_BASE_URL) {
|
|
1206
1083
|
const controller = new AbortController();
|
|
1207
1084
|
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
1208
1085
|
try {
|
|
1209
|
-
const res = await fetch(`${
|
|
1086
|
+
const res = await fetch(`${apiBaseUrl}/v2/makers/validate`, {
|
|
1210
1087
|
method: "POST",
|
|
1211
1088
|
headers: { "Content-Type": "application/json" },
|
|
1212
|
-
body: JSON.stringify({ processorName,
|
|
1089
|
+
body: JSON.stringify({ processorName, offchainId }),
|
|
1213
1090
|
signal: controller.signal
|
|
1214
1091
|
});
|
|
1215
|
-
if (!res.ok)
|
|
1216
|
-
const txt = await res.text().catch(() => "");
|
|
1217
|
-
let detail = txt || res.statusText;
|
|
1218
|
-
try {
|
|
1219
|
-
const parsed = JSON.parse(txt);
|
|
1220
|
-
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
1221
|
-
detail = parsed.message;
|
|
1222
|
-
}
|
|
1223
|
-
} catch {
|
|
1224
|
-
}
|
|
1225
|
-
throw new MakersCreateError(
|
|
1226
|
-
`makers/create failed (${res.status}): ${detail}`,
|
|
1227
|
-
res.status,
|
|
1228
|
-
txt
|
|
1229
|
-
);
|
|
1230
|
-
}
|
|
1092
|
+
if (!res.ok) return "unknown";
|
|
1231
1093
|
const json = await res.json();
|
|
1232
|
-
if (
|
|
1233
|
-
|
|
1234
|
-
json.message || "makers/create returned no hashedOnchainId",
|
|
1235
|
-
json.statusCode ?? null,
|
|
1236
|
-
""
|
|
1237
|
-
);
|
|
1094
|
+
if (typeof json.responseObject === "boolean") {
|
|
1095
|
+
return json.responseObject ? "valid" : "invalid";
|
|
1238
1096
|
}
|
|
1239
|
-
return
|
|
1097
|
+
return "unknown";
|
|
1098
|
+
} catch {
|
|
1099
|
+
return "unknown";
|
|
1240
1100
|
} finally {
|
|
1241
1101
|
clearTimeout(timeout);
|
|
1242
1102
|
}
|
|
@@ -1301,10 +1161,11 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1301
1161
|
return null;
|
|
1302
1162
|
}
|
|
1303
1163
|
}
|
|
1304
|
-
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1164
|
+
async function offramp(walletClient, params, onProgress, telemetryContext, apiBaseUrl) {
|
|
1305
1165
|
const { amount, platform, currency, identifier } = params;
|
|
1306
1166
|
const platformId = platform.id;
|
|
1307
1167
|
const currencyCode = currency.code;
|
|
1168
|
+
const resolvedApiBaseUrl = resolveApiBaseUrl(apiBaseUrl);
|
|
1308
1169
|
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1309
1170
|
sdkVersion: SDK_VERSION,
|
|
1310
1171
|
telemetry: true,
|
|
@@ -1349,7 +1210,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1349
1210
|
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1350
1211
|
if (existing) {
|
|
1351
1212
|
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1352
|
-
const client2 = createSdkClient(walletClient);
|
|
1213
|
+
const client2 = createSdkClient(walletClient, resolvedApiBaseUrl);
|
|
1353
1214
|
const txOverrides2 = { referrer: [REFERRER] };
|
|
1354
1215
|
try {
|
|
1355
1216
|
const result2 = await client2.setRateManager({
|
|
@@ -1385,7 +1246,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1385
1246
|
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1386
1247
|
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1387
1248
|
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1388
|
-
const maxUnits = usdcToUnits(String(Math.min(amt,
|
|
1249
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, MAX_ORDER_USDC)));
|
|
1389
1250
|
try {
|
|
1390
1251
|
const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains3.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
|
|
1391
1252
|
const balance = await publicClient.readContract({
|
|
@@ -1412,7 +1273,22 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1412
1273
|
} catch (err) {
|
|
1413
1274
|
if (err instanceof OfframpError) throw err;
|
|
1414
1275
|
}
|
|
1415
|
-
const
|
|
1276
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1277
|
+
if (!getPeerExtensionRegistrationInfo(platformId)) {
|
|
1278
|
+
const outcome = await validatePayeeDetails(
|
|
1279
|
+
canonicalName,
|
|
1280
|
+
normalizedIdentifier,
|
|
1281
|
+
resolvedApiBaseUrl
|
|
1282
|
+
);
|
|
1283
|
+
if (outcome === "invalid") {
|
|
1284
|
+
throw new OfframpError(
|
|
1285
|
+
`Couldn't verify that ${platform.name} ${platform.identifier.label.toLowerCase()}. Double-check it and try again.`,
|
|
1286
|
+
"VALIDATION",
|
|
1287
|
+
"registering"
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
const client = createSdkClient(walletClient, resolvedApiBaseUrl);
|
|
1416
1292
|
const txOverrides = { referrer: [REFERRER] };
|
|
1417
1293
|
emitProgress({ step: "approving" });
|
|
1418
1294
|
try {
|
|
@@ -1436,16 +1312,15 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1436
1312
|
}
|
|
1437
1313
|
emitProgress({ step: "registering" });
|
|
1438
1314
|
let hashedOnchainId;
|
|
1439
|
-
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1440
1315
|
try {
|
|
1441
1316
|
const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
|
|
1442
|
-
hashedOnchainId = await
|
|
1317
|
+
hashedOnchainId = await postMakerCreate(canonicalName, payeePayload, resolvedApiBaseUrl);
|
|
1443
1318
|
} catch (err) {
|
|
1444
1319
|
const status = err instanceof MakersCreateError ? err.status : null;
|
|
1445
1320
|
const message = err instanceof Error ? err.message : String(err);
|
|
1446
1321
|
if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
|
|
1447
1322
|
throw new OfframpError(
|
|
1448
|
-
`${platform.name} maker is not yet registered in the Peer extension.
|
|
1323
|
+
`${platform.name} maker is not yet registered in the Peer extension. Register this handle with Peer, then retry.`,
|
|
1449
1324
|
"EXTENSION_REGISTRATION_REQUIRED",
|
|
1450
1325
|
"registering",
|
|
1451
1326
|
err
|
|
@@ -1637,6 +1512,14 @@ var CURRENCIES = buildCurrencies();
|
|
|
1637
1512
|
// src/offramp.ts
|
|
1638
1513
|
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1639
1514
|
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1515
|
+
var warnedIdempotencyUnavailable = false;
|
|
1516
|
+
function warnIdempotencyUnavailableOnce() {
|
|
1517
|
+
if (warnedIdempotencyUnavailable) return;
|
|
1518
|
+
warnedIdempotencyUnavailable = true;
|
|
1519
|
+
console.warn(
|
|
1520
|
+
"[offramp] idempotencyKey was provided but sessionStorage is unavailable (Node/worker runtime). The idempotency cache is browser-only and no-ops here, so retries can create duplicate deposits. For server-side dedup, call deposits(walletAddress) and reuse an existing open deposit before creating one."
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1640
1523
|
function getWalletAddress(walletClient) {
|
|
1641
1524
|
const address = walletClient.account?.address;
|
|
1642
1525
|
if (!address) {
|
|
@@ -1707,8 +1590,10 @@ function buildCurrencyGroups() {
|
|
|
1707
1590
|
var Offramp = class {
|
|
1708
1591
|
walletClient;
|
|
1709
1592
|
telemetry;
|
|
1593
|
+
apiBaseUrl;
|
|
1710
1594
|
constructor(options) {
|
|
1711
1595
|
this.walletClient = options.walletClient;
|
|
1596
|
+
this.apiBaseUrl = options.apiBaseUrl;
|
|
1712
1597
|
this.telemetry = createTelemetryContext({
|
|
1713
1598
|
sdkVersion: SDK_VERSION,
|
|
1714
1599
|
telemetry: options.telemetry,
|
|
@@ -1731,6 +1616,9 @@ var Offramp = class {
|
|
|
1731
1616
|
referralId: flowTelemetry.referralId
|
|
1732
1617
|
};
|
|
1733
1618
|
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1619
|
+
if (sanitizedKey && typeof sessionStorage === "undefined") {
|
|
1620
|
+
warnIdempotencyUnavailableOnce();
|
|
1621
|
+
}
|
|
1734
1622
|
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1735
1623
|
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1736
1624
|
if (storageKey) {
|
|
@@ -1741,7 +1629,8 @@ var Offramp = class {
|
|
|
1741
1629
|
this.walletClient,
|
|
1742
1630
|
sanitizedParams,
|
|
1743
1631
|
onProgress,
|
|
1744
|
-
flowTelemetry
|
|
1632
|
+
flowTelemetry,
|
|
1633
|
+
this.apiBaseUrl
|
|
1745
1634
|
);
|
|
1746
1635
|
if (storageKey) {
|
|
1747
1636
|
writeIdempotencyResult(storageKey, result);
|
|
@@ -1797,8 +1686,8 @@ var OFFRAMP_ERROR_CODES = {
|
|
|
1797
1686
|
* handle in the Peer extension yet. Thrown from `offramp()` for PayPal
|
|
1798
1687
|
* and Wise when `/v2/makers/create` returns 400 or a "creating the maker"
|
|
1799
1688
|
* message. Recover by prompting the user through the Peer extension via
|
|
1800
|
-
* `usePeerExtensionRegistration(platform)` (React) or
|
|
1801
|
-
*
|
|
1689
|
+
* `usePeerExtensionRegistration(platform)` (React) or
|
|
1690
|
+
* `completePeerExtensionRegistration(...)`, then retrying `offramp()`.
|
|
1802
1691
|
*/
|
|
1803
1692
|
EXTENSION_REGISTRATION_REQUIRED: "EXTENSION_REGISTRATION_REQUIRED",
|
|
1804
1693
|
DEPOSIT_FAILED: "DEPOSIT_FAILED",
|
|
@@ -1810,6 +1699,78 @@ var OFFRAMP_ERROR_CODES = {
|
|
|
1810
1699
|
|
|
1811
1700
|
// src/extension.ts
|
|
1812
1701
|
var import_sdk7 = require("@zkp2p/sdk");
|
|
1702
|
+
var resolveWindow = (options) => {
|
|
1703
|
+
if (options?.window) {
|
|
1704
|
+
return options.window;
|
|
1705
|
+
}
|
|
1706
|
+
if (typeof window === "undefined") {
|
|
1707
|
+
return void 0;
|
|
1708
|
+
}
|
|
1709
|
+
return window;
|
|
1710
|
+
};
|
|
1711
|
+
var requirePeer = (options) => {
|
|
1712
|
+
const resolvedWindow = resolveWindow(options);
|
|
1713
|
+
if (!resolvedWindow) {
|
|
1714
|
+
throw new Error("Peer extension SDK requires a browser window.");
|
|
1715
|
+
}
|
|
1716
|
+
if (!resolvedWindow.peer) {
|
|
1717
|
+
throw new Error("Peer extension not available. Install or enable the Peer extension.");
|
|
1718
|
+
}
|
|
1719
|
+
return resolvedWindow.peer;
|
|
1720
|
+
};
|
|
1721
|
+
var isPeerExtensionAvailable = (options) => {
|
|
1722
|
+
const resolvedWindow = resolveWindow(options);
|
|
1723
|
+
return Boolean(resolvedWindow?.peer);
|
|
1724
|
+
};
|
|
1725
|
+
var hasHeadlessMetadataBridge = (peer) => typeof peer.authenticate === "function" && typeof peer.onMetadataMessage === "function";
|
|
1726
|
+
var requirePeerMetadataBridge = (options) => {
|
|
1727
|
+
const peer = requirePeer(options);
|
|
1728
|
+
if (!hasHeadlessMetadataBridge(peer)) {
|
|
1729
|
+
throw new Error("Peer extension metadata bridge unavailable. Install or update Peer.");
|
|
1730
|
+
}
|
|
1731
|
+
return peer;
|
|
1732
|
+
};
|
|
1733
|
+
var isPeerExtensionMetadataBridgeAvailable = (options) => {
|
|
1734
|
+
const resolvedWindow = resolveWindow(options);
|
|
1735
|
+
return Boolean(resolvedWindow?.peer && hasHeadlessMetadataBridge(resolvedWindow.peer));
|
|
1736
|
+
};
|
|
1737
|
+
var openPeerExtensionInstallPage = (options) => {
|
|
1738
|
+
const resolvedWindow = resolveWindow(options);
|
|
1739
|
+
if (!resolvedWindow) {
|
|
1740
|
+
throw new Error("Peer extension SDK requires a browser window.");
|
|
1741
|
+
}
|
|
1742
|
+
resolvedWindow.open(import_sdk7.PEER_EXTENSION_CHROME_URL, "_blank", "noopener,noreferrer");
|
|
1743
|
+
};
|
|
1744
|
+
var getPeerExtensionState = async (options) => {
|
|
1745
|
+
if (!isPeerExtensionAvailable(options)) {
|
|
1746
|
+
return "needs_install";
|
|
1747
|
+
}
|
|
1748
|
+
try {
|
|
1749
|
+
const status = await requirePeer(options).checkConnectionStatus();
|
|
1750
|
+
return status === "connected" ? "ready" : "needs_connection";
|
|
1751
|
+
} catch {
|
|
1752
|
+
return "needs_connection";
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
var createPeerExtensionSdk = (options = {}) => {
|
|
1756
|
+
const legacySdk = (0, import_sdk7.createPeerExtensionSdk)(
|
|
1757
|
+
options
|
|
1758
|
+
);
|
|
1759
|
+
return {
|
|
1760
|
+
isAvailable: () => isPeerExtensionAvailable(options),
|
|
1761
|
+
requestConnection: () => requirePeer(options).requestConnection(),
|
|
1762
|
+
checkConnectionStatus: () => requirePeer(options).checkConnectionStatus(),
|
|
1763
|
+
getVersion: () => requirePeer(options).getVersion(),
|
|
1764
|
+
authenticate: (params) => requirePeerMetadataBridge(options).authenticate(params),
|
|
1765
|
+
onMetadataMessage: (callback) => requirePeerMetadataBridge(options).onMetadataMessage(callback),
|
|
1766
|
+
openSidebar: (route) => legacySdk.openSidebar(route),
|
|
1767
|
+
onramp: (params, callback) => legacySdk.onramp(params, callback),
|
|
1768
|
+
getOnrampTransaction: (intentHash) => legacySdk.getOnrampTransaction(intentHash),
|
|
1769
|
+
openInstallPage: () => openPeerExtensionInstallPage(options),
|
|
1770
|
+
getState: () => getPeerExtensionState(options)
|
|
1771
|
+
};
|
|
1772
|
+
};
|
|
1773
|
+
var peerExtensionSdk = createPeerExtensionSdk();
|
|
1813
1774
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1814
1775
|
0 && (module.exports = {
|
|
1815
1776
|
CURRENCIES,
|
|
@@ -1820,7 +1781,9 @@ var import_sdk7 = require("@zkp2p/sdk");
|
|
|
1820
1781
|
OfframpError,
|
|
1821
1782
|
PEER_EXTENSION_CHROME_URL,
|
|
1822
1783
|
PLATFORMS,
|
|
1784
|
+
PLATFORM_LIMITS,
|
|
1823
1785
|
close,
|
|
1786
|
+
completePeerExtensionRegistration,
|
|
1824
1787
|
createOfframp,
|
|
1825
1788
|
createPeerExtensionSdk,
|
|
1826
1789
|
createTelemetryContext,
|
|
@@ -1830,10 +1793,14 @@ var import_sdk7 = require("@zkp2p/sdk");
|
|
|
1830
1793
|
emitEvent,
|
|
1831
1794
|
enableOtc,
|
|
1832
1795
|
getOtcLink,
|
|
1796
|
+
getPeerExtensionRegistrationAuthParams,
|
|
1833
1797
|
getPeerExtensionRegistrationInfo,
|
|
1834
1798
|
getPeerExtensionState,
|
|
1799
|
+
getPlatformLimits,
|
|
1835
1800
|
isPeerExtensionAvailable,
|
|
1801
|
+
isPeerExtensionMetadataBridgeAvailable,
|
|
1836
1802
|
isPeerExtensionRegistrationError,
|
|
1803
|
+
isValidIBAN,
|
|
1837
1804
|
normalizePaypalMeUsername,
|
|
1838
1805
|
offramp,
|
|
1839
1806
|
openPeerExtensionInstallPage,
|