@usdctofiat/offramp 1.1.4 → 2.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 +252 -0
- package/README.md +82 -2
- package/dist/{chunk-OJY6DE3I.js → chunk-HBR3Z6HX.js} +668 -248
- package/dist/chunk-HBR3Z6HX.js.map +1 -0
- package/dist/errors-Deoi_xYZ.d.cts +236 -0
- package/dist/errors-Deoi_xYZ.d.ts +236 -0
- package/dist/index.cjs +681 -248
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -5
- package/dist/index.d.ts +38 -5
- package/dist/index.js +55 -18
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +808 -277
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +90 -3
- package/dist/react.d.ts +90 -3
- package/dist/react.js +152 -14
- package/dist/react.js.map +1 -1
- package/package.json +18 -7
- package/dist/chunk-OJY6DE3I.js.map +0 -1
- package/dist/errors-Dtzrl98J.d.cts +0 -105
- package/dist/errors-Dtzrl98J.d.ts +0 -105
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/config.ts
|
|
2
2
|
import { getContracts, getGatingServiceAddress } from "@zkp2p/sdk";
|
|
3
|
+
var SDK_VERSION = "1.2.0";
|
|
3
4
|
var BASE_CHAIN_ID = 8453;
|
|
4
5
|
var RUNTIME_ENV = "production";
|
|
5
6
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
@@ -15,6 +16,7 @@ var GATING_SERVICE_ADDRESS = getGatingServiceAddress(
|
|
|
15
16
|
);
|
|
16
17
|
var DELEGATE_RATE_MANAGER_ID = "0x8666d6fb0f6797c56e95339fd7ca82fdd348b9db200e10a4c4aa0a0b879fc41c";
|
|
17
18
|
var RATE_MANAGER_REGISTRY_ADDRESS = "0xeed7db23e724ac4590d6db6f78fda6db203535f3";
|
|
19
|
+
var DELEGATE_MANAGER_FEE_BPS = 10;
|
|
18
20
|
var REFERRER = "galleonlabs";
|
|
19
21
|
var MIN_DEPOSIT_USDC = 1;
|
|
20
22
|
var MIN_ORDER_USDC = 1;
|
|
@@ -120,6 +122,17 @@ function resolveSupportedCurrencies(platform) {
|
|
|
120
122
|
}
|
|
121
123
|
return Array.from(codes).sort();
|
|
122
124
|
}
|
|
125
|
+
function normalizePaypalMeUsername(value) {
|
|
126
|
+
const trimmed = value.trim();
|
|
127
|
+
if (!trimmed) return "";
|
|
128
|
+
const withoutProtocol = trimmed.replace(/^https?:\/\//i, "").replace(/^www\./i, "");
|
|
129
|
+
if (/^paypal\.me(?:[?#].*)?$/i.test(withoutProtocol)) return "";
|
|
130
|
+
const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
|
|
131
|
+
const [pathWithoutQuery] = withoutDomain.split(/[?#]/, 1);
|
|
132
|
+
const sanitizedPath = pathWithoutQuery.replace(/^\/+/, "");
|
|
133
|
+
const [username] = sanitizedPath.split("/", 1);
|
|
134
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
135
|
+
}
|
|
123
136
|
var BLUEPRINTS = {
|
|
124
137
|
venmo: {
|
|
125
138
|
id: "venmo",
|
|
@@ -167,7 +180,13 @@ var BLUEPRINTS = {
|
|
|
167
180
|
placeholder: "wisetag (no @)",
|
|
168
181
|
helperText: "Your Wise @wisetag (no @)",
|
|
169
182
|
validation: z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
|
|
170
|
-
transform: (v) => v.replace(/^@+/, "").trim()
|
|
183
|
+
transform: (v) => v.replace(/^@+/, "").trim(),
|
|
184
|
+
extensionRegistration: {
|
|
185
|
+
providerId: "wise",
|
|
186
|
+
requiredPrompt: "This Wisetag is not registered yet. Verify it in the Peer extension, then refresh and retry with the same Wisetag.",
|
|
187
|
+
ctaLabel: "Install Peer Extension",
|
|
188
|
+
minExtensionVersion: "0.4.12"
|
|
189
|
+
}
|
|
171
190
|
},
|
|
172
191
|
mercadopago: {
|
|
173
192
|
id: "mercadopago",
|
|
@@ -188,10 +207,18 @@ var BLUEPRINTS = {
|
|
|
188
207
|
paypal: {
|
|
189
208
|
id: "paypal",
|
|
190
209
|
name: "PayPal",
|
|
191
|
-
identifierLabel: "
|
|
192
|
-
placeholder: "
|
|
193
|
-
helperText: "
|
|
194
|
-
validation: z.string().
|
|
210
|
+
identifierLabel: "PayPal.me Username",
|
|
211
|
+
placeholder: "paypal.me/your-username",
|
|
212
|
+
helperText: "Your PayPal.me username, not your email",
|
|
213
|
+
validation: z.string().min(1, "PayPal.me username is required").regex(/^[a-z0-9._-]+$/i, "Use your PayPal.me username (letters, numbers, . _ -)"),
|
|
214
|
+
transform: normalizePaypalMeUsername,
|
|
215
|
+
extensionRegistration: {
|
|
216
|
+
providerId: "paypal",
|
|
217
|
+
requiredPrompt: "This PayPal username is not registered yet. Verify it in the Peer extension, then refresh and retry with the same PayPal username.",
|
|
218
|
+
ctaLabel: "Install Peer Extension",
|
|
219
|
+
ctaSubtext: "PayPal Business accounts are not supported at this time.",
|
|
220
|
+
minExtensionVersion: "0.4.14"
|
|
221
|
+
}
|
|
195
222
|
},
|
|
196
223
|
monzo: {
|
|
197
224
|
id: "monzo",
|
|
@@ -222,6 +249,7 @@ function buildPlatformEntry(bp) {
|
|
|
222
249
|
placeholder: bp.placeholder,
|
|
223
250
|
help: bp.helperText
|
|
224
251
|
},
|
|
252
|
+
...bp.extensionRegistration ? { extensionRegistration: bp.extensionRegistration } : {},
|
|
225
253
|
validate(input) {
|
|
226
254
|
const transformed = bp.transform ? bp.transform(input) : input;
|
|
227
255
|
const result = bp.validation.safeParse(transformed);
|
|
@@ -248,6 +276,9 @@ var PLATFORMS = {
|
|
|
248
276
|
MONZO: buildPlatformEntry(BLUEPRINTS.monzo),
|
|
249
277
|
N26: buildPlatformEntry(BLUEPRINTS.n26)
|
|
250
278
|
};
|
|
279
|
+
function getPlatformById(id) {
|
|
280
|
+
return Object.values(PLATFORMS).find((p) => p.id === id) ?? null;
|
|
281
|
+
}
|
|
251
282
|
function normalizePaymentMethodLookupName(platform) {
|
|
252
283
|
const normalized = platform.trim().toLowerCase();
|
|
253
284
|
if (!normalized) return null;
|
|
@@ -291,34 +322,37 @@ function getPaymentMethodHashes(platform) {
|
|
|
291
322
|
}
|
|
292
323
|
return Array.from(hashes);
|
|
293
324
|
}
|
|
294
|
-
function buildDepositData(platform, identifier) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
case "monzo":
|
|
313
|
-
return { monzoMeUsername: identifier, telegramUsername: "" };
|
|
314
|
-
case "n26":
|
|
315
|
-
return { iban: identifier, telegramUsername: "" };
|
|
316
|
-
default:
|
|
317
|
-
return { identifier, telegramUsername: "" };
|
|
318
|
-
}
|
|
325
|
+
function buildDepositData(platform, identifier, telegramUsername = "") {
|
|
326
|
+
const offchainId = platform === "chime" ? identifier.toLowerCase() : identifier;
|
|
327
|
+
return telegramUsername ? { offchainId, telegramUsername } : { offchainId };
|
|
328
|
+
}
|
|
329
|
+
function getPeerExtensionRegistrationInfo(platform) {
|
|
330
|
+
const entry = getPlatformById(platform);
|
|
331
|
+
return entry?.extensionRegistration ?? null;
|
|
332
|
+
}
|
|
333
|
+
var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
|
|
334
|
+
function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
335
|
+
const info = getPeerExtensionRegistrationInfo(platform);
|
|
336
|
+
if (!info) return false;
|
|
337
|
+
const normalized = (message ?? "").trim();
|
|
338
|
+
if (normalized === info.requiredPrompt) return true;
|
|
339
|
+
if (statusCode === 400) return true;
|
|
340
|
+
if (!normalized) return false;
|
|
341
|
+
const lower = normalized.toLowerCase();
|
|
342
|
+
return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
|
|
319
343
|
}
|
|
320
344
|
|
|
321
345
|
// src/errors.ts
|
|
346
|
+
var MakersCreateError = class extends Error {
|
|
347
|
+
status;
|
|
348
|
+
body;
|
|
349
|
+
constructor(message, status, body) {
|
|
350
|
+
super(message);
|
|
351
|
+
this.name = "MakersCreateError";
|
|
352
|
+
this.status = status;
|
|
353
|
+
this.body = body;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
322
356
|
var OfframpError = class extends Error {
|
|
323
357
|
code;
|
|
324
358
|
step;
|
|
@@ -967,6 +1001,108 @@ async function undelegate(walletClient, depositId, escrowAddress) {
|
|
|
967
1001
|
return hash;
|
|
968
1002
|
}
|
|
969
1003
|
|
|
1004
|
+
// src/telemetry.ts
|
|
1005
|
+
var SDK_EVENTS_ENDPOINT = "https://usdctofiat.xyz/api/sdk-events";
|
|
1006
|
+
var MAX_BATCH_SIZE = 50;
|
|
1007
|
+
var FLUSH_DELAY_MS = 180;
|
|
1008
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
1009
|
+
var queuedEvents = [];
|
|
1010
|
+
var flushTimer = null;
|
|
1011
|
+
var flushInFlight = false;
|
|
1012
|
+
function sanitizeAttributionId(raw) {
|
|
1013
|
+
if (typeof raw !== "string") return void 0;
|
|
1014
|
+
const stripped = raw.trim().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1015
|
+
if (!stripped) return void 0;
|
|
1016
|
+
if (stripped.length > 64) return void 0;
|
|
1017
|
+
return stripped;
|
|
1018
|
+
}
|
|
1019
|
+
function scheduleFlush() {
|
|
1020
|
+
if (flushTimer != null) return;
|
|
1021
|
+
flushTimer = setTimeout(() => {
|
|
1022
|
+
flushTimer = null;
|
|
1023
|
+
void flushQueue();
|
|
1024
|
+
}, FLUSH_DELAY_MS);
|
|
1025
|
+
}
|
|
1026
|
+
async function postBatch(batch) {
|
|
1027
|
+
if (!batch.length) return;
|
|
1028
|
+
const body = JSON.stringify(batch);
|
|
1029
|
+
try {
|
|
1030
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
1031
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
1032
|
+
const accepted = navigator.sendBeacon(SDK_EVENTS_ENDPOINT, blob);
|
|
1033
|
+
if (accepted) return;
|
|
1034
|
+
}
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
1039
|
+
const timeout = controller ? setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS) : null;
|
|
1040
|
+
await fetch(SDK_EVENTS_ENDPOINT, {
|
|
1041
|
+
method: "POST",
|
|
1042
|
+
headers: { "Content-Type": "application/json" },
|
|
1043
|
+
body,
|
|
1044
|
+
keepalive: true,
|
|
1045
|
+
signal: controller?.signal
|
|
1046
|
+
}).catch(() => {
|
|
1047
|
+
});
|
|
1048
|
+
if (timeout) clearTimeout(timeout);
|
|
1049
|
+
} catch {
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
async function flushQueue() {
|
|
1053
|
+
if (flushInFlight) return;
|
|
1054
|
+
if (!queuedEvents.length) return;
|
|
1055
|
+
flushInFlight = true;
|
|
1056
|
+
try {
|
|
1057
|
+
while (queuedEvents.length > 0) {
|
|
1058
|
+
const batch = queuedEvents.slice(0, MAX_BATCH_SIZE);
|
|
1059
|
+
queuedEvents = queuedEvents.slice(batch.length);
|
|
1060
|
+
await postBatch(batch);
|
|
1061
|
+
}
|
|
1062
|
+
} finally {
|
|
1063
|
+
flushInFlight = false;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
function emitEvent(name, payload) {
|
|
1067
|
+
try {
|
|
1068
|
+
queuedEvents.push({
|
|
1069
|
+
name,
|
|
1070
|
+
payload,
|
|
1071
|
+
ts: Date.now()
|
|
1072
|
+
});
|
|
1073
|
+
if (queuedEvents.length >= MAX_BATCH_SIZE) {
|
|
1074
|
+
void flushQueue();
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
scheduleFlush();
|
|
1078
|
+
} catch {
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function sendTelemetryEvent(name, payload) {
|
|
1082
|
+
emitEvent(name, payload);
|
|
1083
|
+
}
|
|
1084
|
+
function createTelemetryContext(options) {
|
|
1085
|
+
const integratorId = sanitizeAttributionId(options.integratorId);
|
|
1086
|
+
const referralId = sanitizeAttributionId(options.referralId);
|
|
1087
|
+
const enabled = options.telemetry !== false;
|
|
1088
|
+
const sdkVersion = options.sdkVersion;
|
|
1089
|
+
return {
|
|
1090
|
+
enabled,
|
|
1091
|
+
sdkVersion,
|
|
1092
|
+
integratorId,
|
|
1093
|
+
referralId,
|
|
1094
|
+
emit(name, payload = {}) {
|
|
1095
|
+
if (!enabled) return;
|
|
1096
|
+
emitEvent(name, {
|
|
1097
|
+
sdkVersion,
|
|
1098
|
+
integratorId,
|
|
1099
|
+
referralId,
|
|
1100
|
+
...payload
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
970
1106
|
// src/deposit.ts
|
|
971
1107
|
import { createPublicClient as createPublicClient3, decodeEventLog, formatUnits as formatUnits2, http as http3, parseUnits } from "viem";
|
|
972
1108
|
import { base as base3 } from "viem/chains";
|
|
@@ -1018,23 +1154,39 @@ function extractDepositIdFromLogs(logs) {
|
|
|
1018
1154
|
}
|
|
1019
1155
|
return null;
|
|
1020
1156
|
}
|
|
1021
|
-
async function registerPayeeDetails(processorName,
|
|
1157
|
+
async function registerPayeeDetails(processorName, payload) {
|
|
1022
1158
|
const controller = new AbortController();
|
|
1023
1159
|
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
1024
1160
|
try {
|
|
1025
|
-
const res = await fetch(`${API_BASE_URL}/
|
|
1161
|
+
const res = await fetch(`${API_BASE_URL}/v2/makers/create`, {
|
|
1026
1162
|
method: "POST",
|
|
1027
1163
|
headers: { "Content-Type": "application/json" },
|
|
1028
|
-
body: JSON.stringify({ processorName,
|
|
1164
|
+
body: JSON.stringify({ processorName, ...payload }),
|
|
1029
1165
|
signal: controller.signal
|
|
1030
1166
|
});
|
|
1031
1167
|
if (!res.ok) {
|
|
1032
1168
|
const txt = await res.text().catch(() => "");
|
|
1033
|
-
|
|
1169
|
+
let detail = txt || res.statusText;
|
|
1170
|
+
try {
|
|
1171
|
+
const parsed = JSON.parse(txt);
|
|
1172
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
1173
|
+
detail = parsed.message;
|
|
1174
|
+
}
|
|
1175
|
+
} catch {
|
|
1176
|
+
}
|
|
1177
|
+
throw new MakersCreateError(
|
|
1178
|
+
`makers/create failed (${res.status}): ${detail}`,
|
|
1179
|
+
res.status,
|
|
1180
|
+
txt
|
|
1181
|
+
);
|
|
1034
1182
|
}
|
|
1035
1183
|
const json = await res.json();
|
|
1036
1184
|
if (!json.success || !json.responseObject?.hashedOnchainId) {
|
|
1037
|
-
throw new
|
|
1185
|
+
throw new MakersCreateError(
|
|
1186
|
+
json.message || "makers/create returned no hashedOnchainId",
|
|
1187
|
+
json.statusCode ?? null,
|
|
1188
|
+
""
|
|
1189
|
+
);
|
|
1038
1190
|
}
|
|
1039
1191
|
return json.responseObject.hashedOnchainId;
|
|
1040
1192
|
} finally {
|
|
@@ -1090,261 +1242,516 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1090
1242
|
if (remaining === 0n) return false;
|
|
1091
1243
|
return (d.rateManagerId ?? "").toLowerCase() !== delegateIdLower;
|
|
1092
1244
|
});
|
|
1093
|
-
undelegated.sort((a, b) =>
|
|
1245
|
+
undelegated.sort((a, b) => {
|
|
1246
|
+
const left = BigInt(a.depositId);
|
|
1247
|
+
const right = BigInt(b.depositId);
|
|
1248
|
+
if (left === right) return 0;
|
|
1249
|
+
return left > right ? -1 : 1;
|
|
1250
|
+
});
|
|
1094
1251
|
return undelegated[0] ?? null;
|
|
1095
1252
|
} catch {
|
|
1096
1253
|
return null;
|
|
1097
1254
|
}
|
|
1098
1255
|
}
|
|
1099
|
-
async function offramp(walletClient, params, onProgress) {
|
|
1256
|
+
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1100
1257
|
const { amount, platform, currency, identifier } = params;
|
|
1101
1258
|
const platformId = platform.id;
|
|
1102
1259
|
const currencyCode = currency.code;
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1260
|
+
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1261
|
+
sdkVersion: SDK_VERSION,
|
|
1262
|
+
telemetry: true,
|
|
1263
|
+
integratorId: params.integratorId,
|
|
1264
|
+
referralId: params.referralId
|
|
1265
|
+
});
|
|
1266
|
+
const flowStartMs = Date.now();
|
|
1267
|
+
const emitProgress = (progress) => {
|
|
1268
|
+
onProgress?.(progress);
|
|
1269
|
+
telemetry.emit("sdk.createDeposit.progress", {
|
|
1270
|
+
step: progress.step,
|
|
1271
|
+
txHash: progress.txHash
|
|
1272
|
+
});
|
|
1273
|
+
};
|
|
1274
|
+
try {
|
|
1275
|
+
if (!walletClient.account?.address) {
|
|
1276
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1277
|
+
}
|
|
1278
|
+
const walletAddress = walletClient.account.address.toLowerCase();
|
|
1279
|
+
telemetry.emit("sdk.createDeposit.start", {
|
|
1280
|
+
platform: platformId,
|
|
1281
|
+
currency: currencyCode,
|
|
1282
|
+
amount,
|
|
1283
|
+
wallet: walletAddress
|
|
1284
|
+
});
|
|
1285
|
+
const amt = parseFloat(amount);
|
|
1286
|
+
if (!Number.isFinite(amt) || amt < MIN_DEPOSIT_USDC) {
|
|
1287
|
+
throw new OfframpError(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC`, "VALIDATION");
|
|
1288
|
+
}
|
|
1289
|
+
if (!platform.currencies.includes(currencyCode)) {
|
|
1290
|
+
throw new OfframpError(`${currencyCode} is not supported on ${platform.name}`, "UNSUPPORTED");
|
|
1291
|
+
}
|
|
1292
|
+
const validation = platform.validate(identifier);
|
|
1293
|
+
if (!validation.valid) {
|
|
1294
|
+
throw new OfframpError(validation.error || "Invalid identifier", "VALIDATION");
|
|
1295
|
+
}
|
|
1296
|
+
const normalizedIdentifier = validation.normalized;
|
|
1297
|
+
const methodHash = getPaymentMethodHash(platformId);
|
|
1298
|
+
if (!methodHash) {
|
|
1299
|
+
throw new OfframpError(`${platform.name} is not currently supported`, "UNSUPPORTED");
|
|
1300
|
+
}
|
|
1301
|
+
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1302
|
+
if (existing) {
|
|
1303
|
+
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1304
|
+
const client2 = createSdkClient(walletClient);
|
|
1305
|
+
const txOverrides2 = { referrer: [REFERRER] };
|
|
1306
|
+
try {
|
|
1307
|
+
const result2 = await client2.setRateManager({
|
|
1308
|
+
depositId: BigInt(existing.depositId),
|
|
1309
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1310
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1311
|
+
escrowAddress: existing.escrowAddress,
|
|
1312
|
+
txOverrides: txOverrides2
|
|
1313
|
+
});
|
|
1314
|
+
const txHash = extractTxHash2(result2);
|
|
1315
|
+
emitProgress({ step: "done", txHash, depositId: existing.depositId });
|
|
1316
|
+
const resumedResult = { depositId: existing.depositId, txHash, resumed: true };
|
|
1317
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1318
|
+
depositId: resumedResult.depositId,
|
|
1319
|
+
txHash: resumedResult.txHash,
|
|
1320
|
+
wallet: walletAddress,
|
|
1321
|
+
resumed: true,
|
|
1322
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1323
|
+
});
|
|
1324
|
+
return resumedResult;
|
|
1325
|
+
} catch (err) {
|
|
1326
|
+
if (isUserCancellation(err))
|
|
1327
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
|
|
1328
|
+
throw new OfframpError(
|
|
1329
|
+
"Found undelegated deposit but delegation failed. Try again.",
|
|
1330
|
+
"DELEGATION_FAILED",
|
|
1331
|
+
"resuming",
|
|
1332
|
+
err,
|
|
1333
|
+
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1334
|
+
);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1338
|
+
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1339
|
+
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1340
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, 2500)));
|
|
1128
1341
|
try {
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1342
|
+
const publicClient = createPublicClient3({ chain: base3, transport: http3(BASE_RPC_URL) });
|
|
1343
|
+
const balance = await publicClient.readContract({
|
|
1344
|
+
address: USDC_ADDRESS,
|
|
1345
|
+
abi: [
|
|
1346
|
+
{
|
|
1347
|
+
name: "balanceOf",
|
|
1348
|
+
type: "function",
|
|
1349
|
+
stateMutability: "view",
|
|
1350
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1351
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1352
|
+
}
|
|
1353
|
+
],
|
|
1354
|
+
functionName: "balanceOf",
|
|
1355
|
+
args: [walletAddress]
|
|
1356
|
+
});
|
|
1357
|
+
if (balance < amountUnits) {
|
|
1358
|
+
const available = Number(formatUnits2(balance, 6));
|
|
1359
|
+
throw new OfframpError(
|
|
1360
|
+
`Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
|
|
1361
|
+
"VALIDATION"
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
} catch (err) {
|
|
1365
|
+
if (err instanceof OfframpError) throw err;
|
|
1366
|
+
}
|
|
1367
|
+
const client = createSdkClient(walletClient);
|
|
1368
|
+
const txOverrides = { referrer: [REFERRER] };
|
|
1369
|
+
emitProgress({ step: "approving" });
|
|
1370
|
+
try {
|
|
1371
|
+
await client.ensureAllowance({
|
|
1372
|
+
token: USDC_ADDRESS,
|
|
1373
|
+
amount: amountUnits,
|
|
1374
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1375
|
+
maxApprove: false,
|
|
1376
|
+
txOverrides
|
|
1135
1377
|
});
|
|
1136
|
-
const txHash = extractTxHash2(result);
|
|
1137
|
-
onProgress?.({ step: "done", txHash, depositId: existing.depositId });
|
|
1138
|
-
return { depositId: existing.depositId, txHash, resumed: true };
|
|
1139
1378
|
} catch (err) {
|
|
1140
1379
|
if (isUserCancellation(err))
|
|
1141
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "
|
|
1380
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1381
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1142
1382
|
throw new OfframpError(
|
|
1143
|
-
|
|
1144
|
-
"
|
|
1145
|
-
"
|
|
1146
|
-
err
|
|
1147
|
-
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1383
|
+
`USDC approval failed: ${detail}`,
|
|
1384
|
+
"APPROVAL_FAILED",
|
|
1385
|
+
"approving",
|
|
1386
|
+
err
|
|
1148
1387
|
);
|
|
1149
1388
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
name
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
],
|
|
1168
|
-
functionName: "balanceOf",
|
|
1169
|
-
args: [walletAddress]
|
|
1170
|
-
});
|
|
1171
|
-
if (balance < amountUnits) {
|
|
1172
|
-
const available = Number(formatUnits2(balance, 6));
|
|
1389
|
+
emitProgress({ step: "registering" });
|
|
1390
|
+
let hashedOnchainId;
|
|
1391
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1392
|
+
try {
|
|
1393
|
+
const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
|
|
1394
|
+
hashedOnchainId = await registerPayeeDetails(canonicalName, payeePayload);
|
|
1395
|
+
} catch (err) {
|
|
1396
|
+
const status = err instanceof MakersCreateError ? err.status : null;
|
|
1397
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1398
|
+
if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
|
|
1399
|
+
throw new OfframpError(
|
|
1400
|
+
`${platform.name} maker is not yet registered in the Peer extension. Verify this handle in the extension, then retry.`,
|
|
1401
|
+
"EXTENSION_REGISTRATION_REQUIRED",
|
|
1402
|
+
"registering",
|
|
1403
|
+
err
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1173
1406
|
throw new OfframpError(
|
|
1174
|
-
|
|
1175
|
-
"
|
|
1407
|
+
"Payee registration failed",
|
|
1408
|
+
"REGISTRATION_FAILED",
|
|
1409
|
+
"registering",
|
|
1410
|
+
err
|
|
1176
1411
|
);
|
|
1177
1412
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
await client.ensureAllowance({
|
|
1186
|
-
token: USDC_ADDRESS,
|
|
1187
|
-
amount: amountUnits,
|
|
1188
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1189
|
-
maxApprove: false,
|
|
1190
|
-
txOverrides
|
|
1191
|
-
});
|
|
1192
|
-
} catch (err) {
|
|
1193
|
-
if (isUserCancellation(err))
|
|
1194
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1195
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1196
|
-
throw new OfframpError(`USDC approval failed: ${detail}`, "APPROVAL_FAILED", "approving", err);
|
|
1197
|
-
}
|
|
1198
|
-
onProgress?.({ step: "registering" });
|
|
1199
|
-
let hashedOnchainId;
|
|
1200
|
-
try {
|
|
1201
|
-
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1202
|
-
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1203
|
-
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1204
|
-
} catch (err) {
|
|
1205
|
-
throw new OfframpError("Payee registration failed", "REGISTRATION_FAILED", "registering", err);
|
|
1206
|
-
}
|
|
1207
|
-
onProgress?.({ step: "depositing" });
|
|
1208
|
-
const conversionRates = [
|
|
1209
|
-
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1210
|
-
];
|
|
1211
|
-
const baseCurrenciesOverride = mapConversionRatesToOnchainMinRate(conversionRates, 1);
|
|
1212
|
-
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1213
|
-
let hash;
|
|
1214
|
-
try {
|
|
1215
|
-
const result = await client.createDeposit({
|
|
1216
|
-
token: USDC_ADDRESS,
|
|
1217
|
-
amount: amountUnits,
|
|
1218
|
-
retainOnEmpty: false,
|
|
1219
|
-
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1220
|
-
processorNames: [platformId],
|
|
1221
|
-
conversionRates,
|
|
1222
|
-
payeeDetailsHashes: [hashedOnchainId],
|
|
1223
|
-
paymentMethodsOverride: [methodHash],
|
|
1224
|
-
paymentMethodDataOverride: [
|
|
1225
|
-
{
|
|
1226
|
-
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1227
|
-
payeeDetails: hashedOnchainId,
|
|
1228
|
-
data: "0x"
|
|
1229
|
-
}
|
|
1230
|
-
],
|
|
1231
|
-
currenciesOverride,
|
|
1232
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1233
|
-
txOverrides
|
|
1234
|
-
});
|
|
1235
|
-
if (!result?.hash) throw new Error("No transaction hash returned");
|
|
1236
|
-
hash = result.hash;
|
|
1237
|
-
} catch (err) {
|
|
1238
|
-
if (isUserCancellation(err))
|
|
1239
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1240
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1241
|
-
throw new OfframpError(
|
|
1242
|
-
`Deposit transaction failed: ${detail}`,
|
|
1243
|
-
"DEPOSIT_FAILED",
|
|
1244
|
-
"depositing",
|
|
1245
|
-
err
|
|
1246
|
-
);
|
|
1247
|
-
}
|
|
1248
|
-
onProgress?.({ step: "confirming", txHash: hash });
|
|
1249
|
-
let depositId = "";
|
|
1250
|
-
const receiptClient = client;
|
|
1251
|
-
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1413
|
+
emitProgress({ step: "depositing" });
|
|
1414
|
+
const conversionRates = [
|
|
1415
|
+
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1416
|
+
];
|
|
1417
|
+
const baseCurrenciesOverride = mapConversionRatesToOnchainMinRate(conversionRates, 1);
|
|
1418
|
+
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1419
|
+
let hash;
|
|
1252
1420
|
try {
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1421
|
+
const result2 = await client.createDeposit({
|
|
1422
|
+
token: USDC_ADDRESS,
|
|
1423
|
+
amount: amountUnits,
|
|
1424
|
+
retainOnEmpty: false,
|
|
1425
|
+
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1426
|
+
processorNames: [platformId],
|
|
1427
|
+
conversionRates,
|
|
1428
|
+
payeeDetailsHashes: [hashedOnchainId],
|
|
1429
|
+
paymentMethodsOverride: [methodHash],
|
|
1430
|
+
paymentMethodDataOverride: [
|
|
1431
|
+
{
|
|
1432
|
+
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1433
|
+
payeeDetails: hashedOnchainId,
|
|
1434
|
+
data: "0x"
|
|
1435
|
+
}
|
|
1436
|
+
],
|
|
1437
|
+
currenciesOverride,
|
|
1438
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1439
|
+
txOverrides
|
|
1440
|
+
});
|
|
1441
|
+
if (!result2?.hash) throw new Error("No transaction hash returned");
|
|
1442
|
+
hash = result2.hash;
|
|
1443
|
+
} catch (err) {
|
|
1444
|
+
if (isUserCancellation(err))
|
|
1445
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1446
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1447
|
+
throw new OfframpError(
|
|
1448
|
+
`Deposit transaction failed: ${detail}`,
|
|
1449
|
+
"DEPOSIT_FAILED",
|
|
1450
|
+
"depositing",
|
|
1451
|
+
err
|
|
1452
|
+
);
|
|
1256
1453
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1454
|
+
emitProgress({ step: "confirming", txHash: hash });
|
|
1455
|
+
let depositId = "";
|
|
1456
|
+
const receiptClient = client;
|
|
1457
|
+
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1261
1458
|
try {
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
{ limit: 25 }
|
|
1265
|
-
);
|
|
1266
|
-
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1267
|
-
if (hit) {
|
|
1268
|
-
depositId = String(hit.depositId);
|
|
1269
|
-
break;
|
|
1270
|
-
}
|
|
1459
|
+
const receipt = await receiptClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1460
|
+
depositId = extractDepositIdFromLogs(receipt.logs) || "";
|
|
1271
1461
|
} catch {
|
|
1272
1462
|
}
|
|
1273
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
1274
|
-
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1275
1463
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1464
|
+
if (!depositId) {
|
|
1465
|
+
let delay = INDEXER_INITIAL_DELAY_MS;
|
|
1466
|
+
for (let attempt = 0; attempt < INDEXER_MAX_ATTEMPTS && !depositId; attempt++) {
|
|
1467
|
+
try {
|
|
1468
|
+
const deps = await client.indexer.getDepositsWithRelations(
|
|
1469
|
+
{ depositor: walletAddress },
|
|
1470
|
+
{ limit: 25 }
|
|
1471
|
+
);
|
|
1472
|
+
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1473
|
+
if (hit) {
|
|
1474
|
+
depositId = String(hit.depositId);
|
|
1475
|
+
break;
|
|
1476
|
+
}
|
|
1477
|
+
} catch {
|
|
1478
|
+
}
|
|
1479
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1480
|
+
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
if (!depositId) {
|
|
1484
|
+
throw new OfframpError(
|
|
1485
|
+
"Deposit created on-chain but could not confirm deposit ID. Your funds are safe. Call offramp() again to resume delegation.",
|
|
1486
|
+
"CONFIRMATION_FAILED",
|
|
1487
|
+
"confirming",
|
|
1488
|
+
void 0,
|
|
1489
|
+
{ txHash: hash }
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
emitProgress({ step: "delegating", txHash: hash, depositId });
|
|
1493
|
+
try {
|
|
1494
|
+
await client.setRateManager({
|
|
1495
|
+
depositId: BigInt(depositId),
|
|
1496
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1497
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1498
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1499
|
+
txOverrides
|
|
1500
|
+
});
|
|
1501
|
+
} catch (delegationError) {
|
|
1502
|
+
if (isUserCancellation(delegationError)) {
|
|
1503
|
+
throw new OfframpError(
|
|
1504
|
+
"User cancelled delegation",
|
|
1505
|
+
"USER_CANCELLED",
|
|
1506
|
+
"delegating",
|
|
1507
|
+
delegationError,
|
|
1508
|
+
{ txHash: hash, depositId }
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1297
1511
|
throw new OfframpError(
|
|
1298
|
-
"
|
|
1299
|
-
"
|
|
1512
|
+
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1513
|
+
"DELEGATION_FAILED",
|
|
1300
1514
|
"delegating",
|
|
1301
1515
|
delegationError,
|
|
1302
1516
|
{ txHash: hash, depositId }
|
|
1303
1517
|
);
|
|
1304
1518
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
"
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1519
|
+
let otcLink;
|
|
1520
|
+
if (params.otcTaker) {
|
|
1521
|
+
emitProgress({ step: "restricting", txHash: hash, depositId });
|
|
1522
|
+
try {
|
|
1523
|
+
const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
|
|
1524
|
+
otcLink = otcResult.otcLink;
|
|
1525
|
+
} catch (otcError) {
|
|
1526
|
+
if (isUserCancellation(otcError)) {
|
|
1527
|
+
throw new OfframpError(
|
|
1528
|
+
"User cancelled OTC restriction",
|
|
1529
|
+
"USER_CANCELLED",
|
|
1530
|
+
"restricting",
|
|
1531
|
+
otcError,
|
|
1532
|
+
{
|
|
1533
|
+
txHash: hash,
|
|
1534
|
+
depositId
|
|
1535
|
+
}
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1321
1538
|
throw new OfframpError(
|
|
1322
|
-
"
|
|
1323
|
-
"
|
|
1539
|
+
"Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
|
|
1540
|
+
"DEPOSIT_FAILED",
|
|
1324
1541
|
"restricting",
|
|
1325
1542
|
otcError,
|
|
1326
|
-
{
|
|
1327
|
-
txHash: hash,
|
|
1328
|
-
depositId
|
|
1329
|
-
}
|
|
1543
|
+
{ txHash: hash, depositId }
|
|
1330
1544
|
);
|
|
1331
1545
|
}
|
|
1546
|
+
}
|
|
1547
|
+
emitProgress({ step: "done", txHash: hash, depositId });
|
|
1548
|
+
const result = { depositId, txHash: hash, resumed: false, otcLink };
|
|
1549
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1550
|
+
depositId: result.depositId,
|
|
1551
|
+
txHash: result.txHash,
|
|
1552
|
+
wallet: walletAddress,
|
|
1553
|
+
resumed: false,
|
|
1554
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1555
|
+
});
|
|
1556
|
+
return result;
|
|
1557
|
+
} catch (error) {
|
|
1558
|
+
const known = error instanceof OfframpError ? error : null;
|
|
1559
|
+
telemetry.emit("sdk.createDeposit.error", {
|
|
1560
|
+
code: known?.code ?? "DEPOSIT_FAILED",
|
|
1561
|
+
step: known?.step ?? "unknown"
|
|
1562
|
+
});
|
|
1563
|
+
throw error;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// src/currencies.ts
|
|
1568
|
+
import { currencyInfo as currencyInfo2 } from "@zkp2p/sdk";
|
|
1569
|
+
function buildCurrencies() {
|
|
1570
|
+
const entries = {};
|
|
1571
|
+
for (const [code, info] of Object.entries(
|
|
1572
|
+
currencyInfo2
|
|
1573
|
+
)) {
|
|
1574
|
+
entries[code] = {
|
|
1575
|
+
code: info.currencyCode ?? code,
|
|
1576
|
+
name: info.currencyName ?? code,
|
|
1577
|
+
symbol: info.currencySymbol ?? code,
|
|
1578
|
+
countryCode: info.countryCode ?? ""
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
return entries;
|
|
1582
|
+
}
|
|
1583
|
+
var CURRENCIES = buildCurrencies();
|
|
1584
|
+
|
|
1585
|
+
// src/offramp.ts
|
|
1586
|
+
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1587
|
+
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1588
|
+
function getWalletAddress(walletClient) {
|
|
1589
|
+
const address = walletClient.account?.address;
|
|
1590
|
+
if (!address) {
|
|
1591
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1592
|
+
}
|
|
1593
|
+
return address.toLowerCase();
|
|
1594
|
+
}
|
|
1595
|
+
function getIdempotencyStorageKey(walletAddress, idempotencyKey) {
|
|
1596
|
+
return `${IDEMPOTENCY_STORAGE_PREFIX}${walletAddress}:${idempotencyKey}`;
|
|
1597
|
+
}
|
|
1598
|
+
function readIdempotencyResult(storageKey) {
|
|
1599
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1600
|
+
try {
|
|
1601
|
+
const raw = sessionStorage.getItem(storageKey);
|
|
1602
|
+
if (!raw) return null;
|
|
1603
|
+
const parsed = JSON.parse(raw);
|
|
1604
|
+
const expiresAt = typeof parsed.expiresAt === "number" && Number.isFinite(parsed.expiresAt) ? parsed.expiresAt : 0;
|
|
1605
|
+
if (expiresAt <= Date.now()) {
|
|
1606
|
+
sessionStorage.removeItem(storageKey);
|
|
1607
|
+
return null;
|
|
1608
|
+
}
|
|
1609
|
+
const result = parsed.result;
|
|
1610
|
+
if (!result?.depositId || !result.txHash) {
|
|
1611
|
+
sessionStorage.removeItem(storageKey);
|
|
1612
|
+
return null;
|
|
1613
|
+
}
|
|
1614
|
+
return result;
|
|
1615
|
+
} catch {
|
|
1616
|
+
return null;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
function writeIdempotencyResult(storageKey, result) {
|
|
1620
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1621
|
+
try {
|
|
1622
|
+
const record = {
|
|
1623
|
+
result,
|
|
1624
|
+
expiresAt: Date.now() + IDEMPOTENCY_TTL_MS
|
|
1625
|
+
};
|
|
1626
|
+
sessionStorage.setItem(storageKey, JSON.stringify(record));
|
|
1627
|
+
} catch {
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
function createPerFlowTelemetry(base4, integratorId, referralId) {
|
|
1631
|
+
return createTelemetryContext({
|
|
1632
|
+
sdkVersion: base4.sdkVersion,
|
|
1633
|
+
telemetry: base4.enabled,
|
|
1634
|
+
integratorId: sanitizeAttributionId(integratorId) ?? base4.integratorId,
|
|
1635
|
+
referralId: sanitizeAttributionId(referralId) ?? base4.referralId
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
function normalizeCurrencyEntry(code) {
|
|
1639
|
+
const fromCatalog = CURRENCIES[code];
|
|
1640
|
+
if (fromCatalog) return fromCatalog;
|
|
1641
|
+
return {
|
|
1642
|
+
code,
|
|
1643
|
+
name: code,
|
|
1644
|
+
symbol: code,
|
|
1645
|
+
countryCode: ""
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
function buildCurrencyGroups() {
|
|
1649
|
+
const grouped = {};
|
|
1650
|
+
for (const platform of Object.values(PLATFORMS)) {
|
|
1651
|
+
grouped[platform.id] = platform.currencies.map(normalizeCurrencyEntry);
|
|
1652
|
+
}
|
|
1653
|
+
return grouped;
|
|
1654
|
+
}
|
|
1655
|
+
var Offramp = class {
|
|
1656
|
+
walletClient;
|
|
1657
|
+
telemetry;
|
|
1658
|
+
constructor(options) {
|
|
1659
|
+
this.walletClient = options.walletClient;
|
|
1660
|
+
this.telemetry = createTelemetryContext({
|
|
1661
|
+
sdkVersion: SDK_VERSION,
|
|
1662
|
+
telemetry: options.telemetry,
|
|
1663
|
+
integratorId: options.integratorId,
|
|
1664
|
+
referralId: options.referralId
|
|
1665
|
+
});
|
|
1666
|
+
this.telemetry.emit("sdk.init", {
|
|
1667
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown"
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1670
|
+
async createDeposit(params, onProgress) {
|
|
1671
|
+
const flowTelemetry = createPerFlowTelemetry(
|
|
1672
|
+
this.telemetry,
|
|
1673
|
+
params.integratorId,
|
|
1674
|
+
params.referralId
|
|
1675
|
+
);
|
|
1676
|
+
const sanitizedParams = {
|
|
1677
|
+
...params,
|
|
1678
|
+
integratorId: flowTelemetry.integratorId,
|
|
1679
|
+
referralId: flowTelemetry.referralId
|
|
1680
|
+
};
|
|
1681
|
+
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1682
|
+
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1683
|
+
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1684
|
+
if (storageKey) {
|
|
1685
|
+
const existing = readIdempotencyResult(storageKey);
|
|
1686
|
+
if (existing) return existing;
|
|
1687
|
+
}
|
|
1688
|
+
const result = await offramp(
|
|
1689
|
+
this.walletClient,
|
|
1690
|
+
sanitizedParams,
|
|
1691
|
+
onProgress,
|
|
1692
|
+
flowTelemetry
|
|
1693
|
+
);
|
|
1694
|
+
if (storageKey) {
|
|
1695
|
+
writeIdempotencyResult(storageKey, result);
|
|
1696
|
+
}
|
|
1697
|
+
return result;
|
|
1698
|
+
}
|
|
1699
|
+
getQuote(input) {
|
|
1700
|
+
const amountUsdc = Number(input.amount);
|
|
1701
|
+
if (!Number.isFinite(amountUsdc) || amountUsdc <= 0) {
|
|
1702
|
+
throw new OfframpError("Amount must be a positive number", "VALIDATION");
|
|
1703
|
+
}
|
|
1704
|
+
if (!input.platform.currencies.includes(input.currency.code)) {
|
|
1332
1705
|
throw new OfframpError(
|
|
1333
|
-
|
|
1334
|
-
"
|
|
1335
|
-
"restricting",
|
|
1336
|
-
otcError,
|
|
1337
|
-
{ txHash: hash, depositId }
|
|
1706
|
+
`${input.currency.code} is not supported on ${input.platform.name}`,
|
|
1707
|
+
"UNSUPPORTED"
|
|
1338
1708
|
);
|
|
1339
1709
|
}
|
|
1710
|
+
const vaultSpreadBps = 0;
|
|
1711
|
+
const spreadFactor = Math.max(0, 1 - vaultSpreadBps / 1e4);
|
|
1712
|
+
const expectedFiat = amountUsdc * spreadFactor;
|
|
1713
|
+
const effectiveRate = amountUsdc === 0 ? 0 : expectedFiat / amountUsdc;
|
|
1714
|
+
return {
|
|
1715
|
+
amountUsdc,
|
|
1716
|
+
expectedFiat,
|
|
1717
|
+
effectiveRate,
|
|
1718
|
+
vaultSpreadBps
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
getVaultStatus() {
|
|
1722
|
+
return {
|
|
1723
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1724
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1725
|
+
feeRateBps: DELEGATE_MANAGER_FEE_BPS,
|
|
1726
|
+
escrow: ESCROW_ADDRESS,
|
|
1727
|
+
isActive: true
|
|
1728
|
+
};
|
|
1340
1729
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1730
|
+
listCurrencies() {
|
|
1731
|
+
return buildCurrencyGroups();
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
function createOfframp(options) {
|
|
1735
|
+
return new Offramp(options);
|
|
1343
1736
|
}
|
|
1344
1737
|
|
|
1738
|
+
// src/extension.ts
|
|
1739
|
+
import {
|
|
1740
|
+
PEER_EXTENSION_CHROME_URL,
|
|
1741
|
+
peerExtensionSdk,
|
|
1742
|
+
createPeerExtensionSdk,
|
|
1743
|
+
isPeerExtensionAvailable,
|
|
1744
|
+
openPeerExtensionInstallPage,
|
|
1745
|
+
getPeerExtensionState
|
|
1746
|
+
} from "@zkp2p/sdk";
|
|
1747
|
+
|
|
1345
1748
|
export {
|
|
1346
1749
|
ESCROW_ADDRESS,
|
|
1750
|
+
normalizePaypalMeUsername,
|
|
1347
1751
|
PLATFORMS,
|
|
1752
|
+
getPeerExtensionRegistrationInfo,
|
|
1753
|
+
isPeerExtensionRegistrationError,
|
|
1754
|
+
MakersCreateError,
|
|
1348
1755
|
OfframpError,
|
|
1349
1756
|
enableOtc,
|
|
1350
1757
|
disableOtc,
|
|
@@ -1353,6 +1760,19 @@ export {
|
|
|
1353
1760
|
close,
|
|
1354
1761
|
delegate,
|
|
1355
1762
|
undelegate,
|
|
1356
|
-
|
|
1763
|
+
sanitizeAttributionId,
|
|
1764
|
+
emitEvent,
|
|
1765
|
+
sendTelemetryEvent,
|
|
1766
|
+
createTelemetryContext,
|
|
1767
|
+
offramp,
|
|
1768
|
+
CURRENCIES,
|
|
1769
|
+
Offramp,
|
|
1770
|
+
createOfframp,
|
|
1771
|
+
PEER_EXTENSION_CHROME_URL,
|
|
1772
|
+
peerExtensionSdk,
|
|
1773
|
+
createPeerExtensionSdk,
|
|
1774
|
+
isPeerExtensionAvailable,
|
|
1775
|
+
openPeerExtensionInstallPage,
|
|
1776
|
+
getPeerExtensionState
|
|
1357
1777
|
};
|
|
1358
|
-
//# sourceMappingURL=chunk-
|
|
1778
|
+
//# sourceMappingURL=chunk-HBR3Z6HX.js.map
|