@usdctofiat/offramp 3.0.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -1
- package/README.md +128 -23
- package/dist/{chunk-4KKKDDES.js → chunk-QMO7HTBZ.js} +255 -96
- package/dist/chunk-QMO7HTBZ.js.map +1 -0
- package/dist/{errors-D9bV_DWE.d.cts → errors-DYCRKcnY.d.cts} +44 -6
- package/dist/{errors-D9bV_DWE.d.ts → errors-DYCRKcnY.d.ts} +44 -6
- package/dist/index.cjs +259 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -6
- package/dist/index.d.ts +71 -6
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +329 -111
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +24 -13
- package/dist/react.d.ts +24 -13
- package/dist/react.js +106 -27
- package/dist/react.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-4KKKDDES.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -40,11 +40,15 @@ var import_react = require("react");
|
|
|
40
40
|
|
|
41
41
|
// src/config.ts
|
|
42
42
|
var import_sdk = require("@zkp2p/sdk");
|
|
43
|
-
var SDK_VERSION = "
|
|
43
|
+
var SDK_VERSION = "4.0.0";
|
|
44
44
|
var BASE_CHAIN_ID = 8453;
|
|
45
45
|
var RUNTIME_ENV = "production";
|
|
46
46
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
47
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
|
+
}
|
|
48
52
|
var contracts = (0, import_sdk.getContracts)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
49
53
|
var addresses = contracts.addresses;
|
|
50
54
|
var addrs = addresses;
|
|
@@ -61,6 +65,7 @@ var DEFAULT_INTENT_GUARDIAN_ADDRESS = "0xe29a5BD4D0CEbA6C125A0361E7D20ab4eA275C2
|
|
|
61
65
|
var REFERRER = "galleonlabs";
|
|
62
66
|
var MIN_DEPOSIT_USDC = 1;
|
|
63
67
|
var MIN_ORDER_USDC = 1;
|
|
68
|
+
var MAX_ORDER_USDC = 2500;
|
|
64
69
|
var INDEXER_MAX_ATTEMPTS = 12;
|
|
65
70
|
var INDEXER_INITIAL_DELAY_MS = 1e3;
|
|
66
71
|
var INDEXER_MAX_DELAY_MS = 1e4;
|
|
@@ -92,6 +97,41 @@ var import_sdk6 = require("@zkp2p/sdk");
|
|
|
92
97
|
// src/platforms.ts
|
|
93
98
|
var import_sdk3 = require("@zkp2p/sdk");
|
|
94
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
|
|
95
135
|
var PAYMENT_CATALOG = (0, import_sdk3.getPaymentMethodsCatalog)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
96
136
|
var ZELLE_HASH_LOOKUP_NAMES = ["zelle", "zelle-bofa", "zelle-chase", "zelle-citi"];
|
|
97
137
|
var FALLBACK_CURRENCIES = {
|
|
@@ -443,6 +483,113 @@ function getPeerExtensionRegistrationInfo(platform) {
|
|
|
443
483
|
const entry = getPlatformById(platform);
|
|
444
484
|
return entry?.extensionRegistration ?? null;
|
|
445
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, offchainId, bundle) {
|
|
556
|
+
const params = processorName === "wise" ? { platform: "wise", bundle } : { platform: processorName, offchainId, bundle };
|
|
557
|
+
return (0, import_sdk3.apiUploadSellerCredentialBundle)(params, API_BASE_URL, PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
558
|
+
}
|
|
559
|
+
async function completePeerExtensionRegistration(input) {
|
|
560
|
+
const processorName = resolveRegistrationProcessor(input.platform);
|
|
561
|
+
if (input.capturedMetadata.platform !== processorName) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
`Peer metadata is for ${input.capturedMetadata.platform}, not ${processorName}.`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
const sarCredentialCapture = requireSarCredentialCapture(input.capturedMetadata);
|
|
567
|
+
const bundle = sarCredentialCapture.credentialBundle;
|
|
568
|
+
if (bundle.platform.toLowerCase() !== processorName) {
|
|
569
|
+
throw new Error("Seller credential bundle platform does not match registration platform.");
|
|
570
|
+
}
|
|
571
|
+
const validation = input.platform.validate(input.identifier);
|
|
572
|
+
if (!validation.valid) {
|
|
573
|
+
throw new Error(validation.error || "Invalid identifier");
|
|
574
|
+
}
|
|
575
|
+
const payload = buildDepositData(processorName, sarCredentialCapture.offchainId);
|
|
576
|
+
if (payload.offchainId !== validation.normalized) {
|
|
577
|
+
throw new Error("Seller credential offchainId does not match registration identifier.");
|
|
578
|
+
}
|
|
579
|
+
const hashedOnchainId = bundle.payeeIdHash;
|
|
580
|
+
const sellerCredentialResponse = await uploadSellerCredentialBundle(
|
|
581
|
+
processorName,
|
|
582
|
+
payload.offchainId,
|
|
583
|
+
bundle
|
|
584
|
+
);
|
|
585
|
+
return {
|
|
586
|
+
processorName,
|
|
587
|
+
offchainId: payload.offchainId,
|
|
588
|
+
hashedOnchainId,
|
|
589
|
+
sellerCredentialResponse,
|
|
590
|
+
sellerCredentialStatus: sellerCredentialResponse.responseObject
|
|
591
|
+
};
|
|
592
|
+
}
|
|
446
593
|
var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
|
|
447
594
|
function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
448
595
|
const info = getPeerExtensionRegistrationInfo(platform);
|
|
@@ -454,39 +601,6 @@ function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
|
454
601
|
return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
|
|
455
602
|
}
|
|
456
603
|
|
|
457
|
-
// src/errors.ts
|
|
458
|
-
var MakersCreateError = class extends Error {
|
|
459
|
-
status;
|
|
460
|
-
body;
|
|
461
|
-
constructor(message, status, body) {
|
|
462
|
-
super(message);
|
|
463
|
-
this.name = "MakersCreateError";
|
|
464
|
-
this.status = status;
|
|
465
|
-
this.body = body;
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
var OfframpError = class extends Error {
|
|
469
|
-
code;
|
|
470
|
-
step;
|
|
471
|
-
cause;
|
|
472
|
-
txHash;
|
|
473
|
-
depositId;
|
|
474
|
-
constructor(message, code, step, cause, details) {
|
|
475
|
-
super(message);
|
|
476
|
-
this.name = "OfframpError";
|
|
477
|
-
this.code = code;
|
|
478
|
-
this.step = step;
|
|
479
|
-
this.cause = cause;
|
|
480
|
-
this.txHash = details?.txHash;
|
|
481
|
-
this.depositId = details?.depositId;
|
|
482
|
-
}
|
|
483
|
-
};
|
|
484
|
-
function isUserCancellation(error) {
|
|
485
|
-
if (!(error instanceof Error)) return false;
|
|
486
|
-
const msg = error.message.toLowerCase();
|
|
487
|
-
return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("user cancelled") || msg.includes("rejected the request") || msg.includes("action_rejected");
|
|
488
|
-
}
|
|
489
|
-
|
|
490
604
|
// src/otc.ts
|
|
491
605
|
var import_viem = require("viem");
|
|
492
606
|
var import_chains = require("viem/chains");
|
|
@@ -494,13 +608,13 @@ var import_WhitelistPreIntentHook = __toESM(require("@zkp2p/contracts-v2/abis/ba
|
|
|
494
608
|
|
|
495
609
|
// src/sdk-client.ts
|
|
496
610
|
var import_sdk4 = require("@zkp2p/sdk");
|
|
497
|
-
function createSdkClient(walletClient) {
|
|
611
|
+
function createSdkClient(walletClient, apiBaseUrl = API_BASE_URL) {
|
|
498
612
|
return new import_sdk4.OfframpClient({
|
|
499
613
|
walletClient,
|
|
500
614
|
chainId: BASE_CHAIN_ID,
|
|
501
615
|
runtimeEnv: RUNTIME_ENV,
|
|
502
616
|
rpcUrl: BASE_RPC_URL,
|
|
503
|
-
baseApiUrl:
|
|
617
|
+
baseApiUrl: apiBaseUrl
|
|
504
618
|
});
|
|
505
619
|
}
|
|
506
620
|
|
|
@@ -824,50 +938,11 @@ function extractDepositIdFromLogs(logs) {
|
|
|
824
938
|
}
|
|
825
939
|
return null;
|
|
826
940
|
}
|
|
827
|
-
async function
|
|
828
|
-
const controller = new AbortController();
|
|
829
|
-
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
830
|
-
try {
|
|
831
|
-
const res = await fetch(`${API_BASE_URL}/v2/makers/create`, {
|
|
832
|
-
method: "POST",
|
|
833
|
-
headers: { "Content-Type": "application/json" },
|
|
834
|
-
body: JSON.stringify({ processorName, ...payload }),
|
|
835
|
-
signal: controller.signal
|
|
836
|
-
});
|
|
837
|
-
if (!res.ok) {
|
|
838
|
-
const txt = await res.text().catch(() => "");
|
|
839
|
-
let detail = txt || res.statusText;
|
|
840
|
-
try {
|
|
841
|
-
const parsed = JSON.parse(txt);
|
|
842
|
-
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
843
|
-
detail = parsed.message;
|
|
844
|
-
}
|
|
845
|
-
} catch {
|
|
846
|
-
}
|
|
847
|
-
throw new MakersCreateError(
|
|
848
|
-
`makers/create failed (${res.status}): ${detail}`,
|
|
849
|
-
res.status,
|
|
850
|
-
txt
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
|
-
const json = await res.json();
|
|
854
|
-
if (!json.success || !json.responseObject?.hashedOnchainId) {
|
|
855
|
-
throw new MakersCreateError(
|
|
856
|
-
json.message || "makers/create returned no hashedOnchainId",
|
|
857
|
-
json.statusCode ?? null,
|
|
858
|
-
""
|
|
859
|
-
);
|
|
860
|
-
}
|
|
861
|
-
return json.responseObject.hashedOnchainId;
|
|
862
|
-
} finally {
|
|
863
|
-
clearTimeout(timeout);
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
async function validatePayeeDetails(processorName, offchainId) {
|
|
941
|
+
async function validatePayeeDetails(processorName, offchainId, apiBaseUrl = API_BASE_URL) {
|
|
867
942
|
const controller = new AbortController();
|
|
868
943
|
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
869
944
|
try {
|
|
870
|
-
const res = await fetch(`${
|
|
945
|
+
const res = await fetch(`${apiBaseUrl}/v2/makers/validate`, {
|
|
871
946
|
method: "POST",
|
|
872
947
|
headers: { "Content-Type": "application/json" },
|
|
873
948
|
body: JSON.stringify({ processorName, offchainId }),
|
|
@@ -945,10 +1020,11 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
945
1020
|
return null;
|
|
946
1021
|
}
|
|
947
1022
|
}
|
|
948
|
-
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1023
|
+
async function offramp(walletClient, params, onProgress, telemetryContext, apiBaseUrl) {
|
|
949
1024
|
const { amount, platform, currency, identifier } = params;
|
|
950
1025
|
const platformId = platform.id;
|
|
951
1026
|
const currencyCode = currency.code;
|
|
1027
|
+
const resolvedApiBaseUrl = resolveApiBaseUrl(apiBaseUrl);
|
|
952
1028
|
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
953
1029
|
sdkVersion: SDK_VERSION,
|
|
954
1030
|
telemetry: true,
|
|
@@ -993,7 +1069,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
993
1069
|
const existing = await findUndelegatedDeposit(walletAddress);
|
|
994
1070
|
if (existing) {
|
|
995
1071
|
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
996
|
-
const client2 = createSdkClient(walletClient);
|
|
1072
|
+
const client2 = createSdkClient(walletClient, resolvedApiBaseUrl);
|
|
997
1073
|
const txOverrides2 = { referrer: [REFERRER] };
|
|
998
1074
|
try {
|
|
999
1075
|
const result2 = await client2.setRateManager({
|
|
@@ -1029,7 +1105,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1029
1105
|
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1030
1106
|
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1031
1107
|
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1032
|
-
const maxUnits = usdcToUnits(String(Math.min(amt,
|
|
1108
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, MAX_ORDER_USDC)));
|
|
1033
1109
|
try {
|
|
1034
1110
|
const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains3.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
|
|
1035
1111
|
const balance = await publicClient.readContract({
|
|
@@ -1058,7 +1134,11 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1058
1134
|
}
|
|
1059
1135
|
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1060
1136
|
if (!getPeerExtensionRegistrationInfo(platformId)) {
|
|
1061
|
-
const outcome = await validatePayeeDetails(
|
|
1137
|
+
const outcome = await validatePayeeDetails(
|
|
1138
|
+
canonicalName,
|
|
1139
|
+
normalizedIdentifier,
|
|
1140
|
+
resolvedApiBaseUrl
|
|
1141
|
+
);
|
|
1062
1142
|
if (outcome === "invalid") {
|
|
1063
1143
|
throw new OfframpError(
|
|
1064
1144
|
`Couldn't verify that ${platform.name} ${platform.identifier.label.toLowerCase()}. Double-check it and try again.`,
|
|
@@ -1067,7 +1147,7 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1067
1147
|
);
|
|
1068
1148
|
}
|
|
1069
1149
|
}
|
|
1070
|
-
const client = createSdkClient(walletClient);
|
|
1150
|
+
const client = createSdkClient(walletClient, resolvedApiBaseUrl);
|
|
1071
1151
|
const txOverrides = { referrer: [REFERRER] };
|
|
1072
1152
|
emitProgress({ step: "approving" });
|
|
1073
1153
|
try {
|
|
@@ -1093,13 +1173,13 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1093
1173
|
let hashedOnchainId;
|
|
1094
1174
|
try {
|
|
1095
1175
|
const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
|
|
1096
|
-
hashedOnchainId = await
|
|
1176
|
+
hashedOnchainId = await postMakerCreate(canonicalName, payeePayload, resolvedApiBaseUrl);
|
|
1097
1177
|
} catch (err) {
|
|
1098
1178
|
const status = err instanceof MakersCreateError ? err.status : null;
|
|
1099
1179
|
const message = err instanceof Error ? err.message : String(err);
|
|
1100
1180
|
if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
|
|
1101
1181
|
throw new OfframpError(
|
|
1102
|
-
`${platform.name} maker is not yet registered in the Peer extension.
|
|
1182
|
+
`${platform.name} maker is not yet registered in the Peer extension. Register this handle with Peer, then retry.`,
|
|
1103
1183
|
"EXTENSION_REGISTRATION_REQUIRED",
|
|
1104
1184
|
"registering",
|
|
1105
1185
|
err
|
|
@@ -1273,6 +1353,14 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
|
1273
1353
|
// src/offramp.ts
|
|
1274
1354
|
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1275
1355
|
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1356
|
+
var warnedIdempotencyUnavailable = false;
|
|
1357
|
+
function warnIdempotencyUnavailableOnce() {
|
|
1358
|
+
if (warnedIdempotencyUnavailable) return;
|
|
1359
|
+
warnedIdempotencyUnavailable = true;
|
|
1360
|
+
console.warn(
|
|
1361
|
+
"[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."
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1276
1364
|
function getWalletAddress(walletClient) {
|
|
1277
1365
|
const address = walletClient.account?.address;
|
|
1278
1366
|
if (!address) {
|
|
@@ -1343,8 +1431,10 @@ function buildCurrencyGroups() {
|
|
|
1343
1431
|
var Offramp = class {
|
|
1344
1432
|
walletClient;
|
|
1345
1433
|
telemetry;
|
|
1434
|
+
apiBaseUrl;
|
|
1346
1435
|
constructor(options) {
|
|
1347
1436
|
this.walletClient = options.walletClient;
|
|
1437
|
+
this.apiBaseUrl = options.apiBaseUrl;
|
|
1348
1438
|
this.telemetry = createTelemetryContext({
|
|
1349
1439
|
sdkVersion: SDK_VERSION,
|
|
1350
1440
|
telemetry: options.telemetry,
|
|
@@ -1367,6 +1457,9 @@ var Offramp = class {
|
|
|
1367
1457
|
referralId: flowTelemetry.referralId
|
|
1368
1458
|
};
|
|
1369
1459
|
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1460
|
+
if (sanitizedKey && typeof sessionStorage === "undefined") {
|
|
1461
|
+
warnIdempotencyUnavailableOnce();
|
|
1462
|
+
}
|
|
1370
1463
|
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1371
1464
|
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1372
1465
|
if (storageKey) {
|
|
@@ -1377,7 +1470,8 @@ var Offramp = class {
|
|
|
1377
1470
|
this.walletClient,
|
|
1378
1471
|
sanitizedParams,
|
|
1379
1472
|
onProgress,
|
|
1380
|
-
flowTelemetry
|
|
1473
|
+
flowTelemetry,
|
|
1474
|
+
this.apiBaseUrl
|
|
1381
1475
|
);
|
|
1382
1476
|
if (storageKey) {
|
|
1383
1477
|
writeIdempotencyResult(storageKey, result);
|
|
@@ -1508,25 +1602,85 @@ var import_react2 = require("react");
|
|
|
1508
1602
|
|
|
1509
1603
|
// src/extension.ts
|
|
1510
1604
|
var import_sdk7 = require("@zkp2p/sdk");
|
|
1605
|
+
var resolveWindow = (options) => {
|
|
1606
|
+
if (options?.window) {
|
|
1607
|
+
return options.window;
|
|
1608
|
+
}
|
|
1609
|
+
if (typeof window === "undefined") {
|
|
1610
|
+
return void 0;
|
|
1611
|
+
}
|
|
1612
|
+
return window;
|
|
1613
|
+
};
|
|
1614
|
+
var requirePeer = (options) => {
|
|
1615
|
+
const resolvedWindow = resolveWindow(options);
|
|
1616
|
+
if (!resolvedWindow) {
|
|
1617
|
+
throw new Error("Peer extension SDK requires a browser window.");
|
|
1618
|
+
}
|
|
1619
|
+
if (!resolvedWindow.peer) {
|
|
1620
|
+
throw new Error("Peer extension not available. Install or enable the Peer extension.");
|
|
1621
|
+
}
|
|
1622
|
+
return resolvedWindow.peer;
|
|
1623
|
+
};
|
|
1624
|
+
var isPeerExtensionAvailable = (options) => {
|
|
1625
|
+
const resolvedWindow = resolveWindow(options);
|
|
1626
|
+
return Boolean(resolvedWindow?.peer);
|
|
1627
|
+
};
|
|
1628
|
+
var hasHeadlessMetadataBridge = (peer) => typeof peer.authenticate === "function" && typeof peer.onMetadataMessage === "function";
|
|
1629
|
+
var requirePeerMetadataBridge = (options) => {
|
|
1630
|
+
const peer = requirePeer(options);
|
|
1631
|
+
if (!hasHeadlessMetadataBridge(peer)) {
|
|
1632
|
+
throw new Error("Peer extension metadata bridge unavailable. Install or update Peer.");
|
|
1633
|
+
}
|
|
1634
|
+
return peer;
|
|
1635
|
+
};
|
|
1636
|
+
var isPeerExtensionMetadataBridgeAvailable = (options) => {
|
|
1637
|
+
const resolvedWindow = resolveWindow(options);
|
|
1638
|
+
return Boolean(resolvedWindow?.peer && hasHeadlessMetadataBridge(resolvedWindow.peer));
|
|
1639
|
+
};
|
|
1640
|
+
var createPeerExtensionSdk = (options = {}) => {
|
|
1641
|
+
const sdk = (0, import_sdk7.createPeerExtensionSdk)(options);
|
|
1642
|
+
return {
|
|
1643
|
+
isAvailable: () => isPeerExtensionAvailable(options),
|
|
1644
|
+
requestConnection: () => requirePeer(options).requestConnection(),
|
|
1645
|
+
checkConnectionStatus: () => requirePeer(options).checkConnectionStatus(),
|
|
1646
|
+
getVersion: () => requirePeer(options).getVersion(),
|
|
1647
|
+
authenticate: (params) => requirePeerMetadataBridge(options).authenticate(params),
|
|
1648
|
+
onMetadataMessage: (callback) => requirePeerMetadataBridge(options).onMetadataMessage(callback),
|
|
1649
|
+
openInstallPage: () => sdk.openInstallPage(),
|
|
1650
|
+
getState: () => sdk.getState()
|
|
1651
|
+
};
|
|
1652
|
+
};
|
|
1653
|
+
var peerExtensionSdk = createPeerExtensionSdk();
|
|
1511
1654
|
|
|
1512
1655
|
// src/hooks/usePeerExtensionRegistration.ts
|
|
1513
1656
|
function usePeerExtensionRegistration(platform) {
|
|
1514
|
-
const
|
|
1657
|
+
const platformId = platform?.id ?? null;
|
|
1658
|
+
const info = platformId ? getPeerExtensionRegistrationInfo(platformId) : null;
|
|
1659
|
+
const authParams = (0, import_react2.useMemo)(
|
|
1660
|
+
() => platformId ? getPeerExtensionRegistrationAuthParams(platformId) : null,
|
|
1661
|
+
[platformId]
|
|
1662
|
+
);
|
|
1515
1663
|
const [phase, setPhase] = (0, import_react2.useState)(
|
|
1516
1664
|
info ? "checking" : "unsupported"
|
|
1517
1665
|
);
|
|
1518
1666
|
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
1519
1667
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
1668
|
+
const [capturedMetadata, setCapturedMetadata] = (0, import_react2.useState)(null);
|
|
1520
1669
|
const mountedRef = (0, import_react2.useRef)(true);
|
|
1521
1670
|
const providerId = info?.providerId ?? null;
|
|
1671
|
+
const metadataPlatform = authParams?.platform ?? null;
|
|
1522
1672
|
const refresh = (0, import_react2.useCallback)(async () => {
|
|
1523
1673
|
if (!providerId) {
|
|
1524
1674
|
setPhase("unsupported");
|
|
1525
1675
|
return;
|
|
1526
1676
|
}
|
|
1527
1677
|
try {
|
|
1528
|
-
const state = await
|
|
1678
|
+
const state = await peerExtensionSdk.getState();
|
|
1529
1679
|
if (!mountedRef.current) return;
|
|
1680
|
+
if (state === "ready" && !isPeerExtensionMetadataBridgeAvailable()) {
|
|
1681
|
+
setPhase("needs_install");
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1530
1684
|
setPhase(mapSdkState(state));
|
|
1531
1685
|
} catch {
|
|
1532
1686
|
if (!mountedRef.current) return;
|
|
@@ -1537,6 +1691,7 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1537
1691
|
mountedRef.current = true;
|
|
1538
1692
|
setError(null);
|
|
1539
1693
|
setBusy(false);
|
|
1694
|
+
setCapturedMetadata(null);
|
|
1540
1695
|
if (!providerId) {
|
|
1541
1696
|
setPhase("unsupported");
|
|
1542
1697
|
} else {
|
|
@@ -1547,10 +1702,25 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1547
1702
|
mountedRef.current = false;
|
|
1548
1703
|
};
|
|
1549
1704
|
}, [providerId, refresh]);
|
|
1705
|
+
(0, import_react2.useEffect)(() => {
|
|
1706
|
+
setCapturedMetadata(null);
|
|
1707
|
+
if (!metadataPlatform) return;
|
|
1708
|
+
try {
|
|
1709
|
+
return peerExtensionSdk.onMetadataMessage((message) => {
|
|
1710
|
+
if (!mountedRef.current || message.platform !== metadataPlatform) return;
|
|
1711
|
+
setCapturedMetadata(message);
|
|
1712
|
+
if (message.errorMessage) {
|
|
1713
|
+
setError(message.errorMessage);
|
|
1714
|
+
}
|
|
1715
|
+
});
|
|
1716
|
+
} catch {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
}, [metadataPlatform, phase]);
|
|
1550
1720
|
const installExtension = (0, import_react2.useCallback)(() => {
|
|
1551
1721
|
setError(null);
|
|
1552
1722
|
try {
|
|
1553
|
-
|
|
1723
|
+
peerExtensionSdk.openInstallPage();
|
|
1554
1724
|
} catch {
|
|
1555
1725
|
if (typeof window !== "undefined") {
|
|
1556
1726
|
window.open(import_sdk7.PEER_EXTENSION_CHROME_URL, "_blank", "noopener,noreferrer");
|
|
@@ -1558,15 +1728,14 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1558
1728
|
}
|
|
1559
1729
|
}, []);
|
|
1560
1730
|
const connectExtension = (0, import_react2.useCallback)(async () => {
|
|
1561
|
-
if (!
|
|
1731
|
+
if (!authParams) return false;
|
|
1562
1732
|
setError(null);
|
|
1563
1733
|
setBusy(true);
|
|
1564
1734
|
try {
|
|
1565
|
-
const approved = await
|
|
1735
|
+
const approved = await peerExtensionSdk.requestConnection();
|
|
1566
1736
|
if (!mountedRef.current) return approved;
|
|
1567
1737
|
if (approved) {
|
|
1568
|
-
setPhase("ready");
|
|
1569
|
-
import_sdk7.peerExtensionSdk.openSidebar(`/verify/${providerId}`);
|
|
1738
|
+
setPhase(isPeerExtensionMetadataBridgeAvailable() ? "ready" : "needs_install");
|
|
1570
1739
|
} else {
|
|
1571
1740
|
setError("Connection was not approved. Try again once you've approved Peer.");
|
|
1572
1741
|
}
|
|
@@ -1581,40 +1750,89 @@ function usePeerExtensionRegistration(platform) {
|
|
|
1581
1750
|
} finally {
|
|
1582
1751
|
if (mountedRef.current) setBusy(false);
|
|
1583
1752
|
}
|
|
1584
|
-
}, [
|
|
1585
|
-
const
|
|
1586
|
-
if (!
|
|
1753
|
+
}, [authParams, refresh]);
|
|
1754
|
+
const startRegistrationCapture = (0, import_react2.useCallback)(async () => {
|
|
1755
|
+
if (!authParams) return;
|
|
1587
1756
|
setError(null);
|
|
1588
|
-
|
|
1589
|
-
if (!mountedRef.current) return;
|
|
1590
|
-
const nextPhase = mapSdkState(state);
|
|
1591
|
-
setPhase(nextPhase);
|
|
1592
|
-
if (nextPhase === "needs_install") {
|
|
1593
|
-
installExtension();
|
|
1594
|
-
return;
|
|
1595
|
-
}
|
|
1596
|
-
if (nextPhase === "needs_connection") {
|
|
1597
|
-
await connectExtension();
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1757
|
+
setBusy(true);
|
|
1600
1758
|
try {
|
|
1601
|
-
|
|
1759
|
+
const state = await peerExtensionSdk.getState().catch(() => "needs_install");
|
|
1760
|
+
if (!mountedRef.current) return;
|
|
1761
|
+
const nextPhase = state === "ready" && !isPeerExtensionMetadataBridgeAvailable() ? "needs_install" : mapSdkState(state);
|
|
1762
|
+
setPhase(nextPhase);
|
|
1763
|
+
if (nextPhase === "needs_install") {
|
|
1764
|
+
installExtension();
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
if (nextPhase === "needs_connection") {
|
|
1768
|
+
const approved = await peerExtensionSdk.requestConnection();
|
|
1769
|
+
if (!mountedRef.current) return;
|
|
1770
|
+
if (!approved) {
|
|
1771
|
+
setError("Connection was not approved. Try again once you've approved Peer.");
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
if (!isPeerExtensionMetadataBridgeAvailable()) {
|
|
1775
|
+
setPhase("needs_install");
|
|
1776
|
+
installExtension();
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
setPhase("ready");
|
|
1780
|
+
}
|
|
1781
|
+
setCapturedMetadata(null);
|
|
1782
|
+
peerExtensionSdk.authenticate(authParams);
|
|
1602
1783
|
} catch (err) {
|
|
1603
1784
|
setError(
|
|
1604
|
-
err instanceof Error ? err.message : "Couldn't
|
|
1785
|
+
err instanceof Error ? err.message : "Couldn't start Peer extension registration. Make sure Peer is enabled."
|
|
1605
1786
|
);
|
|
1606
1787
|
refresh();
|
|
1788
|
+
} finally {
|
|
1789
|
+
if (mountedRef.current) setBusy(false);
|
|
1607
1790
|
}
|
|
1608
|
-
}, [
|
|
1791
|
+
}, [authParams, installExtension, refresh]);
|
|
1792
|
+
const openVerifySidebar = (0, import_react2.useCallback)(
|
|
1793
|
+
async () => startRegistrationCapture(),
|
|
1794
|
+
[startRegistrationCapture]
|
|
1795
|
+
);
|
|
1796
|
+
const completeRegistration = (0, import_react2.useCallback)(
|
|
1797
|
+
async (walletClient, params, options = {}) => {
|
|
1798
|
+
const metadata = options.capturedMetadata ?? capturedMetadata;
|
|
1799
|
+
if (!metadata) {
|
|
1800
|
+
const message = "No Peer metadata captured yet. Start registration first.";
|
|
1801
|
+
setError(message);
|
|
1802
|
+
throw new Error(message);
|
|
1803
|
+
}
|
|
1804
|
+
setError(null);
|
|
1805
|
+
setBusy(true);
|
|
1806
|
+
try {
|
|
1807
|
+
await completePeerExtensionRegistration({
|
|
1808
|
+
platform: params.platform,
|
|
1809
|
+
identifier: params.identifier,
|
|
1810
|
+
capturedMetadata: metadata
|
|
1811
|
+
});
|
|
1812
|
+
return await offramp(walletClient, params, options.onProgress);
|
|
1813
|
+
} catch (err) {
|
|
1814
|
+
setError(
|
|
1815
|
+
err instanceof Error ? err.message : "Couldn't complete Peer extension registration. Try capturing metadata again."
|
|
1816
|
+
);
|
|
1817
|
+
throw err;
|
|
1818
|
+
} finally {
|
|
1819
|
+
if (mountedRef.current) setBusy(false);
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
[capturedMetadata]
|
|
1823
|
+
);
|
|
1609
1824
|
return {
|
|
1610
1825
|
phase,
|
|
1611
1826
|
info,
|
|
1612
1827
|
busy,
|
|
1613
1828
|
error,
|
|
1829
|
+
capturedMetadata,
|
|
1614
1830
|
refresh,
|
|
1615
1831
|
installExtension,
|
|
1616
1832
|
connectExtension,
|
|
1617
|
-
|
|
1833
|
+
startRegistrationCapture,
|
|
1834
|
+
openVerifySidebar,
|
|
1835
|
+
completeRegistration
|
|
1618
1836
|
};
|
|
1619
1837
|
}
|
|
1620
1838
|
function mapSdkState(state) {
|