@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/react.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/react.ts
|
|
@@ -30,11 +40,15 @@ var import_react = require("react");
|
|
|
30
40
|
|
|
31
41
|
// src/config.ts
|
|
32
42
|
var import_sdk = require("@zkp2p/sdk");
|
|
33
|
-
var SDK_VERSION = "3.0
|
|
43
|
+
var SDK_VERSION = "3.1.0";
|
|
34
44
|
var BASE_CHAIN_ID = 8453;
|
|
35
45
|
var RUNTIME_ENV = "production";
|
|
36
46
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
37
47
|
var BASE_RPC_URL = "https://mainnet.base.org";
|
|
48
|
+
function resolveApiBaseUrl(override) {
|
|
49
|
+
const trimmed = override?.trim();
|
|
50
|
+
return (trimmed && trimmed.length > 0 ? trimmed : API_BASE_URL).replace(/\/$/, "");
|
|
51
|
+
}
|
|
38
52
|
var contracts = (0, import_sdk.getContracts)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
39
53
|
var addresses = contracts.addresses;
|
|
40
54
|
var addrs = addresses;
|
|
@@ -51,6 +65,7 @@ var DEFAULT_INTENT_GUARDIAN_ADDRESS = "0xe29a5BD4D0CEbA6C125A0361E7D20ab4eA275C2
|
|
|
51
65
|
var REFERRER = "galleonlabs";
|
|
52
66
|
var MIN_DEPOSIT_USDC = 1;
|
|
53
67
|
var MIN_ORDER_USDC = 1;
|
|
68
|
+
var MAX_ORDER_USDC = 2500;
|
|
54
69
|
var INDEXER_MAX_ATTEMPTS = 12;
|
|
55
70
|
var INDEXER_INITIAL_DELAY_MS = 1e3;
|
|
56
71
|
var INDEXER_MAX_DELAY_MS = 1e4;
|
|
@@ -82,6 +97,41 @@ var import_sdk6 = require("@zkp2p/sdk");
|
|
|
82
97
|
// src/platforms.ts
|
|
83
98
|
var import_sdk3 = require("@zkp2p/sdk");
|
|
84
99
|
var import_zod = require("zod");
|
|
100
|
+
|
|
101
|
+
// src/errors.ts
|
|
102
|
+
var MakersCreateError = class extends Error {
|
|
103
|
+
status;
|
|
104
|
+
body;
|
|
105
|
+
constructor(message, status, body) {
|
|
106
|
+
super(message);
|
|
107
|
+
this.name = "MakersCreateError";
|
|
108
|
+
this.status = status;
|
|
109
|
+
this.body = body;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var OfframpError = class extends Error {
|
|
113
|
+
code;
|
|
114
|
+
step;
|
|
115
|
+
cause;
|
|
116
|
+
txHash;
|
|
117
|
+
depositId;
|
|
118
|
+
constructor(message, code, step, cause, details) {
|
|
119
|
+
super(message);
|
|
120
|
+
this.name = "OfframpError";
|
|
121
|
+
this.code = code;
|
|
122
|
+
this.step = step;
|
|
123
|
+
this.cause = cause;
|
|
124
|
+
this.txHash = details?.txHash;
|
|
125
|
+
this.depositId = details?.depositId;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
function isUserCancellation(error) {
|
|
129
|
+
if (!(error instanceof Error)) return false;
|
|
130
|
+
const msg = error.message.toLowerCase();
|
|
131
|
+
return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("user cancelled") || msg.includes("rejected the request") || msg.includes("action_rejected");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/platforms.ts
|
|
85
135
|
var PAYMENT_CATALOG = (0, import_sdk3.getPaymentMethodsCatalog)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
86
136
|
var ZELLE_HASH_LOOKUP_NAMES = ["zelle", "zelle-bofa", "zelle-chase", "zelle-citi"];
|
|
87
137
|
var FALLBACK_CURRENCIES = {
|
|
@@ -150,7 +200,8 @@ var FALLBACK_CURRENCIES = {
|
|
|
150
200
|
zelle: ["USD"],
|
|
151
201
|
paypal: ["USD", "EUR", "GBP", "SGD", "NZD", "AUD", "CAD"],
|
|
152
202
|
monzo: ["GBP"],
|
|
153
|
-
n26: ["EUR"]
|
|
203
|
+
n26: ["EUR"],
|
|
204
|
+
luxon: ["USD", "EUR", "GBP"]
|
|
154
205
|
};
|
|
155
206
|
function gatherCatalogHashes(platform) {
|
|
156
207
|
if (platform === "zelle") {
|
|
@@ -174,13 +225,39 @@ function resolveSupportedCurrencies(platform) {
|
|
|
174
225
|
function normalizePaypalMeUsername(value) {
|
|
175
226
|
const trimmed = value.trim();
|
|
176
227
|
if (!trimmed) return "";
|
|
177
|
-
const withoutProtocol = trimmed.replace(/^https?:\/\//i, "")
|
|
178
|
-
|
|
179
|
-
const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
|
|
180
|
-
const [pathWithoutQuery] = withoutDomain.split(/[?#]/, 1);
|
|
228
|
+
const withoutProtocol = trimmed.replace(/^https?:\/\//i, "");
|
|
229
|
+
const [pathWithoutQuery] = withoutProtocol.split(/[?#]/, 1);
|
|
181
230
|
const sanitizedPath = pathWithoutQuery.replace(/^\/+/, "");
|
|
182
|
-
const [
|
|
183
|
-
|
|
231
|
+
const [host = "", ...pathSegments] = sanitizedPath.split("/");
|
|
232
|
+
const normalizedHost = host.toLowerCase().replace(/^www\./i, "");
|
|
233
|
+
if (normalizedHost === "paypal.me") {
|
|
234
|
+
const [username = ""] = pathSegments;
|
|
235
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
236
|
+
}
|
|
237
|
+
if (normalizedHost === "paypal.com") {
|
|
238
|
+
const [paypalMePath = "", username = ""] = pathSegments;
|
|
239
|
+
if (paypalMePath.toLowerCase() !== "paypalme") return "";
|
|
240
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
241
|
+
}
|
|
242
|
+
if (/^https?:\/\//i.test(trimmed) || trimmed.includes("/")) {
|
|
243
|
+
return trimmed;
|
|
244
|
+
}
|
|
245
|
+
return trimmed.replace(/^@+/, "").toLowerCase();
|
|
246
|
+
}
|
|
247
|
+
function isValidIBAN(iban) {
|
|
248
|
+
const upper = iban.toUpperCase();
|
|
249
|
+
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(upper)) return false;
|
|
250
|
+
const rearranged = upper.slice(4) + upper.slice(0, 4);
|
|
251
|
+
const numeric = rearranged.split("").map((ch) => {
|
|
252
|
+
const code = ch.charCodeAt(0);
|
|
253
|
+
return code >= 65 && code <= 90 ? (code - 55).toString() : ch;
|
|
254
|
+
}).join("");
|
|
255
|
+
let remainder = numeric;
|
|
256
|
+
while (remainder.length > 2) {
|
|
257
|
+
const block = remainder.slice(0, 9);
|
|
258
|
+
remainder = (parseInt(block, 10) % 97).toString() + remainder.slice(block.length);
|
|
259
|
+
}
|
|
260
|
+
return parseInt(remainder, 10) % 97 === 1;
|
|
184
261
|
}
|
|
185
262
|
var BLUEPRINTS = {
|
|
186
263
|
venmo: {
|
|
@@ -232,9 +309,9 @@ var BLUEPRINTS = {
|
|
|
232
309
|
transform: (v) => v.replace(/^@+/, "").trim(),
|
|
233
310
|
extensionRegistration: {
|
|
234
311
|
providerId: "wise",
|
|
235
|
-
requiredPrompt: "This Wisetag is not registered yet.
|
|
236
|
-
ctaLabel: "
|
|
237
|
-
minExtensionVersion: "0.
|
|
312
|
+
requiredPrompt: "This Wisetag is not registered yet. Register it with PeerAuth, then retry with the same Wisetag.",
|
|
313
|
+
ctaLabel: "Register Wisetag",
|
|
314
|
+
minExtensionVersion: "0.5.0"
|
|
238
315
|
}
|
|
239
316
|
},
|
|
240
317
|
mercadopago: {
|
|
@@ -251,7 +328,11 @@ var BLUEPRINTS = {
|
|
|
251
328
|
identifierLabel: "Email",
|
|
252
329
|
placeholder: "email",
|
|
253
330
|
helperText: "Registered Zelle email",
|
|
254
|
-
|
|
331
|
+
// Curator's ZelleProcessor rejects any non-lowercase email outright, so
|
|
332
|
+
// canonicalize to lowercase here rather than letting /v2/makers/create
|
|
333
|
+
// reject after the maker has already approved USDC + paid gas.
|
|
334
|
+
validation: import_zod.z.string().email(),
|
|
335
|
+
transform: (v) => v.trim().toLowerCase()
|
|
255
336
|
},
|
|
256
337
|
paypal: {
|
|
257
338
|
id: "paypal",
|
|
@@ -259,14 +340,17 @@ var BLUEPRINTS = {
|
|
|
259
340
|
identifierLabel: "PayPal.me Username",
|
|
260
341
|
placeholder: "paypal.me/your-username",
|
|
261
342
|
helperText: "Your PayPal.me username, not your email",
|
|
262
|
-
validation: import_zod.z.string().min(1, "PayPal.me username is required").regex(
|
|
343
|
+
validation: import_zod.z.string().min(1, "PayPal.me username is required").regex(
|
|
344
|
+
/^[a-z0-9._-]+$/i,
|
|
345
|
+
"Use your PayPal.me username or paypal.me link, not a shortened URL."
|
|
346
|
+
),
|
|
263
347
|
transform: normalizePaypalMeUsername,
|
|
264
348
|
extensionRegistration: {
|
|
265
349
|
providerId: "paypal",
|
|
266
|
-
requiredPrompt: "This PayPal username is not registered yet.
|
|
267
|
-
ctaLabel: "
|
|
350
|
+
requiredPrompt: "This PayPal username is not registered yet. Register it with PeerAuth, then retry with the same PayPal username.",
|
|
351
|
+
ctaLabel: "Register PayPal Username",
|
|
268
352
|
ctaSubtext: "PayPal Business accounts are not supported at this time.",
|
|
269
|
-
minExtensionVersion: "0.
|
|
353
|
+
minExtensionVersion: "0.5.0"
|
|
270
354
|
}
|
|
271
355
|
},
|
|
272
356
|
monzo: {
|
|
@@ -283,8 +367,20 @@ var BLUEPRINTS = {
|
|
|
283
367
|
identifierLabel: "IBAN",
|
|
284
368
|
placeholder: "IBAN (e.g. DE89...)",
|
|
285
369
|
helperText: "Your IBAN (spaces will be removed)",
|
|
286
|
-
validation
|
|
370
|
+
// transform runs before validation, so the schema sees a spaceless,
|
|
371
|
+
// uppercased IBAN. The MOD-97 refine mirrors curator's N26Processor so a
|
|
372
|
+
// typo'd-but-well-formed IBAN fails here, not after USDC approval + gas.
|
|
373
|
+
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"),
|
|
287
374
|
transform: (v) => v.replace(/\s/g, "").toUpperCase()
|
|
375
|
+
},
|
|
376
|
+
luxon: {
|
|
377
|
+
id: "luxon",
|
|
378
|
+
name: "Luxon",
|
|
379
|
+
identifierLabel: "Email",
|
|
380
|
+
placeholder: "your-luxon-email@example.com",
|
|
381
|
+
helperText: "Email address registered with Luxon",
|
|
382
|
+
validation: import_zod.z.string().email(),
|
|
383
|
+
transform: (v) => v.trim()
|
|
288
384
|
}
|
|
289
385
|
};
|
|
290
386
|
function buildPlatformEntry(bp) {
|
|
@@ -323,7 +419,8 @@ var PLATFORMS = {
|
|
|
323
419
|
ZELLE: buildPlatformEntry(BLUEPRINTS.zelle),
|
|
324
420
|
PAYPAL: buildPlatformEntry(BLUEPRINTS.paypal),
|
|
325
421
|
MONZO: buildPlatformEntry(BLUEPRINTS.monzo),
|
|
326
|
-
N26: buildPlatformEntry(BLUEPRINTS.n26)
|
|
422
|
+
N26: buildPlatformEntry(BLUEPRINTS.n26),
|
|
423
|
+
LUXON: buildPlatformEntry(BLUEPRINTS.luxon)
|
|
327
424
|
};
|
|
328
425
|
function getPlatformById(id) {
|
|
329
426
|
return Object.values(PLATFORMS).find((p) => p.id === id) ?? null;
|
|
@@ -386,6 +483,146 @@ function getPeerExtensionRegistrationInfo(platform) {
|
|
|
386
483
|
const entry = getPlatformById(platform);
|
|
387
484
|
return entry?.extensionRegistration ?? null;
|
|
388
485
|
}
|
|
486
|
+
function getPeerExtensionRegistrationAuthParams(platform, options = {}) {
|
|
487
|
+
const info = getPeerExtensionRegistrationInfo(platform);
|
|
488
|
+
if (!info) return null;
|
|
489
|
+
const params = {
|
|
490
|
+
actionType: `transfer_${info.providerId}`,
|
|
491
|
+
captureMode: "sellerCredential",
|
|
492
|
+
platform: info.providerId
|
|
493
|
+
};
|
|
494
|
+
if (options.attestationServiceUrl) {
|
|
495
|
+
params.attestationServiceUrl = options.attestationServiceUrl;
|
|
496
|
+
}
|
|
497
|
+
return params;
|
|
498
|
+
}
|
|
499
|
+
function resolveRegistrationProcessor(platform) {
|
|
500
|
+
const info = getPeerExtensionRegistrationInfo(platform.id);
|
|
501
|
+
if (info?.providerId === "paypal" || info?.providerId === "wise") {
|
|
502
|
+
return info.providerId;
|
|
503
|
+
}
|
|
504
|
+
throw new Error(`${platform.name} does not require Peer extension registration.`);
|
|
505
|
+
}
|
|
506
|
+
async function postMakerCreate(processorName, payload, apiBaseUrl = API_BASE_URL) {
|
|
507
|
+
const controller = new AbortController();
|
|
508
|
+
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
509
|
+
try {
|
|
510
|
+
const res = await fetch(`${apiBaseUrl}/v2/makers/create`, {
|
|
511
|
+
method: "POST",
|
|
512
|
+
headers: { "Content-Type": "application/json" },
|
|
513
|
+
body: JSON.stringify({ processorName, ...payload }),
|
|
514
|
+
signal: controller.signal
|
|
515
|
+
});
|
|
516
|
+
if (!res.ok) {
|
|
517
|
+
const txt = await res.text().catch(() => "");
|
|
518
|
+
let detail = txt || res.statusText;
|
|
519
|
+
try {
|
|
520
|
+
const parsed = JSON.parse(txt);
|
|
521
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
522
|
+
detail = parsed.message;
|
|
523
|
+
}
|
|
524
|
+
} catch {
|
|
525
|
+
}
|
|
526
|
+
throw new MakersCreateError(
|
|
527
|
+
`makers/create failed (${res.status}): ${detail}`,
|
|
528
|
+
res.status,
|
|
529
|
+
txt
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const json = await res.json();
|
|
533
|
+
if (!json.success || !json.responseObject?.hashedOnchainId) {
|
|
534
|
+
throw new MakersCreateError(
|
|
535
|
+
json.message || "makers/create returned no hashedOnchainId",
|
|
536
|
+
json.statusCode ?? null,
|
|
537
|
+
""
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
return json.responseObject.hashedOnchainId;
|
|
541
|
+
} finally {
|
|
542
|
+
clearTimeout(timeout);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function requireSarCredentialCapture(capturedMetadata) {
|
|
546
|
+
const capture = capturedMetadata.sarCredentialCapture;
|
|
547
|
+
if (!capture?.credentialBundle) {
|
|
548
|
+
throw new Error("Peer metadata did not include a seller credential bundle.");
|
|
549
|
+
}
|
|
550
|
+
if (!capture.offchainId) {
|
|
551
|
+
throw new Error("Peer metadata did not include seller credential offchainId.");
|
|
552
|
+
}
|
|
553
|
+
return capture;
|
|
554
|
+
}
|
|
555
|
+
async function uploadSellerCredentialBundle(processorName, payeeHash, bundle) {
|
|
556
|
+
const controller = new AbortController();
|
|
557
|
+
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
558
|
+
try {
|
|
559
|
+
const res = await fetch(
|
|
560
|
+
`${API_BASE_URL}/v2/makers/${encodeURIComponent(processorName)}/${encodeURIComponent(payeeHash)}/seller-credential`,
|
|
561
|
+
{
|
|
562
|
+
method: "POST",
|
|
563
|
+
headers: { "Content-Type": "application/json" },
|
|
564
|
+
body: JSON.stringify(bundle),
|
|
565
|
+
signal: controller.signal
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
if (!res.ok) {
|
|
569
|
+
const txt = await res.text().catch(() => "");
|
|
570
|
+
let detail = txt || res.statusText;
|
|
571
|
+
try {
|
|
572
|
+
const parsed = JSON.parse(txt);
|
|
573
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
574
|
+
detail = parsed.message;
|
|
575
|
+
}
|
|
576
|
+
} catch {
|
|
577
|
+
}
|
|
578
|
+
throw new Error(`seller-credential upload failed (${res.status}): ${detail}`);
|
|
579
|
+
}
|
|
580
|
+
const json = await res.json();
|
|
581
|
+
if (!json.success || !json.responseObject) {
|
|
582
|
+
throw new Error(json.message || "seller-credential upload returned no status.");
|
|
583
|
+
}
|
|
584
|
+
return json;
|
|
585
|
+
} finally {
|
|
586
|
+
clearTimeout(timeout);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function completePeerExtensionRegistration(input) {
|
|
590
|
+
const processorName = resolveRegistrationProcessor(input.platform);
|
|
591
|
+
if (input.capturedMetadata.platform !== processorName) {
|
|
592
|
+
throw new Error(
|
|
593
|
+
`Peer metadata is for ${input.capturedMetadata.platform}, not ${processorName}.`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
const sarCredentialCapture = requireSarCredentialCapture(input.capturedMetadata);
|
|
597
|
+
const bundle = sarCredentialCapture.credentialBundle;
|
|
598
|
+
if (bundle.platform.toLowerCase() !== processorName) {
|
|
599
|
+
throw new Error("Seller credential bundle platform does not match registration platform.");
|
|
600
|
+
}
|
|
601
|
+
const validation = input.platform.validate(input.identifier);
|
|
602
|
+
if (!validation.valid) {
|
|
603
|
+
throw new Error(validation.error || "Invalid identifier");
|
|
604
|
+
}
|
|
605
|
+
const payload = buildDepositData(processorName, sarCredentialCapture.offchainId);
|
|
606
|
+
if (payload.offchainId !== validation.normalized) {
|
|
607
|
+
throw new Error("Seller credential offchainId does not match registration identifier.");
|
|
608
|
+
}
|
|
609
|
+
const hashedOnchainId = processorName === "wise" ? bundle.payeeIdHash : await postMakerCreate(processorName, payload);
|
|
610
|
+
if (hashedOnchainId.toLowerCase() !== bundle.payeeIdHash.toLowerCase()) {
|
|
611
|
+
throw new Error("Seller credential payee hash does not match registered payee details.");
|
|
612
|
+
}
|
|
613
|
+
const sellerCredentialResponse = await uploadSellerCredentialBundle(
|
|
614
|
+
processorName,
|
|
615
|
+
hashedOnchainId,
|
|
616
|
+
bundle
|
|
617
|
+
);
|
|
618
|
+
return {
|
|
619
|
+
processorName,
|
|
620
|
+
offchainId: payload.offchainId,
|
|
621
|
+
hashedOnchainId,
|
|
622
|
+
sellerCredentialResponse,
|
|
623
|
+
sellerCredentialStatus: sellerCredentialResponse.responseObject
|
|
624
|
+
};
|
|
625
|
+
}
|
|
389
626
|
var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
|
|
390
627
|
function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
391
628
|
const info = getPeerExtensionRegistrationInfo(platform);
|
|
@@ -397,401 +634,26 @@ function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
|
397
634
|
return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
|
|
398
635
|
}
|
|
399
636
|
|
|
400
|
-
// src/errors.ts
|
|
401
|
-
var MakersCreateError = class extends Error {
|
|
402
|
-
status;
|
|
403
|
-
body;
|
|
404
|
-
constructor(message, status, body) {
|
|
405
|
-
super(message);
|
|
406
|
-
this.name = "MakersCreateError";
|
|
407
|
-
this.status = status;
|
|
408
|
-
this.body = body;
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
var OfframpError = class extends Error {
|
|
412
|
-
code;
|
|
413
|
-
step;
|
|
414
|
-
cause;
|
|
415
|
-
txHash;
|
|
416
|
-
depositId;
|
|
417
|
-
constructor(message, code, step, cause, details) {
|
|
418
|
-
super(message);
|
|
419
|
-
this.name = "OfframpError";
|
|
420
|
-
this.code = code;
|
|
421
|
-
this.step = step;
|
|
422
|
-
this.cause = cause;
|
|
423
|
-
this.txHash = details?.txHash;
|
|
424
|
-
this.depositId = details?.depositId;
|
|
425
|
-
}
|
|
426
|
-
};
|
|
427
|
-
function isUserCancellation(error) {
|
|
428
|
-
if (!(error instanceof Error)) return false;
|
|
429
|
-
const msg = error.message.toLowerCase();
|
|
430
|
-
return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("user cancelled") || msg.includes("rejected the request") || msg.includes("action_rejected");
|
|
431
|
-
}
|
|
432
|
-
|
|
433
637
|
// src/otc.ts
|
|
434
638
|
var import_viem = require("viem");
|
|
435
639
|
var import_chains = require("viem/chains");
|
|
436
|
-
|
|
437
|
-
// ../../node_modules/@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json
|
|
438
|
-
var WhitelistPreIntentHook_default = [
|
|
439
|
-
{
|
|
440
|
-
inputs: [
|
|
441
|
-
{
|
|
442
|
-
internalType: "address",
|
|
443
|
-
name: "_orchestratorRegistry",
|
|
444
|
-
type: "address"
|
|
445
|
-
}
|
|
446
|
-
],
|
|
447
|
-
stateMutability: "nonpayable",
|
|
448
|
-
type: "constructor"
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
inputs: [],
|
|
452
|
-
name: "EmptyArray",
|
|
453
|
-
type: "error"
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
inputs: [
|
|
457
|
-
{
|
|
458
|
-
internalType: "address",
|
|
459
|
-
name: "taker",
|
|
460
|
-
type: "address"
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
internalType: "address",
|
|
464
|
-
name: "escrow",
|
|
465
|
-
type: "address"
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
internalType: "uint256",
|
|
469
|
-
name: "depositId",
|
|
470
|
-
type: "uint256"
|
|
471
|
-
}
|
|
472
|
-
],
|
|
473
|
-
name: "TakerNotInWhitelist",
|
|
474
|
-
type: "error"
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
inputs: [
|
|
478
|
-
{
|
|
479
|
-
internalType: "address",
|
|
480
|
-
name: "taker",
|
|
481
|
-
type: "address"
|
|
482
|
-
},
|
|
483
|
-
{
|
|
484
|
-
internalType: "address",
|
|
485
|
-
name: "escrow",
|
|
486
|
-
type: "address"
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
internalType: "uint256",
|
|
490
|
-
name: "depositId",
|
|
491
|
-
type: "uint256"
|
|
492
|
-
}
|
|
493
|
-
],
|
|
494
|
-
name: "TakerNotWhitelisted",
|
|
495
|
-
type: "error"
|
|
496
|
-
},
|
|
497
|
-
{
|
|
498
|
-
inputs: [
|
|
499
|
-
{
|
|
500
|
-
internalType: "address",
|
|
501
|
-
name: "caller",
|
|
502
|
-
type: "address"
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
internalType: "address",
|
|
506
|
-
name: "owner",
|
|
507
|
-
type: "address"
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
internalType: "address",
|
|
511
|
-
name: "delegate",
|
|
512
|
-
type: "address"
|
|
513
|
-
}
|
|
514
|
-
],
|
|
515
|
-
name: "UnauthorizedCallerOrDelegate",
|
|
516
|
-
type: "error"
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
inputs: [
|
|
520
|
-
{
|
|
521
|
-
internalType: "address",
|
|
522
|
-
name: "caller",
|
|
523
|
-
type: "address"
|
|
524
|
-
}
|
|
525
|
-
],
|
|
526
|
-
name: "UnauthorizedOrchestratorCaller",
|
|
527
|
-
type: "error"
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
inputs: [],
|
|
531
|
-
name: "ZeroAddress",
|
|
532
|
-
type: "error"
|
|
533
|
-
},
|
|
534
|
-
{
|
|
535
|
-
anonymous: false,
|
|
536
|
-
inputs: [
|
|
537
|
-
{
|
|
538
|
-
indexed: true,
|
|
539
|
-
internalType: "address",
|
|
540
|
-
name: "escrow",
|
|
541
|
-
type: "address"
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
indexed: true,
|
|
545
|
-
internalType: "uint256",
|
|
546
|
-
name: "depositId",
|
|
547
|
-
type: "uint256"
|
|
548
|
-
},
|
|
549
|
-
{
|
|
550
|
-
indexed: true,
|
|
551
|
-
internalType: "address",
|
|
552
|
-
name: "taker",
|
|
553
|
-
type: "address"
|
|
554
|
-
}
|
|
555
|
-
],
|
|
556
|
-
name: "TakerRemovedFromWhitelist",
|
|
557
|
-
type: "event"
|
|
558
|
-
},
|
|
559
|
-
{
|
|
560
|
-
anonymous: false,
|
|
561
|
-
inputs: [
|
|
562
|
-
{
|
|
563
|
-
indexed: true,
|
|
564
|
-
internalType: "address",
|
|
565
|
-
name: "escrow",
|
|
566
|
-
type: "address"
|
|
567
|
-
},
|
|
568
|
-
{
|
|
569
|
-
indexed: true,
|
|
570
|
-
internalType: "uint256",
|
|
571
|
-
name: "depositId",
|
|
572
|
-
type: "uint256"
|
|
573
|
-
},
|
|
574
|
-
{
|
|
575
|
-
indexed: true,
|
|
576
|
-
internalType: "address",
|
|
577
|
-
name: "taker",
|
|
578
|
-
type: "address"
|
|
579
|
-
}
|
|
580
|
-
],
|
|
581
|
-
name: "TakerWhitelisted",
|
|
582
|
-
type: "event"
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
inputs: [
|
|
586
|
-
{
|
|
587
|
-
internalType: "address",
|
|
588
|
-
name: "_escrow",
|
|
589
|
-
type: "address"
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
internalType: "uint256",
|
|
593
|
-
name: "_depositId",
|
|
594
|
-
type: "uint256"
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
internalType: "address[]",
|
|
598
|
-
name: "_takers",
|
|
599
|
-
type: "address[]"
|
|
600
|
-
}
|
|
601
|
-
],
|
|
602
|
-
name: "addToWhitelist",
|
|
603
|
-
outputs: [],
|
|
604
|
-
stateMutability: "nonpayable",
|
|
605
|
-
type: "function"
|
|
606
|
-
},
|
|
607
|
-
{
|
|
608
|
-
inputs: [
|
|
609
|
-
{
|
|
610
|
-
internalType: "address",
|
|
611
|
-
name: "_escrow",
|
|
612
|
-
type: "address"
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
internalType: "uint256",
|
|
616
|
-
name: "_depositId",
|
|
617
|
-
type: "uint256"
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
internalType: "address",
|
|
621
|
-
name: "_taker",
|
|
622
|
-
type: "address"
|
|
623
|
-
}
|
|
624
|
-
],
|
|
625
|
-
name: "isWhitelisted",
|
|
626
|
-
outputs: [
|
|
627
|
-
{
|
|
628
|
-
internalType: "bool",
|
|
629
|
-
name: "",
|
|
630
|
-
type: "bool"
|
|
631
|
-
}
|
|
632
|
-
],
|
|
633
|
-
stateMutability: "view",
|
|
634
|
-
type: "function"
|
|
635
|
-
},
|
|
636
|
-
{
|
|
637
|
-
inputs: [],
|
|
638
|
-
name: "orchestratorRegistry",
|
|
639
|
-
outputs: [
|
|
640
|
-
{
|
|
641
|
-
internalType: "contract IOrchestratorRegistry",
|
|
642
|
-
name: "",
|
|
643
|
-
type: "address"
|
|
644
|
-
}
|
|
645
|
-
],
|
|
646
|
-
stateMutability: "view",
|
|
647
|
-
type: "function"
|
|
648
|
-
},
|
|
649
|
-
{
|
|
650
|
-
inputs: [
|
|
651
|
-
{
|
|
652
|
-
internalType: "address",
|
|
653
|
-
name: "_escrow",
|
|
654
|
-
type: "address"
|
|
655
|
-
},
|
|
656
|
-
{
|
|
657
|
-
internalType: "uint256",
|
|
658
|
-
name: "_depositId",
|
|
659
|
-
type: "uint256"
|
|
660
|
-
},
|
|
661
|
-
{
|
|
662
|
-
internalType: "address[]",
|
|
663
|
-
name: "_takers",
|
|
664
|
-
type: "address[]"
|
|
665
|
-
}
|
|
666
|
-
],
|
|
667
|
-
name: "removeFromWhitelist",
|
|
668
|
-
outputs: [],
|
|
669
|
-
stateMutability: "nonpayable",
|
|
670
|
-
type: "function"
|
|
671
|
-
},
|
|
672
|
-
{
|
|
673
|
-
inputs: [
|
|
674
|
-
{
|
|
675
|
-
components: [
|
|
676
|
-
{
|
|
677
|
-
internalType: "address",
|
|
678
|
-
name: "taker",
|
|
679
|
-
type: "address"
|
|
680
|
-
},
|
|
681
|
-
{
|
|
682
|
-
internalType: "address",
|
|
683
|
-
name: "escrow",
|
|
684
|
-
type: "address"
|
|
685
|
-
},
|
|
686
|
-
{
|
|
687
|
-
internalType: "uint256",
|
|
688
|
-
name: "depositId",
|
|
689
|
-
type: "uint256"
|
|
690
|
-
},
|
|
691
|
-
{
|
|
692
|
-
internalType: "uint256",
|
|
693
|
-
name: "amount",
|
|
694
|
-
type: "uint256"
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
internalType: "address",
|
|
698
|
-
name: "to",
|
|
699
|
-
type: "address"
|
|
700
|
-
},
|
|
701
|
-
{
|
|
702
|
-
internalType: "bytes32",
|
|
703
|
-
name: "paymentMethod",
|
|
704
|
-
type: "bytes32"
|
|
705
|
-
},
|
|
706
|
-
{
|
|
707
|
-
internalType: "bytes32",
|
|
708
|
-
name: "fiatCurrency",
|
|
709
|
-
type: "bytes32"
|
|
710
|
-
},
|
|
711
|
-
{
|
|
712
|
-
internalType: "uint256",
|
|
713
|
-
name: "conversionRate",
|
|
714
|
-
type: "uint256"
|
|
715
|
-
},
|
|
716
|
-
{
|
|
717
|
-
components: [
|
|
718
|
-
{
|
|
719
|
-
internalType: "address",
|
|
720
|
-
name: "recipient",
|
|
721
|
-
type: "address"
|
|
722
|
-
},
|
|
723
|
-
{
|
|
724
|
-
internalType: "uint256",
|
|
725
|
-
name: "fee",
|
|
726
|
-
type: "uint256"
|
|
727
|
-
}
|
|
728
|
-
],
|
|
729
|
-
internalType: "struct IReferralFee.ReferralFee[]",
|
|
730
|
-
name: "referralFees",
|
|
731
|
-
type: "tuple[]"
|
|
732
|
-
},
|
|
733
|
-
{
|
|
734
|
-
internalType: "bytes",
|
|
735
|
-
name: "preIntentHookData",
|
|
736
|
-
type: "bytes"
|
|
737
|
-
}
|
|
738
|
-
],
|
|
739
|
-
internalType: "struct IPreIntentHook.PreIntentContext",
|
|
740
|
-
name: "_ctx",
|
|
741
|
-
type: "tuple"
|
|
742
|
-
}
|
|
743
|
-
],
|
|
744
|
-
name: "validateSignalIntent",
|
|
745
|
-
outputs: [],
|
|
746
|
-
stateMutability: "view",
|
|
747
|
-
type: "function"
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
inputs: [
|
|
751
|
-
{
|
|
752
|
-
internalType: "address",
|
|
753
|
-
name: "",
|
|
754
|
-
type: "address"
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
internalType: "uint256",
|
|
758
|
-
name: "",
|
|
759
|
-
type: "uint256"
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
internalType: "address",
|
|
763
|
-
name: "",
|
|
764
|
-
type: "address"
|
|
765
|
-
}
|
|
766
|
-
],
|
|
767
|
-
name: "whitelist",
|
|
768
|
-
outputs: [
|
|
769
|
-
{
|
|
770
|
-
internalType: "bool",
|
|
771
|
-
name: "",
|
|
772
|
-
type: "bool"
|
|
773
|
-
}
|
|
774
|
-
],
|
|
775
|
-
stateMutability: "view",
|
|
776
|
-
type: "function"
|
|
777
|
-
}
|
|
778
|
-
];
|
|
640
|
+
var import_WhitelistPreIntentHook = __toESM(require("@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json"), 1);
|
|
779
641
|
|
|
780
642
|
// src/sdk-client.ts
|
|
781
643
|
var import_sdk4 = require("@zkp2p/sdk");
|
|
782
|
-
function createSdkClient(walletClient) {
|
|
644
|
+
function createSdkClient(walletClient, apiBaseUrl = API_BASE_URL) {
|
|
783
645
|
return new import_sdk4.OfframpClient({
|
|
784
646
|
walletClient,
|
|
785
647
|
chainId: BASE_CHAIN_ID,
|
|
786
648
|
runtimeEnv: RUNTIME_ENV,
|
|
787
649
|
rpcUrl: BASE_RPC_URL,
|
|
788
|
-
baseApiUrl:
|
|
650
|
+
baseApiUrl: apiBaseUrl
|
|
789
651
|
});
|
|
790
652
|
}
|
|
791
653
|
|
|
792
654
|
// src/otc.ts
|
|
793
655
|
var WHITELIST_HOOK_ADDRESS = "0xda023Ea0d789A41BcF5866F7B6BBd2CaDF9b79B8";
|
|
794
|
-
var OTC_BASE_URL = "https://usdctofiat.xyz";
|
|
656
|
+
var OTC_BASE_URL = "https://otc.usdctofiat.xyz";
|
|
795
657
|
async function enableOtc(walletClient, depositId, takerAddress, escrowAddress) {
|
|
796
658
|
if (!(0, import_viem.isAddress)(takerAddress)) {
|
|
797
659
|
throw new OfframpError("Invalid taker address", "VALIDATION");
|
|
@@ -842,7 +704,7 @@ async function enableOtc(walletClient, depositId, takerAddress, escrowAddress) {
|
|
|
842
704
|
}
|
|
843
705
|
function getOtcLink(depositId, escrowAddress) {
|
|
844
706
|
const escrow = escrowAddress || ESCROW_ADDRESS;
|
|
845
|
-
return `${OTC_BASE_URL}/
|
|
707
|
+
return `${OTC_BASE_URL}/d/${escrow}/${depositId}`;
|
|
846
708
|
}
|
|
847
709
|
async function writeContract(walletClient, functionName, args) {
|
|
848
710
|
const wc = walletClient;
|
|
@@ -851,7 +713,7 @@ async function writeContract(walletClient, functionName, args) {
|
|
|
851
713
|
}
|
|
852
714
|
await wc.writeContract({
|
|
853
715
|
address: WHITELIST_HOOK_ADDRESS,
|
|
854
|
-
abi:
|
|
716
|
+
abi: import_WhitelistPreIntentHook.default,
|
|
855
717
|
functionName,
|
|
856
718
|
args
|
|
857
719
|
});
|
|
@@ -860,7 +722,7 @@ async function readIsWhitelisted(escrow, depositId, taker) {
|
|
|
860
722
|
const publicClient = (0, import_viem.createPublicClient)({ chain: import_chains.base, transport: (0, import_viem.http)(BASE_RPC_URL) });
|
|
861
723
|
return publicClient.readContract({
|
|
862
724
|
address: WHITELIST_HOOK_ADDRESS,
|
|
863
|
-
abi:
|
|
725
|
+
abi: import_WhitelistPreIntentHook.default,
|
|
864
726
|
functionName: "isWhitelisted",
|
|
865
727
|
args: [escrow, depositId, taker]
|
|
866
728
|
});
|
|
@@ -1109,41 +971,24 @@ function extractDepositIdFromLogs(logs) {
|
|
|
1109
971
|
}
|
|
1110
972
|
return null;
|
|
1111
973
|
}
|
|
1112
|
-
async function
|
|
974
|
+
async function validatePayeeDetails(processorName, offchainId, apiBaseUrl = API_BASE_URL) {
|
|
1113
975
|
const controller = new AbortController();
|
|
1114
976
|
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
1115
977
|
try {
|
|
1116
|
-
const res = await fetch(`${
|
|
978
|
+
const res = await fetch(`${apiBaseUrl}/v2/makers/validate`, {
|
|
1117
979
|
method: "POST",
|
|
1118
980
|
headers: { "Content-Type": "application/json" },
|
|
1119
|
-
body: JSON.stringify({ processorName,
|
|
981
|
+
body: JSON.stringify({ processorName, offchainId }),
|
|
1120
982
|
signal: controller.signal
|
|
1121
983
|
});
|
|
1122
|
-
if (!res.ok)
|
|
1123
|
-
const txt = await res.text().catch(() => "");
|
|
1124
|
-
let detail = txt || res.statusText;
|
|
1125
|
-
try {
|
|
1126
|
-
const parsed = JSON.parse(txt);
|
|
1127
|
-
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
1128
|
-
detail = parsed.message;
|
|
1129
|
-
}
|
|
1130
|
-
} catch {
|
|
1131
|
-
}
|
|
1132
|
-
throw new MakersCreateError(
|
|
1133
|
-
`makers/create failed (${res.status}): ${detail}`,
|
|
1134
|
-
res.status,
|
|
1135
|
-
txt
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
984
|
+
if (!res.ok) return "unknown";
|
|
1138
985
|
const json = await res.json();
|
|
1139
|
-
if (
|
|
1140
|
-
|
|
1141
|
-
json.message || "makers/create returned no hashedOnchainId",
|
|
1142
|
-
json.statusCode ?? null,
|
|
1143
|
-
""
|
|
1144
|
-
);
|
|
986
|
+
if (typeof json.responseObject === "boolean") {
|
|
987
|
+
return json.responseObject ? "valid" : "invalid";
|
|
1145
988
|
}
|
|
1146
|
-
return
|
|
989
|
+
return "unknown";
|
|
990
|
+
} catch {
|
|
991
|
+
return "unknown";
|
|
1147
992
|
} finally {
|
|
1148
993
|
clearTimeout(timeout);
|
|
1149
994
|
}
|
|
@@ -1208,10 +1053,11 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1208
1053
|
return null;
|
|
1209
1054
|
}
|
|
1210
1055
|
}
|
|
1211
|
-
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1056
|
+
async function offramp(walletClient, params, onProgress, telemetryContext, apiBaseUrl) {
|
|
1212
1057
|
const { amount, platform, currency, identifier } = params;
|
|
1213
1058
|
const platformId = platform.id;
|
|
1214
1059
|
const currencyCode = currency.code;
|
|
1060
|
+
const resolvedApiBaseUrl = resolveApiBaseUrl(apiBaseUrl);
|
|
1215
1061
|
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1216
1062
|
sdkVersion: SDK_VERSION,
|
|
1217
1063
|
telemetry: true,
|
|
@@ -1256,7 +1102,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1256
1102
|
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1257
1103
|
if (existing) {
|
|
1258
1104
|
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1259
|
-
const client2 = createSdkClient(walletClient);
|
|
1105
|
+
const client2 = createSdkClient(walletClient, resolvedApiBaseUrl);
|
|
1260
1106
|
const txOverrides2 = { referrer: [REFERRER] };
|
|
1261
1107
|
try {
|
|
1262
1108
|
const result2 = await client2.setRateManager({
|
|
@@ -1292,7 +1138,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1292
1138
|
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1293
1139
|
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1294
1140
|
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1295
|
-
const maxUnits = usdcToUnits(String(Math.min(amt,
|
|
1141
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, MAX_ORDER_USDC)));
|
|
1296
1142
|
try {
|
|
1297
1143
|
const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains3.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
|
|
1298
1144
|
const balance = await publicClient.readContract({
|
|
@@ -1319,7 +1165,22 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1319
1165
|
} catch (err) {
|
|
1320
1166
|
if (err instanceof OfframpError) throw err;
|
|
1321
1167
|
}
|
|
1322
|
-
const
|
|
1168
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1169
|
+
if (!getPeerExtensionRegistrationInfo(platformId)) {
|
|
1170
|
+
const outcome = await validatePayeeDetails(
|
|
1171
|
+
canonicalName,
|
|
1172
|
+
normalizedIdentifier,
|
|
1173
|
+
resolvedApiBaseUrl
|
|
1174
|
+
);
|
|
1175
|
+
if (outcome === "invalid") {
|
|
1176
|
+
throw new OfframpError(
|
|
1177
|
+
`Couldn't verify that ${platform.name} ${platform.identifier.label.toLowerCase()}. Double-check it and try again.`,
|
|
1178
|
+
"VALIDATION",
|
|
1179
|
+
"registering"
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const client = createSdkClient(walletClient, resolvedApiBaseUrl);
|
|
1323
1184
|
const txOverrides = { referrer: [REFERRER] };
|
|
1324
1185
|
emitProgress({ step: "approving" });
|
|
1325
1186
|
try {
|
|
@@ -1343,16 +1204,15 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1343
1204
|
}
|
|
1344
1205
|
emitProgress({ step: "registering" });
|
|
1345
1206
|
let hashedOnchainId;
|
|
1346
|
-
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1347
1207
|
try {
|
|
1348
1208
|
const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
|
|
1349
|
-
hashedOnchainId = await
|
|
1209
|
+
hashedOnchainId = await postMakerCreate(canonicalName, payeePayload, resolvedApiBaseUrl);
|
|
1350
1210
|
} catch (err) {
|
|
1351
1211
|
const status = err instanceof MakersCreateError ? err.status : null;
|
|
1352
1212
|
const message = err instanceof Error ? err.message : String(err);
|
|
1353
1213
|
if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
|
|
1354
1214
|
throw new OfframpError(
|
|
1355
|
-
`${platform.name} maker is not yet registered in the Peer extension.
|
|
1215
|
+
`${platform.name} maker is not yet registered in the Peer extension. Register this handle with Peer, then retry.`,
|
|
1356
1216
|
"EXTENSION_REGISTRATION_REQUIRED",
|
|
1357
1217
|
"registering",
|
|
1358
1218
|
err
|
|
@@ -1526,6 +1386,14 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1526
1386
|
// src/offramp.ts
|
|
1527
1387
|
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1528
1388
|
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1389
|
+
var warnedIdempotencyUnavailable = false;
|
|
1390
|
+
function warnIdempotencyUnavailableOnce() {
|
|
1391
|
+
if (warnedIdempotencyUnavailable) return;
|
|
1392
|
+
warnedIdempotencyUnavailable = true;
|
|
1393
|
+
console.warn(
|
|
1394
|
+
"[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."
|
|
1395
|
+
);
|
|
1396
|
+
}
|
|
1529
1397
|
function getWalletAddress(walletClient) {
|
|
1530
1398
|
const address = walletClient.account?.address;
|
|
1531
1399
|
if (!address) {
|
|
@@ -1596,8 +1464,10 @@ function buildCurrencyGroups() {
|
|
|
1596
1464
|
var Offramp = class {
|
|
1597
1465
|
walletClient;
|
|
1598
1466
|
telemetry;
|
|
1467
|
+
apiBaseUrl;
|
|
1599
1468
|
constructor(options) {
|
|
1600
1469
|
this.walletClient = options.walletClient;
|
|
1470
|
+
this.apiBaseUrl = options.apiBaseUrl;
|
|
1601
1471
|
this.telemetry = createTelemetryContext({
|
|
1602
1472
|
sdkVersion: SDK_VERSION,
|
|
1603
1473
|
telemetry: options.telemetry,
|
|
@@ -1620,6 +1490,9 @@ var Offramp = class {
|
|
|
1620
1490
|
referralId: flowTelemetry.referralId
|
|
1621
1491
|
};
|
|
1622
1492
|
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1493
|
+
if (sanitizedKey && typeof sessionStorage === "undefined") {
|
|
1494
|
+
warnIdempotencyUnavailableOnce();
|
|
1495
|
+
}
|
|
1623
1496
|
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1624
1497
|
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1625
1498
|
if (storageKey) {
|
|
@@ -1630,7 +1503,8 @@ var Offramp = class {
|
|
|
1630
1503
|
this.walletClient,
|
|
1631
1504
|
sanitizedParams,
|
|
1632
1505
|
onProgress,
|
|
1633
|
-
flowTelemetry
|
|
1506
|
+
flowTelemetry,
|
|
1507
|
+
this.apiBaseUrl
|
|
1634
1508
|
);
|
|
1635
1509
|
if (storageKey) {
|
|
1636
1510
|
writeIdempotencyResult(storageKey, result);
|
|
@@ -1761,25 +1635,108 @@ var import_react2 = require("react");
|
|
|
1761
1635
|
|
|
1762
1636
|
// src/extension.ts
|
|
1763
1637
|
var import_sdk7 = require("@zkp2p/sdk");
|
|
1638
|
+
var resolveWindow = (options) => {
|
|
1639
|
+
if (options?.window) {
|
|
1640
|
+
return options.window;
|
|
1641
|
+
}
|
|
1642
|
+
if (typeof window === "undefined") {
|
|
1643
|
+
return void 0;
|
|
1644
|
+
}
|
|
1645
|
+
return window;
|
|
1646
|
+
};
|
|
1647
|
+
var requirePeer = (options) => {
|
|
1648
|
+
const resolvedWindow = resolveWindow(options);
|
|
1649
|
+
if (!resolvedWindow) {
|
|
1650
|
+
throw new Error("Peer extension SDK requires a browser window.");
|
|
1651
|
+
}
|
|
1652
|
+
if (!resolvedWindow.peer) {
|
|
1653
|
+
throw new Error("Peer extension not available. Install or enable the Peer extension.");
|
|
1654
|
+
}
|
|
1655
|
+
return resolvedWindow.peer;
|
|
1656
|
+
};
|
|
1657
|
+
var isPeerExtensionAvailable = (options) => {
|
|
1658
|
+
const resolvedWindow = resolveWindow(options);
|
|
1659
|
+
return Boolean(resolvedWindow?.peer);
|
|
1660
|
+
};
|
|
1661
|
+
var hasHeadlessMetadataBridge = (peer) => typeof peer.authenticate === "function" && typeof peer.onMetadataMessage === "function";
|
|
1662
|
+
var requirePeerMetadataBridge = (options) => {
|
|
1663
|
+
const peer = requirePeer(options);
|
|
1664
|
+
if (!hasHeadlessMetadataBridge(peer)) {
|
|
1665
|
+
throw new Error("Peer extension metadata bridge unavailable. Install or update Peer.");
|
|
1666
|
+
}
|
|
1667
|
+
return peer;
|
|
1668
|
+
};
|
|
1669
|
+
var isPeerExtensionMetadataBridgeAvailable = (options) => {
|
|
1670
|
+
const resolvedWindow = resolveWindow(options);
|
|
1671
|
+
return Boolean(resolvedWindow?.peer && hasHeadlessMetadataBridge(resolvedWindow.peer));
|
|
1672
|
+
};
|
|
1673
|
+
var openPeerExtensionInstallPage = (options) => {
|
|
1674
|
+
const resolvedWindow = resolveWindow(options);
|
|
1675
|
+
if (!resolvedWindow) {
|
|
1676
|
+
throw new Error("Peer extension SDK requires a browser window.");
|
|
1677
|
+
}
|
|
1678
|
+
resolvedWindow.open(import_sdk7.PEER_EXTENSION_CHROME_URL, "_blank", "noopener,noreferrer");
|
|
1679
|
+
};
|
|
1680
|
+
var getPeerExtensionState = async (options) => {
|
|
1681
|
+
if (!isPeerExtensionAvailable(options)) {
|
|
1682
|
+
return "needs_install";
|
|
1683
|
+
}
|
|
1684
|
+
try {
|
|
1685
|
+
const status = await requirePeer(options).checkConnectionStatus();
|
|
1686
|
+
return status === "connected" ? "ready" : "needs_connection";
|
|
1687
|
+
} catch {
|
|
1688
|
+
return "needs_connection";
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
var createPeerExtensionSdk = (options = {}) => {
|
|
1692
|
+
const legacySdk = (0, import_sdk7.createPeerExtensionSdk)(
|
|
1693
|
+
options
|
|
1694
|
+
);
|
|
1695
|
+
return {
|
|
1696
|
+
isAvailable: () => isPeerExtensionAvailable(options),
|
|
1697
|
+
requestConnection: () => requirePeer(options).requestConnection(),
|
|
1698
|
+
checkConnectionStatus: () => requirePeer(options).checkConnectionStatus(),
|
|
1699
|
+
getVersion: () => requirePeer(options).getVersion(),
|
|
1700
|
+
authenticate: (params) => requirePeerMetadataBridge(options).authenticate(params),
|
|
1701
|
+
onMetadataMessage: (callback) => requirePeerMetadataBridge(options).onMetadataMessage(callback),
|
|
1702
|
+
openSidebar: (route) => legacySdk.openSidebar(route),
|
|
1703
|
+
onramp: (params, callback) => legacySdk.onramp(params, callback),
|
|
1704
|
+
getOnrampTransaction: (intentHash) => legacySdk.getOnrampTransaction(intentHash),
|
|
1705
|
+
openInstallPage: () => openPeerExtensionInstallPage(options),
|
|
1706
|
+
getState: () => getPeerExtensionState(options)
|
|
1707
|
+
};
|
|
1708
|
+
};
|
|
1709
|
+
var peerExtensionSdk = createPeerExtensionSdk();
|
|
1764
1710
|
|
|
1765
1711
|
// src/hooks/usePeerExtensionRegistration.ts
|
|
1766
1712
|
function usePeerExtensionRegistration(platform) {
|
|
1767
|
-
const
|
|
1713
|
+
const platformId = platform?.id ?? null;
|
|
1714
|
+
const info = platformId ? getPeerExtensionRegistrationInfo(platformId) : null;
|
|
1715
|
+
const authParams = (0, import_react2.useMemo)(
|
|
1716
|
+
() => platformId ? getPeerExtensionRegistrationAuthParams(platformId) : null,
|
|
1717
|
+
[platformId]
|
|
1718
|
+
);
|
|
1768
1719
|
const [phase, setPhase] = (0, import_react2.useState)(
|
|
1769
1720
|
info ? "checking" : "unsupported"
|
|
1770
1721
|
);
|
|
1771
1722
|
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
1772
1723
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
1724
|
+
const [capturedMetadata, setCapturedMetadata] = (0, import_react2.useState)(null);
|
|
1773
1725
|
const mountedRef = (0, import_react2.useRef)(true);
|
|
1774
1726
|
const providerId = info?.providerId ?? null;
|
|
1727
|
+
const metadataPlatform = authParams?.platform ?? null;
|
|
1775
1728
|
const refresh = (0, import_react2.useCallback)(async () => {
|
|
1776
1729
|
if (!providerId) {
|
|
1777
1730
|
setPhase("unsupported");
|
|
1778
1731
|
return;
|
|
1779
1732
|
}
|
|
1780
1733
|
try {
|
|
1781
|
-
const state = await
|
|
1734
|
+
const state = await peerExtensionSdk.getState();
|
|
1782
1735
|
if (!mountedRef.current) return;
|
|
1736
|
+
if (state === "ready" && !isPeerExtensionMetadataBridgeAvailable()) {
|
|
1737
|
+
setPhase("needs_install");
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1783
1740
|
setPhase(mapSdkState(state));
|
|
1784
1741
|
} catch {
|
|
1785
1742
|
if (!mountedRef.current) return;
|
|
@@ -1790,6 +1747,7 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1790
1747
|
mountedRef.current = true;
|
|
1791
1748
|
setError(null);
|
|
1792
1749
|
setBusy(false);
|
|
1750
|
+
setCapturedMetadata(null);
|
|
1793
1751
|
if (!providerId) {
|
|
1794
1752
|
setPhase("unsupported");
|
|
1795
1753
|
} else {
|
|
@@ -1800,10 +1758,25 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1800
1758
|
mountedRef.current = false;
|
|
1801
1759
|
};
|
|
1802
1760
|
}, [providerId, refresh]);
|
|
1761
|
+
(0, import_react2.useEffect)(() => {
|
|
1762
|
+
setCapturedMetadata(null);
|
|
1763
|
+
if (!metadataPlatform) return;
|
|
1764
|
+
try {
|
|
1765
|
+
return peerExtensionSdk.onMetadataMessage((message) => {
|
|
1766
|
+
if (!mountedRef.current || message.platform !== metadataPlatform) return;
|
|
1767
|
+
setCapturedMetadata(message);
|
|
1768
|
+
if (message.errorMessage) {
|
|
1769
|
+
setError(message.errorMessage);
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
} catch {
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
}, [metadataPlatform, phase]);
|
|
1803
1776
|
const installExtension = (0, import_react2.useCallback)(() => {
|
|
1804
1777
|
setError(null);
|
|
1805
1778
|
try {
|
|
1806
|
-
|
|
1779
|
+
peerExtensionSdk.openInstallPage();
|
|
1807
1780
|
} catch {
|
|
1808
1781
|
if (typeof window !== "undefined") {
|
|
1809
1782
|
window.open(import_sdk7.PEER_EXTENSION_CHROME_URL, "_blank", "noopener,noreferrer");
|
|
@@ -1811,15 +1784,14 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1811
1784
|
}
|
|
1812
1785
|
}, []);
|
|
1813
1786
|
const connectExtension = (0, import_react2.useCallback)(async () => {
|
|
1814
|
-
if (!
|
|
1787
|
+
if (!authParams) return false;
|
|
1815
1788
|
setError(null);
|
|
1816
1789
|
setBusy(true);
|
|
1817
1790
|
try {
|
|
1818
|
-
const approved = await
|
|
1791
|
+
const approved = await peerExtensionSdk.requestConnection();
|
|
1819
1792
|
if (!mountedRef.current) return approved;
|
|
1820
1793
|
if (approved) {
|
|
1821
|
-
setPhase("ready");
|
|
1822
|
-
import_sdk7.peerExtensionSdk.openSidebar(`/verify/${providerId}`);
|
|
1794
|
+
setPhase(isPeerExtensionMetadataBridgeAvailable() ? "ready" : "needs_install");
|
|
1823
1795
|
} else {
|
|
1824
1796
|
setError("Connection was not approved. Try again once you've approved Peer.");
|
|
1825
1797
|
}
|
|
@@ -1834,40 +1806,89 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1834
1806
|
} finally {
|
|
1835
1807
|
if (mountedRef.current) setBusy(false);
|
|
1836
1808
|
}
|
|
1837
|
-
}, [
|
|
1838
|
-
const
|
|
1839
|
-
if (!
|
|
1809
|
+
}, [authParams, refresh]);
|
|
1810
|
+
const startRegistrationCapture = (0, import_react2.useCallback)(async () => {
|
|
1811
|
+
if (!authParams) return;
|
|
1840
1812
|
setError(null);
|
|
1841
|
-
|
|
1842
|
-
if (!mountedRef.current) return;
|
|
1843
|
-
const nextPhase = mapSdkState(state);
|
|
1844
|
-
setPhase(nextPhase);
|
|
1845
|
-
if (nextPhase === "needs_install") {
|
|
1846
|
-
installExtension();
|
|
1847
|
-
return;
|
|
1848
|
-
}
|
|
1849
|
-
if (nextPhase === "needs_connection") {
|
|
1850
|
-
await connectExtension();
|
|
1851
|
-
return;
|
|
1852
|
-
}
|
|
1813
|
+
setBusy(true);
|
|
1853
1814
|
try {
|
|
1854
|
-
|
|
1815
|
+
const state = await peerExtensionSdk.getState().catch(() => "needs_install");
|
|
1816
|
+
if (!mountedRef.current) return;
|
|
1817
|
+
const nextPhase = state === "ready" && !isPeerExtensionMetadataBridgeAvailable() ? "needs_install" : mapSdkState(state);
|
|
1818
|
+
setPhase(nextPhase);
|
|
1819
|
+
if (nextPhase === "needs_install") {
|
|
1820
|
+
installExtension();
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
if (nextPhase === "needs_connection") {
|
|
1824
|
+
const approved = await peerExtensionSdk.requestConnection();
|
|
1825
|
+
if (!mountedRef.current) return;
|
|
1826
|
+
if (!approved) {
|
|
1827
|
+
setError("Connection was not approved. Try again once you've approved Peer.");
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
if (!isPeerExtensionMetadataBridgeAvailable()) {
|
|
1831
|
+
setPhase("needs_install");
|
|
1832
|
+
installExtension();
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
setPhase("ready");
|
|
1836
|
+
}
|
|
1837
|
+
setCapturedMetadata(null);
|
|
1838
|
+
peerExtensionSdk.authenticate(authParams);
|
|
1855
1839
|
} catch (err) {
|
|
1856
1840
|
setError(
|
|
1857
|
-
err instanceof Error ? err.message : "Couldn't
|
|
1841
|
+
err instanceof Error ? err.message : "Couldn't start Peer extension registration. Make sure Peer is enabled."
|
|
1858
1842
|
);
|
|
1859
1843
|
refresh();
|
|
1844
|
+
} finally {
|
|
1845
|
+
if (mountedRef.current) setBusy(false);
|
|
1860
1846
|
}
|
|
1861
|
-
}, [
|
|
1847
|
+
}, [authParams, installExtension, refresh]);
|
|
1848
|
+
const openVerifySidebar = (0, import_react2.useCallback)(
|
|
1849
|
+
async () => startRegistrationCapture(),
|
|
1850
|
+
[startRegistrationCapture]
|
|
1851
|
+
);
|
|
1852
|
+
const completeRegistration = (0, import_react2.useCallback)(
|
|
1853
|
+
async (walletClient, params, options = {}) => {
|
|
1854
|
+
const metadata = options.capturedMetadata ?? capturedMetadata;
|
|
1855
|
+
if (!metadata) {
|
|
1856
|
+
const message = "No Peer metadata captured yet. Start registration first.";
|
|
1857
|
+
setError(message);
|
|
1858
|
+
throw new Error(message);
|
|
1859
|
+
}
|
|
1860
|
+
setError(null);
|
|
1861
|
+
setBusy(true);
|
|
1862
|
+
try {
|
|
1863
|
+
await completePeerExtensionRegistration({
|
|
1864
|
+
platform: params.platform,
|
|
1865
|
+
identifier: params.identifier,
|
|
1866
|
+
capturedMetadata: metadata
|
|
1867
|
+
});
|
|
1868
|
+
return await offramp(walletClient, params, options.onProgress);
|
|
1869
|
+
} catch (err) {
|
|
1870
|
+
setError(
|
|
1871
|
+
err instanceof Error ? err.message : "Couldn't complete Peer extension registration. Try capturing metadata again."
|
|
1872
|
+
);
|
|
1873
|
+
throw err;
|
|
1874
|
+
} finally {
|
|
1875
|
+
if (mountedRef.current) setBusy(false);
|
|
1876
|
+
}
|
|
1877
|
+
},
|
|
1878
|
+
[capturedMetadata]
|
|
1879
|
+
);
|
|
1862
1880
|
return {
|
|
1863
1881
|
phase,
|
|
1864
1882
|
info,
|
|
1865
1883
|
busy,
|
|
1866
1884
|
error,
|
|
1885
|
+
capturedMetadata,
|
|
1867
1886
|
refresh,
|
|
1868
1887
|
installExtension,
|
|
1869
1888
|
connectExtension,
|
|
1870
|
-
|
|
1889
|
+
startRegistrationCapture,
|
|
1890
|
+
openVerifySidebar,
|
|
1891
|
+
completeRegistration
|
|
1871
1892
|
};
|
|
1872
1893
|
}
|
|
1873
1894
|
function mapSdkState(state) {
|