@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
package/dist/index.cjs
CHANGED
|
@@ -22,15 +22,32 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
CURRENCIES: () => CURRENCIES,
|
|
24
24
|
ESCROW_ADDRESS: () => ESCROW_ADDRESS,
|
|
25
|
+
MakersCreateError: () => MakersCreateError,
|
|
26
|
+
OFFRAMP_ERROR_CODES: () => OFFRAMP_ERROR_CODES,
|
|
27
|
+
Offramp: () => Offramp,
|
|
25
28
|
OfframpError: () => OfframpError,
|
|
29
|
+
PEER_EXTENSION_CHROME_URL: () => import_sdk7.PEER_EXTENSION_CHROME_URL,
|
|
26
30
|
PLATFORMS: () => PLATFORMS,
|
|
27
31
|
close: () => close,
|
|
32
|
+
createOfframp: () => createOfframp,
|
|
33
|
+
createPeerExtensionSdk: () => import_sdk7.createPeerExtensionSdk,
|
|
34
|
+
createTelemetryContext: () => createTelemetryContext,
|
|
28
35
|
delegate: () => delegate,
|
|
29
36
|
deposits: () => deposits,
|
|
30
37
|
disableOtc: () => disableOtc,
|
|
38
|
+
emitEvent: () => emitEvent,
|
|
31
39
|
enableOtc: () => enableOtc,
|
|
32
40
|
getOtcLink: () => getOtcLink,
|
|
41
|
+
getPeerExtensionRegistrationInfo: () => getPeerExtensionRegistrationInfo,
|
|
42
|
+
getPeerExtensionState: () => import_sdk7.getPeerExtensionState,
|
|
43
|
+
isPeerExtensionAvailable: () => import_sdk7.isPeerExtensionAvailable,
|
|
44
|
+
isPeerExtensionRegistrationError: () => isPeerExtensionRegistrationError,
|
|
45
|
+
normalizePaypalMeUsername: () => normalizePaypalMeUsername,
|
|
33
46
|
offramp: () => offramp,
|
|
47
|
+
openPeerExtensionInstallPage: () => import_sdk7.openPeerExtensionInstallPage,
|
|
48
|
+
peerExtensionSdk: () => import_sdk7.peerExtensionSdk,
|
|
49
|
+
sanitizeAttributionId: () => sanitizeAttributionId,
|
|
50
|
+
sendTelemetryEvent: () => sendTelemetryEvent,
|
|
34
51
|
undelegate: () => undelegate
|
|
35
52
|
});
|
|
36
53
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -42,6 +59,7 @@ var import_sdk5 = require("@zkp2p/sdk");
|
|
|
42
59
|
|
|
43
60
|
// src/config.ts
|
|
44
61
|
var import_sdk = require("@zkp2p/sdk");
|
|
62
|
+
var SDK_VERSION = "1.2.0";
|
|
45
63
|
var BASE_CHAIN_ID = 8453;
|
|
46
64
|
var RUNTIME_ENV = "production";
|
|
47
65
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
@@ -57,6 +75,7 @@ var GATING_SERVICE_ADDRESS = (0, import_sdk.getGatingServiceAddress)(
|
|
|
57
75
|
);
|
|
58
76
|
var DELEGATE_RATE_MANAGER_ID = "0x8666d6fb0f6797c56e95339fd7ca82fdd348b9db200e10a4c4aa0a0b879fc41c";
|
|
59
77
|
var RATE_MANAGER_REGISTRY_ADDRESS = "0xeed7db23e724ac4590d6db6f78fda6db203535f3";
|
|
78
|
+
var DELEGATE_MANAGER_FEE_BPS = 10;
|
|
60
79
|
var REFERRER = "galleonlabs";
|
|
61
80
|
var MIN_DEPOSIT_USDC = 1;
|
|
62
81
|
var MIN_ORDER_USDC = 1;
|
|
@@ -157,6 +176,17 @@ function resolveSupportedCurrencies(platform) {
|
|
|
157
176
|
}
|
|
158
177
|
return Array.from(codes).sort();
|
|
159
178
|
}
|
|
179
|
+
function normalizePaypalMeUsername(value) {
|
|
180
|
+
const trimmed = value.trim();
|
|
181
|
+
if (!trimmed) return "";
|
|
182
|
+
const withoutProtocol = trimmed.replace(/^https?:\/\//i, "").replace(/^www\./i, "");
|
|
183
|
+
if (/^paypal\.me(?:[?#].*)?$/i.test(withoutProtocol)) return "";
|
|
184
|
+
const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
|
|
185
|
+
const [pathWithoutQuery] = withoutDomain.split(/[?#]/, 1);
|
|
186
|
+
const sanitizedPath = pathWithoutQuery.replace(/^\/+/, "");
|
|
187
|
+
const [username] = sanitizedPath.split("/", 1);
|
|
188
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
189
|
+
}
|
|
160
190
|
var BLUEPRINTS = {
|
|
161
191
|
venmo: {
|
|
162
192
|
id: "venmo",
|
|
@@ -204,7 +234,13 @@ var BLUEPRINTS = {
|
|
|
204
234
|
placeholder: "wisetag (no @)",
|
|
205
235
|
helperText: "Your Wise @wisetag (no @)",
|
|
206
236
|
validation: import_zod.z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
|
|
207
|
-
transform: (v) => v.replace(/^@+/, "").trim()
|
|
237
|
+
transform: (v) => v.replace(/^@+/, "").trim(),
|
|
238
|
+
extensionRegistration: {
|
|
239
|
+
providerId: "wise",
|
|
240
|
+
requiredPrompt: "This Wisetag is not registered yet. Verify it in the Peer extension, then refresh and retry with the same Wisetag.",
|
|
241
|
+
ctaLabel: "Install Peer Extension",
|
|
242
|
+
minExtensionVersion: "0.4.12"
|
|
243
|
+
}
|
|
208
244
|
},
|
|
209
245
|
mercadopago: {
|
|
210
246
|
id: "mercadopago",
|
|
@@ -225,10 +261,18 @@ var BLUEPRINTS = {
|
|
|
225
261
|
paypal: {
|
|
226
262
|
id: "paypal",
|
|
227
263
|
name: "PayPal",
|
|
228
|
-
identifierLabel: "
|
|
229
|
-
placeholder: "
|
|
230
|
-
helperText: "
|
|
231
|
-
validation: import_zod.z.string().
|
|
264
|
+
identifierLabel: "PayPal.me Username",
|
|
265
|
+
placeholder: "paypal.me/your-username",
|
|
266
|
+
helperText: "Your PayPal.me username, not your email",
|
|
267
|
+
validation: import_zod.z.string().min(1, "PayPal.me username is required").regex(/^[a-z0-9._-]+$/i, "Use your PayPal.me username (letters, numbers, . _ -)"),
|
|
268
|
+
transform: normalizePaypalMeUsername,
|
|
269
|
+
extensionRegistration: {
|
|
270
|
+
providerId: "paypal",
|
|
271
|
+
requiredPrompt: "This PayPal username is not registered yet. Verify it in the Peer extension, then refresh and retry with the same PayPal username.",
|
|
272
|
+
ctaLabel: "Install Peer Extension",
|
|
273
|
+
ctaSubtext: "PayPal Business accounts are not supported at this time.",
|
|
274
|
+
minExtensionVersion: "0.4.14"
|
|
275
|
+
}
|
|
232
276
|
},
|
|
233
277
|
monzo: {
|
|
234
278
|
id: "monzo",
|
|
@@ -259,6 +303,7 @@ function buildPlatformEntry(bp) {
|
|
|
259
303
|
placeholder: bp.placeholder,
|
|
260
304
|
help: bp.helperText
|
|
261
305
|
},
|
|
306
|
+
...bp.extensionRegistration ? { extensionRegistration: bp.extensionRegistration } : {},
|
|
262
307
|
validate(input) {
|
|
263
308
|
const transformed = bp.transform ? bp.transform(input) : input;
|
|
264
309
|
const result = bp.validation.safeParse(transformed);
|
|
@@ -285,6 +330,9 @@ var PLATFORMS = {
|
|
|
285
330
|
MONZO: buildPlatformEntry(BLUEPRINTS.monzo),
|
|
286
331
|
N26: buildPlatformEntry(BLUEPRINTS.n26)
|
|
287
332
|
};
|
|
333
|
+
function getPlatformById(id) {
|
|
334
|
+
return Object.values(PLATFORMS).find((p) => p.id === id) ?? null;
|
|
335
|
+
}
|
|
288
336
|
function normalizePaymentMethodLookupName(platform) {
|
|
289
337
|
const normalized = platform.trim().toLowerCase();
|
|
290
338
|
if (!normalized) return null;
|
|
@@ -328,34 +376,37 @@ function getPaymentMethodHashes(platform) {
|
|
|
328
376
|
}
|
|
329
377
|
return Array.from(hashes);
|
|
330
378
|
}
|
|
331
|
-
function buildDepositData(platform, identifier) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
case "monzo":
|
|
350
|
-
return { monzoMeUsername: identifier, telegramUsername: "" };
|
|
351
|
-
case "n26":
|
|
352
|
-
return { iban: identifier, telegramUsername: "" };
|
|
353
|
-
default:
|
|
354
|
-
return { identifier, telegramUsername: "" };
|
|
355
|
-
}
|
|
379
|
+
function buildDepositData(platform, identifier, telegramUsername = "") {
|
|
380
|
+
const offchainId = platform === "chime" ? identifier.toLowerCase() : identifier;
|
|
381
|
+
return telegramUsername ? { offchainId, telegramUsername } : { offchainId };
|
|
382
|
+
}
|
|
383
|
+
function getPeerExtensionRegistrationInfo(platform) {
|
|
384
|
+
const entry = getPlatformById(platform);
|
|
385
|
+
return entry?.extensionRegistration ?? null;
|
|
386
|
+
}
|
|
387
|
+
var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
|
|
388
|
+
function isPeerExtensionRegistrationError(platform, message, statusCode) {
|
|
389
|
+
const info = getPeerExtensionRegistrationInfo(platform);
|
|
390
|
+
if (!info) return false;
|
|
391
|
+
const normalized = (message ?? "").trim();
|
|
392
|
+
if (normalized === info.requiredPrompt) return true;
|
|
393
|
+
if (statusCode === 400) return true;
|
|
394
|
+
if (!normalized) return false;
|
|
395
|
+
const lower = normalized.toLowerCase();
|
|
396
|
+
return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
|
|
356
397
|
}
|
|
357
398
|
|
|
358
399
|
// src/errors.ts
|
|
400
|
+
var MakersCreateError = class extends Error {
|
|
401
|
+
status;
|
|
402
|
+
body;
|
|
403
|
+
constructor(message, status, body) {
|
|
404
|
+
super(message);
|
|
405
|
+
this.name = "MakersCreateError";
|
|
406
|
+
this.status = status;
|
|
407
|
+
this.body = body;
|
|
408
|
+
}
|
|
409
|
+
};
|
|
359
410
|
var OfframpError = class extends Error {
|
|
360
411
|
code;
|
|
361
412
|
step;
|
|
@@ -997,6 +1048,108 @@ async function undelegate(walletClient, depositId, escrowAddress) {
|
|
|
997
1048
|
return hash;
|
|
998
1049
|
}
|
|
999
1050
|
|
|
1051
|
+
// src/telemetry.ts
|
|
1052
|
+
var SDK_EVENTS_ENDPOINT = "https://usdctofiat.xyz/api/sdk-events";
|
|
1053
|
+
var MAX_BATCH_SIZE = 50;
|
|
1054
|
+
var FLUSH_DELAY_MS = 180;
|
|
1055
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
1056
|
+
var queuedEvents = [];
|
|
1057
|
+
var flushTimer = null;
|
|
1058
|
+
var flushInFlight = false;
|
|
1059
|
+
function sanitizeAttributionId(raw) {
|
|
1060
|
+
if (typeof raw !== "string") return void 0;
|
|
1061
|
+
const stripped = raw.trim().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1062
|
+
if (!stripped) return void 0;
|
|
1063
|
+
if (stripped.length > 64) return void 0;
|
|
1064
|
+
return stripped;
|
|
1065
|
+
}
|
|
1066
|
+
function scheduleFlush() {
|
|
1067
|
+
if (flushTimer != null) return;
|
|
1068
|
+
flushTimer = setTimeout(() => {
|
|
1069
|
+
flushTimer = null;
|
|
1070
|
+
void flushQueue();
|
|
1071
|
+
}, FLUSH_DELAY_MS);
|
|
1072
|
+
}
|
|
1073
|
+
async function postBatch(batch) {
|
|
1074
|
+
if (!batch.length) return;
|
|
1075
|
+
const body = JSON.stringify(batch);
|
|
1076
|
+
try {
|
|
1077
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
1078
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
1079
|
+
const accepted = navigator.sendBeacon(SDK_EVENTS_ENDPOINT, blob);
|
|
1080
|
+
if (accepted) return;
|
|
1081
|
+
}
|
|
1082
|
+
} catch {
|
|
1083
|
+
}
|
|
1084
|
+
try {
|
|
1085
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
1086
|
+
const timeout = controller ? setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS) : null;
|
|
1087
|
+
await fetch(SDK_EVENTS_ENDPOINT, {
|
|
1088
|
+
method: "POST",
|
|
1089
|
+
headers: { "Content-Type": "application/json" },
|
|
1090
|
+
body,
|
|
1091
|
+
keepalive: true,
|
|
1092
|
+
signal: controller?.signal
|
|
1093
|
+
}).catch(() => {
|
|
1094
|
+
});
|
|
1095
|
+
if (timeout) clearTimeout(timeout);
|
|
1096
|
+
} catch {
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
async function flushQueue() {
|
|
1100
|
+
if (flushInFlight) return;
|
|
1101
|
+
if (!queuedEvents.length) return;
|
|
1102
|
+
flushInFlight = true;
|
|
1103
|
+
try {
|
|
1104
|
+
while (queuedEvents.length > 0) {
|
|
1105
|
+
const batch = queuedEvents.slice(0, MAX_BATCH_SIZE);
|
|
1106
|
+
queuedEvents = queuedEvents.slice(batch.length);
|
|
1107
|
+
await postBatch(batch);
|
|
1108
|
+
}
|
|
1109
|
+
} finally {
|
|
1110
|
+
flushInFlight = false;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
function emitEvent(name, payload) {
|
|
1114
|
+
try {
|
|
1115
|
+
queuedEvents.push({
|
|
1116
|
+
name,
|
|
1117
|
+
payload,
|
|
1118
|
+
ts: Date.now()
|
|
1119
|
+
});
|
|
1120
|
+
if (queuedEvents.length >= MAX_BATCH_SIZE) {
|
|
1121
|
+
void flushQueue();
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
scheduleFlush();
|
|
1125
|
+
} catch {
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
function sendTelemetryEvent(name, payload) {
|
|
1129
|
+
emitEvent(name, payload);
|
|
1130
|
+
}
|
|
1131
|
+
function createTelemetryContext(options) {
|
|
1132
|
+
const integratorId = sanitizeAttributionId(options.integratorId);
|
|
1133
|
+
const referralId = sanitizeAttributionId(options.referralId);
|
|
1134
|
+
const enabled = options.telemetry !== false;
|
|
1135
|
+
const sdkVersion = options.sdkVersion;
|
|
1136
|
+
return {
|
|
1137
|
+
enabled,
|
|
1138
|
+
sdkVersion,
|
|
1139
|
+
integratorId,
|
|
1140
|
+
referralId,
|
|
1141
|
+
emit(name, payload = {}) {
|
|
1142
|
+
if (!enabled) return;
|
|
1143
|
+
emitEvent(name, {
|
|
1144
|
+
sdkVersion,
|
|
1145
|
+
integratorId,
|
|
1146
|
+
referralId,
|
|
1147
|
+
...payload
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1000
1153
|
// src/deposit.ts
|
|
1001
1154
|
function usdcToUnits(amount) {
|
|
1002
1155
|
return (0, import_viem3.parseUnits)(amount, 6);
|
|
@@ -1042,23 +1195,39 @@ function extractDepositIdFromLogs(logs) {
|
|
|
1042
1195
|
}
|
|
1043
1196
|
return null;
|
|
1044
1197
|
}
|
|
1045
|
-
async function registerPayeeDetails(processorName,
|
|
1198
|
+
async function registerPayeeDetails(processorName, payload) {
|
|
1046
1199
|
const controller = new AbortController();
|
|
1047
1200
|
const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
|
|
1048
1201
|
try {
|
|
1049
|
-
const res = await fetch(`${API_BASE_URL}/
|
|
1202
|
+
const res = await fetch(`${API_BASE_URL}/v2/makers/create`, {
|
|
1050
1203
|
method: "POST",
|
|
1051
1204
|
headers: { "Content-Type": "application/json" },
|
|
1052
|
-
body: JSON.stringify({ processorName,
|
|
1205
|
+
body: JSON.stringify({ processorName, ...payload }),
|
|
1053
1206
|
signal: controller.signal
|
|
1054
1207
|
});
|
|
1055
1208
|
if (!res.ok) {
|
|
1056
1209
|
const txt = await res.text().catch(() => "");
|
|
1057
|
-
|
|
1210
|
+
let detail = txt || res.statusText;
|
|
1211
|
+
try {
|
|
1212
|
+
const parsed = JSON.parse(txt);
|
|
1213
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
1214
|
+
detail = parsed.message;
|
|
1215
|
+
}
|
|
1216
|
+
} catch {
|
|
1217
|
+
}
|
|
1218
|
+
throw new MakersCreateError(
|
|
1219
|
+
`makers/create failed (${res.status}): ${detail}`,
|
|
1220
|
+
res.status,
|
|
1221
|
+
txt
|
|
1222
|
+
);
|
|
1058
1223
|
}
|
|
1059
1224
|
const json = await res.json();
|
|
1060
1225
|
if (!json.success || !json.responseObject?.hashedOnchainId) {
|
|
1061
|
-
throw new
|
|
1226
|
+
throw new MakersCreateError(
|
|
1227
|
+
json.message || "makers/create returned no hashedOnchainId",
|
|
1228
|
+
json.statusCode ?? null,
|
|
1229
|
+
""
|
|
1230
|
+
);
|
|
1062
1231
|
}
|
|
1063
1232
|
return json.responseObject.hashedOnchainId;
|
|
1064
1233
|
} finally {
|
|
@@ -1114,256 +1283,326 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1114
1283
|
if (remaining === 0n) return false;
|
|
1115
1284
|
return (d.rateManagerId ?? "").toLowerCase() !== delegateIdLower;
|
|
1116
1285
|
});
|
|
1117
|
-
undelegated.sort((a, b) =>
|
|
1286
|
+
undelegated.sort((a, b) => {
|
|
1287
|
+
const left = BigInt(a.depositId);
|
|
1288
|
+
const right = BigInt(b.depositId);
|
|
1289
|
+
if (left === right) return 0;
|
|
1290
|
+
return left > right ? -1 : 1;
|
|
1291
|
+
});
|
|
1118
1292
|
return undelegated[0] ?? null;
|
|
1119
1293
|
} catch {
|
|
1120
1294
|
return null;
|
|
1121
1295
|
}
|
|
1122
1296
|
}
|
|
1123
|
-
async function offramp(walletClient, params, onProgress) {
|
|
1297
|
+
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1124
1298
|
const { amount, platform, currency, identifier } = params;
|
|
1125
1299
|
const platformId = platform.id;
|
|
1126
1300
|
const currencyCode = currency.code;
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1301
|
+
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1302
|
+
sdkVersion: SDK_VERSION,
|
|
1303
|
+
telemetry: true,
|
|
1304
|
+
integratorId: params.integratorId,
|
|
1305
|
+
referralId: params.referralId
|
|
1306
|
+
});
|
|
1307
|
+
const flowStartMs = Date.now();
|
|
1308
|
+
const emitProgress = (progress) => {
|
|
1309
|
+
onProgress?.(progress);
|
|
1310
|
+
telemetry.emit("sdk.createDeposit.progress", {
|
|
1311
|
+
step: progress.step,
|
|
1312
|
+
txHash: progress.txHash
|
|
1313
|
+
});
|
|
1314
|
+
};
|
|
1315
|
+
try {
|
|
1316
|
+
if (!walletClient.account?.address) {
|
|
1317
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1318
|
+
}
|
|
1319
|
+
const walletAddress = walletClient.account.address.toLowerCase();
|
|
1320
|
+
telemetry.emit("sdk.createDeposit.start", {
|
|
1321
|
+
platform: platformId,
|
|
1322
|
+
currency: currencyCode,
|
|
1323
|
+
amount,
|
|
1324
|
+
wallet: walletAddress
|
|
1325
|
+
});
|
|
1326
|
+
const amt = parseFloat(amount);
|
|
1327
|
+
if (!Number.isFinite(amt) || amt < MIN_DEPOSIT_USDC) {
|
|
1328
|
+
throw new OfframpError(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC`, "VALIDATION");
|
|
1329
|
+
}
|
|
1330
|
+
if (!platform.currencies.includes(currencyCode)) {
|
|
1331
|
+
throw new OfframpError(`${currencyCode} is not supported on ${platform.name}`, "UNSUPPORTED");
|
|
1332
|
+
}
|
|
1333
|
+
const validation = platform.validate(identifier);
|
|
1334
|
+
if (!validation.valid) {
|
|
1335
|
+
throw new OfframpError(validation.error || "Invalid identifier", "VALIDATION");
|
|
1336
|
+
}
|
|
1337
|
+
const normalizedIdentifier = validation.normalized;
|
|
1338
|
+
const methodHash = getPaymentMethodHash(platformId);
|
|
1339
|
+
if (!methodHash) {
|
|
1340
|
+
throw new OfframpError(`${platform.name} is not currently supported`, "UNSUPPORTED");
|
|
1341
|
+
}
|
|
1342
|
+
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1343
|
+
if (existing) {
|
|
1344
|
+
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1345
|
+
const client2 = createSdkClient(walletClient);
|
|
1346
|
+
const txOverrides2 = { referrer: [REFERRER] };
|
|
1347
|
+
try {
|
|
1348
|
+
const result2 = await client2.setRateManager({
|
|
1349
|
+
depositId: BigInt(existing.depositId),
|
|
1350
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1351
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1352
|
+
escrowAddress: existing.escrowAddress,
|
|
1353
|
+
txOverrides: txOverrides2
|
|
1354
|
+
});
|
|
1355
|
+
const txHash = extractTxHash2(result2);
|
|
1356
|
+
emitProgress({ step: "done", txHash, depositId: existing.depositId });
|
|
1357
|
+
const resumedResult = { depositId: existing.depositId, txHash, resumed: true };
|
|
1358
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1359
|
+
depositId: resumedResult.depositId,
|
|
1360
|
+
txHash: resumedResult.txHash,
|
|
1361
|
+
wallet: walletAddress,
|
|
1362
|
+
resumed: true,
|
|
1363
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1364
|
+
});
|
|
1365
|
+
return resumedResult;
|
|
1366
|
+
} catch (err) {
|
|
1367
|
+
if (isUserCancellation(err))
|
|
1368
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
|
|
1369
|
+
throw new OfframpError(
|
|
1370
|
+
"Found undelegated deposit but delegation failed. Try again.",
|
|
1371
|
+
"DELEGATION_FAILED",
|
|
1372
|
+
"resuming",
|
|
1373
|
+
err,
|
|
1374
|
+
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1379
|
+
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1380
|
+
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1381
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, 2500)));
|
|
1152
1382
|
try {
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1383
|
+
const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains3.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
|
|
1384
|
+
const balance = await publicClient.readContract({
|
|
1385
|
+
address: USDC_ADDRESS,
|
|
1386
|
+
abi: [
|
|
1387
|
+
{
|
|
1388
|
+
name: "balanceOf",
|
|
1389
|
+
type: "function",
|
|
1390
|
+
stateMutability: "view",
|
|
1391
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1392
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1393
|
+
}
|
|
1394
|
+
],
|
|
1395
|
+
functionName: "balanceOf",
|
|
1396
|
+
args: [walletAddress]
|
|
1397
|
+
});
|
|
1398
|
+
if (balance < amountUnits) {
|
|
1399
|
+
const available = Number((0, import_viem3.formatUnits)(balance, 6));
|
|
1400
|
+
throw new OfframpError(
|
|
1401
|
+
`Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
|
|
1402
|
+
"VALIDATION"
|
|
1403
|
+
);
|
|
1404
|
+
}
|
|
1405
|
+
} catch (err) {
|
|
1406
|
+
if (err instanceof OfframpError) throw err;
|
|
1407
|
+
}
|
|
1408
|
+
const client = createSdkClient(walletClient);
|
|
1409
|
+
const txOverrides = { referrer: [REFERRER] };
|
|
1410
|
+
emitProgress({ step: "approving" });
|
|
1411
|
+
try {
|
|
1412
|
+
await client.ensureAllowance({
|
|
1413
|
+
token: USDC_ADDRESS,
|
|
1414
|
+
amount: amountUnits,
|
|
1415
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1416
|
+
maxApprove: false,
|
|
1417
|
+
txOverrides
|
|
1159
1418
|
});
|
|
1160
|
-
const txHash = extractTxHash2(result);
|
|
1161
|
-
onProgress?.({ step: "done", txHash, depositId: existing.depositId });
|
|
1162
|
-
return { depositId: existing.depositId, txHash, resumed: true };
|
|
1163
1419
|
} catch (err) {
|
|
1164
1420
|
if (isUserCancellation(err))
|
|
1165
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "
|
|
1421
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1422
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1166
1423
|
throw new OfframpError(
|
|
1167
|
-
|
|
1168
|
-
"
|
|
1169
|
-
"
|
|
1170
|
-
err
|
|
1171
|
-
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1424
|
+
`USDC approval failed: ${detail}`,
|
|
1425
|
+
"APPROVAL_FAILED",
|
|
1426
|
+
"approving",
|
|
1427
|
+
err
|
|
1172
1428
|
);
|
|
1173
1429
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
name
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
],
|
|
1192
|
-
functionName: "balanceOf",
|
|
1193
|
-
args: [walletAddress]
|
|
1194
|
-
});
|
|
1195
|
-
if (balance < amountUnits) {
|
|
1196
|
-
const available = Number((0, import_viem3.formatUnits)(balance, 6));
|
|
1430
|
+
emitProgress({ step: "registering" });
|
|
1431
|
+
let hashedOnchainId;
|
|
1432
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1433
|
+
try {
|
|
1434
|
+
const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
|
|
1435
|
+
hashedOnchainId = await registerPayeeDetails(canonicalName, payeePayload);
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
const status = err instanceof MakersCreateError ? err.status : null;
|
|
1438
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1439
|
+
if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
|
|
1440
|
+
throw new OfframpError(
|
|
1441
|
+
`${platform.name} maker is not yet registered in the Peer extension. Verify this handle in the extension, then retry.`,
|
|
1442
|
+
"EXTENSION_REGISTRATION_REQUIRED",
|
|
1443
|
+
"registering",
|
|
1444
|
+
err
|
|
1445
|
+
);
|
|
1446
|
+
}
|
|
1197
1447
|
throw new OfframpError(
|
|
1198
|
-
|
|
1199
|
-
"
|
|
1448
|
+
"Payee registration failed",
|
|
1449
|
+
"REGISTRATION_FAILED",
|
|
1450
|
+
"registering",
|
|
1451
|
+
err
|
|
1200
1452
|
);
|
|
1201
1453
|
}
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
await client.ensureAllowance({
|
|
1210
|
-
token: USDC_ADDRESS,
|
|
1211
|
-
amount: amountUnits,
|
|
1212
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1213
|
-
maxApprove: false,
|
|
1214
|
-
txOverrides
|
|
1215
|
-
});
|
|
1216
|
-
} catch (err) {
|
|
1217
|
-
if (isUserCancellation(err))
|
|
1218
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1219
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1220
|
-
throw new OfframpError(`USDC approval failed: ${detail}`, "APPROVAL_FAILED", "approving", err);
|
|
1221
|
-
}
|
|
1222
|
-
onProgress?.({ step: "registering" });
|
|
1223
|
-
let hashedOnchainId;
|
|
1224
|
-
try {
|
|
1225
|
-
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1226
|
-
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1227
|
-
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1228
|
-
} catch (err) {
|
|
1229
|
-
throw new OfframpError("Payee registration failed", "REGISTRATION_FAILED", "registering", err);
|
|
1230
|
-
}
|
|
1231
|
-
onProgress?.({ step: "depositing" });
|
|
1232
|
-
const conversionRates = [
|
|
1233
|
-
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1234
|
-
];
|
|
1235
|
-
const baseCurrenciesOverride = (0, import_sdk5.mapConversionRatesToOnchainMinRate)(conversionRates, 1);
|
|
1236
|
-
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1237
|
-
let hash;
|
|
1238
|
-
try {
|
|
1239
|
-
const result = await client.createDeposit({
|
|
1240
|
-
token: USDC_ADDRESS,
|
|
1241
|
-
amount: amountUnits,
|
|
1242
|
-
retainOnEmpty: false,
|
|
1243
|
-
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1244
|
-
processorNames: [platformId],
|
|
1245
|
-
conversionRates,
|
|
1246
|
-
payeeDetailsHashes: [hashedOnchainId],
|
|
1247
|
-
paymentMethodsOverride: [methodHash],
|
|
1248
|
-
paymentMethodDataOverride: [
|
|
1249
|
-
{
|
|
1250
|
-
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1251
|
-
payeeDetails: hashedOnchainId,
|
|
1252
|
-
data: "0x"
|
|
1253
|
-
}
|
|
1254
|
-
],
|
|
1255
|
-
currenciesOverride,
|
|
1256
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1257
|
-
txOverrides
|
|
1258
|
-
});
|
|
1259
|
-
if (!result?.hash) throw new Error("No transaction hash returned");
|
|
1260
|
-
hash = result.hash;
|
|
1261
|
-
} catch (err) {
|
|
1262
|
-
if (isUserCancellation(err))
|
|
1263
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1264
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1265
|
-
throw new OfframpError(
|
|
1266
|
-
`Deposit transaction failed: ${detail}`,
|
|
1267
|
-
"DEPOSIT_FAILED",
|
|
1268
|
-
"depositing",
|
|
1269
|
-
err
|
|
1270
|
-
);
|
|
1271
|
-
}
|
|
1272
|
-
onProgress?.({ step: "confirming", txHash: hash });
|
|
1273
|
-
let depositId = "";
|
|
1274
|
-
const receiptClient = client;
|
|
1275
|
-
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1454
|
+
emitProgress({ step: "depositing" });
|
|
1455
|
+
const conversionRates = [
|
|
1456
|
+
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1457
|
+
];
|
|
1458
|
+
const baseCurrenciesOverride = (0, import_sdk5.mapConversionRatesToOnchainMinRate)(conversionRates, 1);
|
|
1459
|
+
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1460
|
+
let hash;
|
|
1276
1461
|
try {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1462
|
+
const result2 = await client.createDeposit({
|
|
1463
|
+
token: USDC_ADDRESS,
|
|
1464
|
+
amount: amountUnits,
|
|
1465
|
+
retainOnEmpty: false,
|
|
1466
|
+
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1467
|
+
processorNames: [platformId],
|
|
1468
|
+
conversionRates,
|
|
1469
|
+
payeeDetailsHashes: [hashedOnchainId],
|
|
1470
|
+
paymentMethodsOverride: [methodHash],
|
|
1471
|
+
paymentMethodDataOverride: [
|
|
1472
|
+
{
|
|
1473
|
+
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1474
|
+
payeeDetails: hashedOnchainId,
|
|
1475
|
+
data: "0x"
|
|
1476
|
+
}
|
|
1477
|
+
],
|
|
1478
|
+
currenciesOverride,
|
|
1479
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1480
|
+
txOverrides
|
|
1481
|
+
});
|
|
1482
|
+
if (!result2?.hash) throw new Error("No transaction hash returned");
|
|
1483
|
+
hash = result2.hash;
|
|
1484
|
+
} catch (err) {
|
|
1485
|
+
if (isUserCancellation(err))
|
|
1486
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1487
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1488
|
+
throw new OfframpError(
|
|
1489
|
+
`Deposit transaction failed: ${detail}`,
|
|
1490
|
+
"DEPOSIT_FAILED",
|
|
1491
|
+
"depositing",
|
|
1492
|
+
err
|
|
1493
|
+
);
|
|
1280
1494
|
}
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1495
|
+
emitProgress({ step: "confirming", txHash: hash });
|
|
1496
|
+
let depositId = "";
|
|
1497
|
+
const receiptClient = client;
|
|
1498
|
+
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1285
1499
|
try {
|
|
1286
|
-
const
|
|
1287
|
-
|
|
1288
|
-
{ limit: 25 }
|
|
1289
|
-
);
|
|
1290
|
-
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1291
|
-
if (hit) {
|
|
1292
|
-
depositId = String(hit.depositId);
|
|
1293
|
-
break;
|
|
1294
|
-
}
|
|
1500
|
+
const receipt = await receiptClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1501
|
+
depositId = extractDepositIdFromLogs(receipt.logs) || "";
|
|
1295
1502
|
} catch {
|
|
1296
1503
|
}
|
|
1297
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
1298
|
-
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1299
1504
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
if (isUserCancellation(delegationError)) {
|
|
1505
|
+
if (!depositId) {
|
|
1506
|
+
let delay = INDEXER_INITIAL_DELAY_MS;
|
|
1507
|
+
for (let attempt = 0; attempt < INDEXER_MAX_ATTEMPTS && !depositId; attempt++) {
|
|
1508
|
+
try {
|
|
1509
|
+
const deps = await client.indexer.getDepositsWithRelations(
|
|
1510
|
+
{ depositor: walletAddress },
|
|
1511
|
+
{ limit: 25 }
|
|
1512
|
+
);
|
|
1513
|
+
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1514
|
+
if (hit) {
|
|
1515
|
+
depositId = String(hit.depositId);
|
|
1516
|
+
break;
|
|
1517
|
+
}
|
|
1518
|
+
} catch {
|
|
1519
|
+
}
|
|
1520
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1521
|
+
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
if (!depositId) {
|
|
1321
1525
|
throw new OfframpError(
|
|
1322
|
-
"
|
|
1323
|
-
"
|
|
1324
|
-
"
|
|
1325
|
-
|
|
1326
|
-
{ txHash: hash
|
|
1526
|
+
"Deposit created on-chain but could not confirm deposit ID. Your funds are safe. Call offramp() again to resume delegation.",
|
|
1527
|
+
"CONFIRMATION_FAILED",
|
|
1528
|
+
"confirming",
|
|
1529
|
+
void 0,
|
|
1530
|
+
{ txHash: hash }
|
|
1327
1531
|
);
|
|
1328
1532
|
}
|
|
1329
|
-
|
|
1330
|
-
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1331
|
-
"DELEGATION_FAILED",
|
|
1332
|
-
"delegating",
|
|
1333
|
-
delegationError,
|
|
1334
|
-
{ txHash: hash, depositId }
|
|
1335
|
-
);
|
|
1336
|
-
}
|
|
1337
|
-
let otcLink;
|
|
1338
|
-
if (params.otcTaker) {
|
|
1339
|
-
onProgress?.({ step: "restricting", txHash: hash, depositId });
|
|
1533
|
+
emitProgress({ step: "delegating", txHash: hash, depositId });
|
|
1340
1534
|
try {
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1535
|
+
await client.setRateManager({
|
|
1536
|
+
depositId: BigInt(depositId),
|
|
1537
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1538
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1539
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1540
|
+
txOverrides
|
|
1541
|
+
});
|
|
1542
|
+
} catch (delegationError) {
|
|
1543
|
+
if (isUserCancellation(delegationError)) {
|
|
1345
1544
|
throw new OfframpError(
|
|
1346
|
-
"User cancelled
|
|
1545
|
+
"User cancelled delegation",
|
|
1347
1546
|
"USER_CANCELLED",
|
|
1348
|
-
"
|
|
1349
|
-
|
|
1350
|
-
{
|
|
1351
|
-
txHash: hash,
|
|
1352
|
-
depositId
|
|
1353
|
-
}
|
|
1547
|
+
"delegating",
|
|
1548
|
+
delegationError,
|
|
1549
|
+
{ txHash: hash, depositId }
|
|
1354
1550
|
);
|
|
1355
1551
|
}
|
|
1356
1552
|
throw new OfframpError(
|
|
1357
|
-
"Deposit created
|
|
1358
|
-
"
|
|
1359
|
-
"
|
|
1360
|
-
|
|
1553
|
+
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1554
|
+
"DELEGATION_FAILED",
|
|
1555
|
+
"delegating",
|
|
1556
|
+
delegationError,
|
|
1361
1557
|
{ txHash: hash, depositId }
|
|
1362
1558
|
);
|
|
1363
1559
|
}
|
|
1560
|
+
let otcLink;
|
|
1561
|
+
if (params.otcTaker) {
|
|
1562
|
+
emitProgress({ step: "restricting", txHash: hash, depositId });
|
|
1563
|
+
try {
|
|
1564
|
+
const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
|
|
1565
|
+
otcLink = otcResult.otcLink;
|
|
1566
|
+
} catch (otcError) {
|
|
1567
|
+
if (isUserCancellation(otcError)) {
|
|
1568
|
+
throw new OfframpError(
|
|
1569
|
+
"User cancelled OTC restriction",
|
|
1570
|
+
"USER_CANCELLED",
|
|
1571
|
+
"restricting",
|
|
1572
|
+
otcError,
|
|
1573
|
+
{
|
|
1574
|
+
txHash: hash,
|
|
1575
|
+
depositId
|
|
1576
|
+
}
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
throw new OfframpError(
|
|
1580
|
+
"Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
|
|
1581
|
+
"DEPOSIT_FAILED",
|
|
1582
|
+
"restricting",
|
|
1583
|
+
otcError,
|
|
1584
|
+
{ txHash: hash, depositId }
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
emitProgress({ step: "done", txHash: hash, depositId });
|
|
1589
|
+
const result = { depositId, txHash: hash, resumed: false, otcLink };
|
|
1590
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1591
|
+
depositId: result.depositId,
|
|
1592
|
+
txHash: result.txHash,
|
|
1593
|
+
wallet: walletAddress,
|
|
1594
|
+
resumed: false,
|
|
1595
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1596
|
+
});
|
|
1597
|
+
return result;
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
const known = error instanceof OfframpError ? error : null;
|
|
1600
|
+
telemetry.emit("sdk.createDeposit.error", {
|
|
1601
|
+
code: known?.code ?? "DEPOSIT_FAILED",
|
|
1602
|
+
step: known?.step ?? "unknown"
|
|
1603
|
+
});
|
|
1604
|
+
throw error;
|
|
1364
1605
|
}
|
|
1365
|
-
onProgress?.({ step: "done", txHash: hash, depositId });
|
|
1366
|
-
return { depositId, txHash: hash, resumed: false, otcLink };
|
|
1367
1606
|
}
|
|
1368
1607
|
|
|
1369
1608
|
// src/currencies.ts
|
|
@@ -1383,19 +1622,213 @@ function buildCurrencies() {
|
|
|
1383
1622
|
return entries;
|
|
1384
1623
|
}
|
|
1385
1624
|
var CURRENCIES = buildCurrencies();
|
|
1625
|
+
|
|
1626
|
+
// src/offramp.ts
|
|
1627
|
+
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1628
|
+
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1629
|
+
function getWalletAddress(walletClient) {
|
|
1630
|
+
const address = walletClient.account?.address;
|
|
1631
|
+
if (!address) {
|
|
1632
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1633
|
+
}
|
|
1634
|
+
return address.toLowerCase();
|
|
1635
|
+
}
|
|
1636
|
+
function getIdempotencyStorageKey(walletAddress, idempotencyKey) {
|
|
1637
|
+
return `${IDEMPOTENCY_STORAGE_PREFIX}${walletAddress}:${idempotencyKey}`;
|
|
1638
|
+
}
|
|
1639
|
+
function readIdempotencyResult(storageKey) {
|
|
1640
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1641
|
+
try {
|
|
1642
|
+
const raw = sessionStorage.getItem(storageKey);
|
|
1643
|
+
if (!raw) return null;
|
|
1644
|
+
const parsed = JSON.parse(raw);
|
|
1645
|
+
const expiresAt = typeof parsed.expiresAt === "number" && Number.isFinite(parsed.expiresAt) ? parsed.expiresAt : 0;
|
|
1646
|
+
if (expiresAt <= Date.now()) {
|
|
1647
|
+
sessionStorage.removeItem(storageKey);
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1650
|
+
const result = parsed.result;
|
|
1651
|
+
if (!result?.depositId || !result.txHash) {
|
|
1652
|
+
sessionStorage.removeItem(storageKey);
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
return result;
|
|
1656
|
+
} catch {
|
|
1657
|
+
return null;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
function writeIdempotencyResult(storageKey, result) {
|
|
1661
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1662
|
+
try {
|
|
1663
|
+
const record = {
|
|
1664
|
+
result,
|
|
1665
|
+
expiresAt: Date.now() + IDEMPOTENCY_TTL_MS
|
|
1666
|
+
};
|
|
1667
|
+
sessionStorage.setItem(storageKey, JSON.stringify(record));
|
|
1668
|
+
} catch {
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
function createPerFlowTelemetry(base4, integratorId, referralId) {
|
|
1672
|
+
return createTelemetryContext({
|
|
1673
|
+
sdkVersion: base4.sdkVersion,
|
|
1674
|
+
telemetry: base4.enabled,
|
|
1675
|
+
integratorId: sanitizeAttributionId(integratorId) ?? base4.integratorId,
|
|
1676
|
+
referralId: sanitizeAttributionId(referralId) ?? base4.referralId
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
function normalizeCurrencyEntry(code) {
|
|
1680
|
+
const fromCatalog = CURRENCIES[code];
|
|
1681
|
+
if (fromCatalog) return fromCatalog;
|
|
1682
|
+
return {
|
|
1683
|
+
code,
|
|
1684
|
+
name: code,
|
|
1685
|
+
symbol: code,
|
|
1686
|
+
countryCode: ""
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
function buildCurrencyGroups() {
|
|
1690
|
+
const grouped = {};
|
|
1691
|
+
for (const platform of Object.values(PLATFORMS)) {
|
|
1692
|
+
grouped[platform.id] = platform.currencies.map(normalizeCurrencyEntry);
|
|
1693
|
+
}
|
|
1694
|
+
return grouped;
|
|
1695
|
+
}
|
|
1696
|
+
var Offramp = class {
|
|
1697
|
+
walletClient;
|
|
1698
|
+
telemetry;
|
|
1699
|
+
constructor(options) {
|
|
1700
|
+
this.walletClient = options.walletClient;
|
|
1701
|
+
this.telemetry = createTelemetryContext({
|
|
1702
|
+
sdkVersion: SDK_VERSION,
|
|
1703
|
+
telemetry: options.telemetry,
|
|
1704
|
+
integratorId: options.integratorId,
|
|
1705
|
+
referralId: options.referralId
|
|
1706
|
+
});
|
|
1707
|
+
this.telemetry.emit("sdk.init", {
|
|
1708
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown"
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
async createDeposit(params, onProgress) {
|
|
1712
|
+
const flowTelemetry = createPerFlowTelemetry(
|
|
1713
|
+
this.telemetry,
|
|
1714
|
+
params.integratorId,
|
|
1715
|
+
params.referralId
|
|
1716
|
+
);
|
|
1717
|
+
const sanitizedParams = {
|
|
1718
|
+
...params,
|
|
1719
|
+
integratorId: flowTelemetry.integratorId,
|
|
1720
|
+
referralId: flowTelemetry.referralId
|
|
1721
|
+
};
|
|
1722
|
+
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1723
|
+
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1724
|
+
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1725
|
+
if (storageKey) {
|
|
1726
|
+
const existing = readIdempotencyResult(storageKey);
|
|
1727
|
+
if (existing) return existing;
|
|
1728
|
+
}
|
|
1729
|
+
const result = await offramp(
|
|
1730
|
+
this.walletClient,
|
|
1731
|
+
sanitizedParams,
|
|
1732
|
+
onProgress,
|
|
1733
|
+
flowTelemetry
|
|
1734
|
+
);
|
|
1735
|
+
if (storageKey) {
|
|
1736
|
+
writeIdempotencyResult(storageKey, result);
|
|
1737
|
+
}
|
|
1738
|
+
return result;
|
|
1739
|
+
}
|
|
1740
|
+
getQuote(input) {
|
|
1741
|
+
const amountUsdc = Number(input.amount);
|
|
1742
|
+
if (!Number.isFinite(amountUsdc) || amountUsdc <= 0) {
|
|
1743
|
+
throw new OfframpError("Amount must be a positive number", "VALIDATION");
|
|
1744
|
+
}
|
|
1745
|
+
if (!input.platform.currencies.includes(input.currency.code)) {
|
|
1746
|
+
throw new OfframpError(
|
|
1747
|
+
`${input.currency.code} is not supported on ${input.platform.name}`,
|
|
1748
|
+
"UNSUPPORTED"
|
|
1749
|
+
);
|
|
1750
|
+
}
|
|
1751
|
+
const vaultSpreadBps = 0;
|
|
1752
|
+
const spreadFactor = Math.max(0, 1 - vaultSpreadBps / 1e4);
|
|
1753
|
+
const expectedFiat = amountUsdc * spreadFactor;
|
|
1754
|
+
const effectiveRate = amountUsdc === 0 ? 0 : expectedFiat / amountUsdc;
|
|
1755
|
+
return {
|
|
1756
|
+
amountUsdc,
|
|
1757
|
+
expectedFiat,
|
|
1758
|
+
effectiveRate,
|
|
1759
|
+
vaultSpreadBps
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
getVaultStatus() {
|
|
1763
|
+
return {
|
|
1764
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1765
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1766
|
+
feeRateBps: DELEGATE_MANAGER_FEE_BPS,
|
|
1767
|
+
escrow: ESCROW_ADDRESS,
|
|
1768
|
+
isActive: true
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
listCurrencies() {
|
|
1772
|
+
return buildCurrencyGroups();
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
function createOfframp(options) {
|
|
1776
|
+
return new Offramp(options);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// src/types.ts
|
|
1780
|
+
var OFFRAMP_ERROR_CODES = {
|
|
1781
|
+
VALIDATION: "VALIDATION",
|
|
1782
|
+
APPROVAL_FAILED: "APPROVAL_FAILED",
|
|
1783
|
+
REGISTRATION_FAILED: "REGISTRATION_FAILED",
|
|
1784
|
+
/**
|
|
1785
|
+
* Curator rejected the maker because the user has not registered this
|
|
1786
|
+
* handle in the Peer extension yet. Thrown from `offramp()` for PayPal
|
|
1787
|
+
* and Wise when `/v2/makers/create` returns 400 or a "creating the maker"
|
|
1788
|
+
* message. Recover by prompting the user through the Peer extension via
|
|
1789
|
+
* `usePeerExtensionRegistration(platform)` (React) or `peerExtensionSdk`
|
|
1790
|
+
* directly, then call `offramp()` again.
|
|
1791
|
+
*/
|
|
1792
|
+
EXTENSION_REGISTRATION_REQUIRED: "EXTENSION_REGISTRATION_REQUIRED",
|
|
1793
|
+
DEPOSIT_FAILED: "DEPOSIT_FAILED",
|
|
1794
|
+
CONFIRMATION_FAILED: "CONFIRMATION_FAILED",
|
|
1795
|
+
DELEGATION_FAILED: "DELEGATION_FAILED",
|
|
1796
|
+
USER_CANCELLED: "USER_CANCELLED",
|
|
1797
|
+
UNSUPPORTED: "UNSUPPORTED"
|
|
1798
|
+
};
|
|
1799
|
+
|
|
1800
|
+
// src/extension.ts
|
|
1801
|
+
var import_sdk7 = require("@zkp2p/sdk");
|
|
1386
1802
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1387
1803
|
0 && (module.exports = {
|
|
1388
1804
|
CURRENCIES,
|
|
1389
1805
|
ESCROW_ADDRESS,
|
|
1806
|
+
MakersCreateError,
|
|
1807
|
+
OFFRAMP_ERROR_CODES,
|
|
1808
|
+
Offramp,
|
|
1390
1809
|
OfframpError,
|
|
1810
|
+
PEER_EXTENSION_CHROME_URL,
|
|
1391
1811
|
PLATFORMS,
|
|
1392
1812
|
close,
|
|
1813
|
+
createOfframp,
|
|
1814
|
+
createPeerExtensionSdk,
|
|
1815
|
+
createTelemetryContext,
|
|
1393
1816
|
delegate,
|
|
1394
1817
|
deposits,
|
|
1395
1818
|
disableOtc,
|
|
1819
|
+
emitEvent,
|
|
1396
1820
|
enableOtc,
|
|
1397
1821
|
getOtcLink,
|
|
1822
|
+
getPeerExtensionRegistrationInfo,
|
|
1823
|
+
getPeerExtensionState,
|
|
1824
|
+
isPeerExtensionAvailable,
|
|
1825
|
+
isPeerExtensionRegistrationError,
|
|
1826
|
+
normalizePaypalMeUsername,
|
|
1398
1827
|
offramp,
|
|
1828
|
+
openPeerExtensionInstallPage,
|
|
1829
|
+
peerExtensionSdk,
|
|
1830
|
+
sanitizeAttributionId,
|
|
1831
|
+
sendTelemetryEvent,
|
|
1399
1832
|
undelegate
|
|
1400
1833
|
});
|
|
1401
1834
|
//# sourceMappingURL=index.cjs.map
|