@usdctofiat/offramp 1.1.4 → 1.3.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/dist/{chunk-OJY6DE3I.js → chunk-JQBKGGI2.js} +555 -213
- package/dist/chunk-JQBKGGI2.js.map +1 -0
- package/dist/{errors-Dtzrl98J.d.cts → errors-CfZrDly0.d.cts} +42 -2
- package/dist/{errors-Dtzrl98J.d.ts → errors-CfZrDly0.d.ts} +42 -2
- package/dist/index.cjs +556 -213
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -4
- package/dist/index.d.ts +37 -4
- package/dist/index.js +26 -18
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +591 -240
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +5 -2
- package/dist/react.d.ts +5 -2
- package/dist/react.js +33 -14
- package/dist/react.js.map +1 -1
- package/package.json +16 -6
- package/dist/chunk-OJY6DE3I.js.map +0 -1
|
@@ -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;
|
|
@@ -967,6 +969,108 @@ async function undelegate(walletClient, depositId, escrowAddress) {
|
|
|
967
969
|
return hash;
|
|
968
970
|
}
|
|
969
971
|
|
|
972
|
+
// src/telemetry.ts
|
|
973
|
+
var SDK_EVENTS_ENDPOINT = "https://usdctofiat.xyz/api/sdk-events";
|
|
974
|
+
var MAX_BATCH_SIZE = 50;
|
|
975
|
+
var FLUSH_DELAY_MS = 180;
|
|
976
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
977
|
+
var queuedEvents = [];
|
|
978
|
+
var flushTimer = null;
|
|
979
|
+
var flushInFlight = false;
|
|
980
|
+
function sanitizeAttributionId(raw) {
|
|
981
|
+
if (typeof raw !== "string") return void 0;
|
|
982
|
+
const stripped = raw.trim().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
983
|
+
if (!stripped) return void 0;
|
|
984
|
+
if (stripped.length > 64) return void 0;
|
|
985
|
+
return stripped;
|
|
986
|
+
}
|
|
987
|
+
function scheduleFlush() {
|
|
988
|
+
if (flushTimer != null) return;
|
|
989
|
+
flushTimer = setTimeout(() => {
|
|
990
|
+
flushTimer = null;
|
|
991
|
+
void flushQueue();
|
|
992
|
+
}, FLUSH_DELAY_MS);
|
|
993
|
+
}
|
|
994
|
+
async function postBatch(batch) {
|
|
995
|
+
if (!batch.length) return;
|
|
996
|
+
const body = JSON.stringify(batch);
|
|
997
|
+
try {
|
|
998
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
999
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
1000
|
+
const accepted = navigator.sendBeacon(SDK_EVENTS_ENDPOINT, blob);
|
|
1001
|
+
if (accepted) return;
|
|
1002
|
+
}
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
try {
|
|
1006
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
1007
|
+
const timeout = controller ? setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS) : null;
|
|
1008
|
+
await fetch(SDK_EVENTS_ENDPOINT, {
|
|
1009
|
+
method: "POST",
|
|
1010
|
+
headers: { "Content-Type": "application/json" },
|
|
1011
|
+
body,
|
|
1012
|
+
keepalive: true,
|
|
1013
|
+
signal: controller?.signal
|
|
1014
|
+
}).catch(() => {
|
|
1015
|
+
});
|
|
1016
|
+
if (timeout) clearTimeout(timeout);
|
|
1017
|
+
} catch {
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
async function flushQueue() {
|
|
1021
|
+
if (flushInFlight) return;
|
|
1022
|
+
if (!queuedEvents.length) return;
|
|
1023
|
+
flushInFlight = true;
|
|
1024
|
+
try {
|
|
1025
|
+
while (queuedEvents.length > 0) {
|
|
1026
|
+
const batch = queuedEvents.slice(0, MAX_BATCH_SIZE);
|
|
1027
|
+
queuedEvents = queuedEvents.slice(batch.length);
|
|
1028
|
+
await postBatch(batch);
|
|
1029
|
+
}
|
|
1030
|
+
} finally {
|
|
1031
|
+
flushInFlight = false;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
function emitEvent(name, payload) {
|
|
1035
|
+
try {
|
|
1036
|
+
queuedEvents.push({
|
|
1037
|
+
name,
|
|
1038
|
+
payload,
|
|
1039
|
+
ts: Date.now()
|
|
1040
|
+
});
|
|
1041
|
+
if (queuedEvents.length >= MAX_BATCH_SIZE) {
|
|
1042
|
+
void flushQueue();
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
scheduleFlush();
|
|
1046
|
+
} catch {
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
function sendTelemetryEvent(name, payload) {
|
|
1050
|
+
emitEvent(name, payload);
|
|
1051
|
+
}
|
|
1052
|
+
function createTelemetryContext(options) {
|
|
1053
|
+
const integratorId = sanitizeAttributionId(options.integratorId);
|
|
1054
|
+
const referralId = sanitizeAttributionId(options.referralId);
|
|
1055
|
+
const enabled = options.telemetry !== false;
|
|
1056
|
+
const sdkVersion = options.sdkVersion;
|
|
1057
|
+
return {
|
|
1058
|
+
enabled,
|
|
1059
|
+
sdkVersion,
|
|
1060
|
+
integratorId,
|
|
1061
|
+
referralId,
|
|
1062
|
+
emit(name, payload = {}) {
|
|
1063
|
+
if (!enabled) return;
|
|
1064
|
+
emitEvent(name, {
|
|
1065
|
+
sdkVersion,
|
|
1066
|
+
integratorId,
|
|
1067
|
+
referralId,
|
|
1068
|
+
...payload
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
|
|
970
1074
|
// src/deposit.ts
|
|
971
1075
|
import { createPublicClient as createPublicClient3, decodeEventLog, formatUnits as formatUnits2, http as http3, parseUnits } from "viem";
|
|
972
1076
|
import { base as base3 } from "viem/chains";
|
|
@@ -1090,256 +1194,487 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1090
1194
|
if (remaining === 0n) return false;
|
|
1091
1195
|
return (d.rateManagerId ?? "").toLowerCase() !== delegateIdLower;
|
|
1092
1196
|
});
|
|
1093
|
-
undelegated.sort((a, b) =>
|
|
1197
|
+
undelegated.sort((a, b) => {
|
|
1198
|
+
const left = BigInt(a.depositId);
|
|
1199
|
+
const right = BigInt(b.depositId);
|
|
1200
|
+
if (left === right) return 0;
|
|
1201
|
+
return left > right ? -1 : 1;
|
|
1202
|
+
});
|
|
1094
1203
|
return undelegated[0] ?? null;
|
|
1095
1204
|
} catch {
|
|
1096
1205
|
return null;
|
|
1097
1206
|
}
|
|
1098
1207
|
}
|
|
1099
|
-
async function offramp(walletClient, params, onProgress) {
|
|
1208
|
+
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1100
1209
|
const { amount, platform, currency, identifier } = params;
|
|
1101
1210
|
const platformId = platform.id;
|
|
1102
1211
|
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
|
-
|
|
1212
|
+
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1213
|
+
sdkVersion: SDK_VERSION,
|
|
1214
|
+
telemetry: true,
|
|
1215
|
+
integratorId: params.integratorId,
|
|
1216
|
+
referralId: params.referralId
|
|
1217
|
+
});
|
|
1218
|
+
const flowStartMs = Date.now();
|
|
1219
|
+
const emitProgress = (progress) => {
|
|
1220
|
+
onProgress?.(progress);
|
|
1221
|
+
telemetry.emit("sdk.createDeposit.progress", {
|
|
1222
|
+
step: progress.step,
|
|
1223
|
+
txHash: progress.txHash
|
|
1224
|
+
});
|
|
1225
|
+
};
|
|
1226
|
+
try {
|
|
1227
|
+
if (!walletClient.account?.address) {
|
|
1228
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1229
|
+
}
|
|
1230
|
+
const walletAddress = walletClient.account.address.toLowerCase();
|
|
1231
|
+
telemetry.emit("sdk.createDeposit.start", {
|
|
1232
|
+
platform: platformId,
|
|
1233
|
+
currency: currencyCode,
|
|
1234
|
+
amount,
|
|
1235
|
+
wallet: walletAddress
|
|
1236
|
+
});
|
|
1237
|
+
const amt = parseFloat(amount);
|
|
1238
|
+
if (!Number.isFinite(amt) || amt < MIN_DEPOSIT_USDC) {
|
|
1239
|
+
throw new OfframpError(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC`, "VALIDATION");
|
|
1240
|
+
}
|
|
1241
|
+
if (!platform.currencies.includes(currencyCode)) {
|
|
1242
|
+
throw new OfframpError(`${currencyCode} is not supported on ${platform.name}`, "UNSUPPORTED");
|
|
1243
|
+
}
|
|
1244
|
+
const validation = platform.validate(identifier);
|
|
1245
|
+
if (!validation.valid) {
|
|
1246
|
+
throw new OfframpError(validation.error || "Invalid identifier", "VALIDATION");
|
|
1247
|
+
}
|
|
1248
|
+
const normalizedIdentifier = validation.normalized;
|
|
1249
|
+
const methodHash = getPaymentMethodHash(platformId);
|
|
1250
|
+
if (!methodHash) {
|
|
1251
|
+
throw new OfframpError(`${platform.name} is not currently supported`, "UNSUPPORTED");
|
|
1252
|
+
}
|
|
1253
|
+
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1254
|
+
if (existing) {
|
|
1255
|
+
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1256
|
+
const client2 = createSdkClient(walletClient);
|
|
1257
|
+
const txOverrides2 = { referrer: [REFERRER] };
|
|
1258
|
+
try {
|
|
1259
|
+
const result2 = await client2.setRateManager({
|
|
1260
|
+
depositId: BigInt(existing.depositId),
|
|
1261
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1262
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1263
|
+
escrowAddress: existing.escrowAddress,
|
|
1264
|
+
txOverrides: txOverrides2
|
|
1265
|
+
});
|
|
1266
|
+
const txHash = extractTxHash2(result2);
|
|
1267
|
+
emitProgress({ step: "done", txHash, depositId: existing.depositId });
|
|
1268
|
+
const resumedResult = { depositId: existing.depositId, txHash, resumed: true };
|
|
1269
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1270
|
+
depositId: resumedResult.depositId,
|
|
1271
|
+
txHash: resumedResult.txHash,
|
|
1272
|
+
wallet: walletAddress,
|
|
1273
|
+
resumed: true,
|
|
1274
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1275
|
+
});
|
|
1276
|
+
return resumedResult;
|
|
1277
|
+
} catch (err) {
|
|
1278
|
+
if (isUserCancellation(err))
|
|
1279
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
|
|
1280
|
+
throw new OfframpError(
|
|
1281
|
+
"Found undelegated deposit but delegation failed. Try again.",
|
|
1282
|
+
"DELEGATION_FAILED",
|
|
1283
|
+
"resuming",
|
|
1284
|
+
err,
|
|
1285
|
+
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1290
|
+
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1291
|
+
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1292
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, 2500)));
|
|
1128
1293
|
try {
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1294
|
+
const publicClient = createPublicClient3({ chain: base3, transport: http3(BASE_RPC_URL) });
|
|
1295
|
+
const balance = await publicClient.readContract({
|
|
1296
|
+
address: USDC_ADDRESS,
|
|
1297
|
+
abi: [
|
|
1298
|
+
{
|
|
1299
|
+
name: "balanceOf",
|
|
1300
|
+
type: "function",
|
|
1301
|
+
stateMutability: "view",
|
|
1302
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1303
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1304
|
+
}
|
|
1305
|
+
],
|
|
1306
|
+
functionName: "balanceOf",
|
|
1307
|
+
args: [walletAddress]
|
|
1308
|
+
});
|
|
1309
|
+
if (balance < amountUnits) {
|
|
1310
|
+
const available = Number(formatUnits2(balance, 6));
|
|
1311
|
+
throw new OfframpError(
|
|
1312
|
+
`Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
|
|
1313
|
+
"VALIDATION"
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
} catch (err) {
|
|
1317
|
+
if (err instanceof OfframpError) throw err;
|
|
1318
|
+
}
|
|
1319
|
+
const client = createSdkClient(walletClient);
|
|
1320
|
+
const txOverrides = { referrer: [REFERRER] };
|
|
1321
|
+
emitProgress({ step: "approving" });
|
|
1322
|
+
try {
|
|
1323
|
+
await client.ensureAllowance({
|
|
1324
|
+
token: USDC_ADDRESS,
|
|
1325
|
+
amount: amountUnits,
|
|
1326
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1327
|
+
maxApprove: false,
|
|
1328
|
+
txOverrides
|
|
1135
1329
|
});
|
|
1136
|
-
const txHash = extractTxHash2(result);
|
|
1137
|
-
onProgress?.({ step: "done", txHash, depositId: existing.depositId });
|
|
1138
|
-
return { depositId: existing.depositId, txHash, resumed: true };
|
|
1139
1330
|
} catch (err) {
|
|
1140
1331
|
if (isUserCancellation(err))
|
|
1141
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "
|
|
1332
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1333
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1142
1334
|
throw new OfframpError(
|
|
1143
|
-
|
|
1144
|
-
"
|
|
1145
|
-
"
|
|
1146
|
-
err
|
|
1147
|
-
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1335
|
+
`USDC approval failed: ${detail}`,
|
|
1336
|
+
"APPROVAL_FAILED",
|
|
1337
|
+
"approving",
|
|
1338
|
+
err
|
|
1148
1339
|
);
|
|
1149
1340
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
const balance = await publicClient.readContract({
|
|
1158
|
-
address: USDC_ADDRESS,
|
|
1159
|
-
abi: [
|
|
1160
|
-
{
|
|
1161
|
-
name: "balanceOf",
|
|
1162
|
-
type: "function",
|
|
1163
|
-
stateMutability: "view",
|
|
1164
|
-
inputs: [{ name: "account", type: "address" }],
|
|
1165
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
1166
|
-
}
|
|
1167
|
-
],
|
|
1168
|
-
functionName: "balanceOf",
|
|
1169
|
-
args: [walletAddress]
|
|
1170
|
-
});
|
|
1171
|
-
if (balance < amountUnits) {
|
|
1172
|
-
const available = Number(formatUnits2(balance, 6));
|
|
1341
|
+
emitProgress({ step: "registering" });
|
|
1342
|
+
let hashedOnchainId;
|
|
1343
|
+
try {
|
|
1344
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1345
|
+
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1346
|
+
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1347
|
+
} catch (err) {
|
|
1173
1348
|
throw new OfframpError(
|
|
1174
|
-
|
|
1175
|
-
"
|
|
1349
|
+
"Payee registration failed",
|
|
1350
|
+
"REGISTRATION_FAILED",
|
|
1351
|
+
"registering",
|
|
1352
|
+
err
|
|
1176
1353
|
);
|
|
1177
1354
|
}
|
|
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") {
|
|
1355
|
+
emitProgress({ step: "depositing" });
|
|
1356
|
+
const conversionRates = [
|
|
1357
|
+
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1358
|
+
];
|
|
1359
|
+
const baseCurrenciesOverride = mapConversionRatesToOnchainMinRate(conversionRates, 1);
|
|
1360
|
+
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1361
|
+
let hash;
|
|
1252
1362
|
try {
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1363
|
+
const result2 = await client.createDeposit({
|
|
1364
|
+
token: USDC_ADDRESS,
|
|
1365
|
+
amount: amountUnits,
|
|
1366
|
+
retainOnEmpty: false,
|
|
1367
|
+
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1368
|
+
processorNames: [platformId],
|
|
1369
|
+
conversionRates,
|
|
1370
|
+
payeeDetailsHashes: [hashedOnchainId],
|
|
1371
|
+
paymentMethodsOverride: [methodHash],
|
|
1372
|
+
paymentMethodDataOverride: [
|
|
1373
|
+
{
|
|
1374
|
+
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1375
|
+
payeeDetails: hashedOnchainId,
|
|
1376
|
+
data: "0x"
|
|
1377
|
+
}
|
|
1378
|
+
],
|
|
1379
|
+
currenciesOverride,
|
|
1380
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1381
|
+
txOverrides
|
|
1382
|
+
});
|
|
1383
|
+
if (!result2?.hash) throw new Error("No transaction hash returned");
|
|
1384
|
+
hash = result2.hash;
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
if (isUserCancellation(err))
|
|
1387
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1388
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1389
|
+
throw new OfframpError(
|
|
1390
|
+
`Deposit transaction failed: ${detail}`,
|
|
1391
|
+
"DEPOSIT_FAILED",
|
|
1392
|
+
"depositing",
|
|
1393
|
+
err
|
|
1394
|
+
);
|
|
1256
1395
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1396
|
+
emitProgress({ step: "confirming", txHash: hash });
|
|
1397
|
+
let depositId = "";
|
|
1398
|
+
const receiptClient = client;
|
|
1399
|
+
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1261
1400
|
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
|
-
}
|
|
1401
|
+
const receipt = await receiptClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1402
|
+
depositId = extractDepositIdFromLogs(receipt.logs) || "";
|
|
1271
1403
|
} catch {
|
|
1272
1404
|
}
|
|
1273
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
1274
|
-
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1275
1405
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
|
|
1406
|
+
if (!depositId) {
|
|
1407
|
+
let delay = INDEXER_INITIAL_DELAY_MS;
|
|
1408
|
+
for (let attempt = 0; attempt < INDEXER_MAX_ATTEMPTS && !depositId; attempt++) {
|
|
1409
|
+
try {
|
|
1410
|
+
const deps = await client.indexer.getDepositsWithRelations(
|
|
1411
|
+
{ depositor: walletAddress },
|
|
1412
|
+
{ limit: 25 }
|
|
1413
|
+
);
|
|
1414
|
+
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1415
|
+
if (hit) {
|
|
1416
|
+
depositId = String(hit.depositId);
|
|
1417
|
+
break;
|
|
1418
|
+
}
|
|
1419
|
+
} catch {
|
|
1420
|
+
}
|
|
1421
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1422
|
+
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
if (!depositId) {
|
|
1426
|
+
throw new OfframpError(
|
|
1427
|
+
"Deposit created on-chain but could not confirm deposit ID. Your funds are safe. Call offramp() again to resume delegation.",
|
|
1428
|
+
"CONFIRMATION_FAILED",
|
|
1429
|
+
"confirming",
|
|
1430
|
+
void 0,
|
|
1431
|
+
{ txHash: hash }
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
emitProgress({ step: "delegating", txHash: hash, depositId });
|
|
1435
|
+
try {
|
|
1436
|
+
await client.setRateManager({
|
|
1437
|
+
depositId: BigInt(depositId),
|
|
1438
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1439
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1440
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1441
|
+
txOverrides
|
|
1442
|
+
});
|
|
1443
|
+
} catch (delegationError) {
|
|
1444
|
+
if (isUserCancellation(delegationError)) {
|
|
1445
|
+
throw new OfframpError(
|
|
1446
|
+
"User cancelled delegation",
|
|
1447
|
+
"USER_CANCELLED",
|
|
1448
|
+
"delegating",
|
|
1449
|
+
delegationError,
|
|
1450
|
+
{ txHash: hash, depositId }
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1297
1453
|
throw new OfframpError(
|
|
1298
|
-
"
|
|
1299
|
-
"
|
|
1454
|
+
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1455
|
+
"DELEGATION_FAILED",
|
|
1300
1456
|
"delegating",
|
|
1301
1457
|
delegationError,
|
|
1302
1458
|
{ txHash: hash, depositId }
|
|
1303
1459
|
);
|
|
1304
1460
|
}
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
"
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1461
|
+
let otcLink;
|
|
1462
|
+
if (params.otcTaker) {
|
|
1463
|
+
emitProgress({ step: "restricting", txHash: hash, depositId });
|
|
1464
|
+
try {
|
|
1465
|
+
const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
|
|
1466
|
+
otcLink = otcResult.otcLink;
|
|
1467
|
+
} catch (otcError) {
|
|
1468
|
+
if (isUserCancellation(otcError)) {
|
|
1469
|
+
throw new OfframpError(
|
|
1470
|
+
"User cancelled OTC restriction",
|
|
1471
|
+
"USER_CANCELLED",
|
|
1472
|
+
"restricting",
|
|
1473
|
+
otcError,
|
|
1474
|
+
{
|
|
1475
|
+
txHash: hash,
|
|
1476
|
+
depositId
|
|
1477
|
+
}
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1321
1480
|
throw new OfframpError(
|
|
1322
|
-
"
|
|
1323
|
-
"
|
|
1481
|
+
"Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
|
|
1482
|
+
"DEPOSIT_FAILED",
|
|
1324
1483
|
"restricting",
|
|
1325
1484
|
otcError,
|
|
1326
|
-
{
|
|
1327
|
-
txHash: hash,
|
|
1328
|
-
depositId
|
|
1329
|
-
}
|
|
1485
|
+
{ txHash: hash, depositId }
|
|
1330
1486
|
);
|
|
1331
1487
|
}
|
|
1488
|
+
}
|
|
1489
|
+
emitProgress({ step: "done", txHash: hash, depositId });
|
|
1490
|
+
const result = { depositId, txHash: hash, resumed: false, otcLink };
|
|
1491
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1492
|
+
depositId: result.depositId,
|
|
1493
|
+
txHash: result.txHash,
|
|
1494
|
+
wallet: walletAddress,
|
|
1495
|
+
resumed: false,
|
|
1496
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1497
|
+
});
|
|
1498
|
+
return result;
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
const known = error instanceof OfframpError ? error : null;
|
|
1501
|
+
telemetry.emit("sdk.createDeposit.error", {
|
|
1502
|
+
code: known?.code ?? "DEPOSIT_FAILED",
|
|
1503
|
+
step: known?.step ?? "unknown"
|
|
1504
|
+
});
|
|
1505
|
+
throw error;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// src/currencies.ts
|
|
1510
|
+
import { currencyInfo as currencyInfo2 } from "@zkp2p/sdk";
|
|
1511
|
+
function buildCurrencies() {
|
|
1512
|
+
const entries = {};
|
|
1513
|
+
for (const [code, info] of Object.entries(
|
|
1514
|
+
currencyInfo2
|
|
1515
|
+
)) {
|
|
1516
|
+
entries[code] = {
|
|
1517
|
+
code: info.currencyCode ?? code,
|
|
1518
|
+
name: info.currencyName ?? code,
|
|
1519
|
+
symbol: info.currencySymbol ?? code,
|
|
1520
|
+
countryCode: info.countryCode ?? ""
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
return entries;
|
|
1524
|
+
}
|
|
1525
|
+
var CURRENCIES = buildCurrencies();
|
|
1526
|
+
|
|
1527
|
+
// src/offramp.ts
|
|
1528
|
+
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1529
|
+
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1530
|
+
function getWalletAddress(walletClient) {
|
|
1531
|
+
const address = walletClient.account?.address;
|
|
1532
|
+
if (!address) {
|
|
1533
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1534
|
+
}
|
|
1535
|
+
return address.toLowerCase();
|
|
1536
|
+
}
|
|
1537
|
+
function getIdempotencyStorageKey(walletAddress, idempotencyKey) {
|
|
1538
|
+
return `${IDEMPOTENCY_STORAGE_PREFIX}${walletAddress}:${idempotencyKey}`;
|
|
1539
|
+
}
|
|
1540
|
+
function readIdempotencyResult(storageKey) {
|
|
1541
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1542
|
+
try {
|
|
1543
|
+
const raw = sessionStorage.getItem(storageKey);
|
|
1544
|
+
if (!raw) return null;
|
|
1545
|
+
const parsed = JSON.parse(raw);
|
|
1546
|
+
const expiresAt = typeof parsed.expiresAt === "number" && Number.isFinite(parsed.expiresAt) ? parsed.expiresAt : 0;
|
|
1547
|
+
if (expiresAt <= Date.now()) {
|
|
1548
|
+
sessionStorage.removeItem(storageKey);
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
const result = parsed.result;
|
|
1552
|
+
if (!result?.depositId || !result.txHash) {
|
|
1553
|
+
sessionStorage.removeItem(storageKey);
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
return result;
|
|
1557
|
+
} catch {
|
|
1558
|
+
return null;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
function writeIdempotencyResult(storageKey, result) {
|
|
1562
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1563
|
+
try {
|
|
1564
|
+
const record = {
|
|
1565
|
+
result,
|
|
1566
|
+
expiresAt: Date.now() + IDEMPOTENCY_TTL_MS
|
|
1567
|
+
};
|
|
1568
|
+
sessionStorage.setItem(storageKey, JSON.stringify(record));
|
|
1569
|
+
} catch {
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
function createPerFlowTelemetry(base4, integratorId, referralId) {
|
|
1573
|
+
return createTelemetryContext({
|
|
1574
|
+
sdkVersion: base4.sdkVersion,
|
|
1575
|
+
telemetry: base4.enabled,
|
|
1576
|
+
integratorId: sanitizeAttributionId(integratorId) ?? base4.integratorId,
|
|
1577
|
+
referralId: sanitizeAttributionId(referralId) ?? base4.referralId
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
function normalizeCurrencyEntry(code) {
|
|
1581
|
+
const fromCatalog = CURRENCIES[code];
|
|
1582
|
+
if (fromCatalog) return fromCatalog;
|
|
1583
|
+
return {
|
|
1584
|
+
code,
|
|
1585
|
+
name: code,
|
|
1586
|
+
symbol: code,
|
|
1587
|
+
countryCode: ""
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function buildCurrencyGroups() {
|
|
1591
|
+
const grouped = {};
|
|
1592
|
+
for (const platform of Object.values(PLATFORMS)) {
|
|
1593
|
+
grouped[platform.id] = platform.currencies.map(normalizeCurrencyEntry);
|
|
1594
|
+
}
|
|
1595
|
+
return grouped;
|
|
1596
|
+
}
|
|
1597
|
+
var Offramp = class {
|
|
1598
|
+
walletClient;
|
|
1599
|
+
telemetry;
|
|
1600
|
+
constructor(options) {
|
|
1601
|
+
this.walletClient = options.walletClient;
|
|
1602
|
+
this.telemetry = createTelemetryContext({
|
|
1603
|
+
sdkVersion: SDK_VERSION,
|
|
1604
|
+
telemetry: options.telemetry,
|
|
1605
|
+
integratorId: options.integratorId,
|
|
1606
|
+
referralId: options.referralId
|
|
1607
|
+
});
|
|
1608
|
+
this.telemetry.emit("sdk.init", {
|
|
1609
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown"
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
async createDeposit(params, onProgress) {
|
|
1613
|
+
const flowTelemetry = createPerFlowTelemetry(
|
|
1614
|
+
this.telemetry,
|
|
1615
|
+
params.integratorId,
|
|
1616
|
+
params.referralId
|
|
1617
|
+
);
|
|
1618
|
+
const sanitizedParams = {
|
|
1619
|
+
...params,
|
|
1620
|
+
integratorId: flowTelemetry.integratorId,
|
|
1621
|
+
referralId: flowTelemetry.referralId
|
|
1622
|
+
};
|
|
1623
|
+
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1624
|
+
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1625
|
+
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1626
|
+
if (storageKey) {
|
|
1627
|
+
const existing = readIdempotencyResult(storageKey);
|
|
1628
|
+
if (existing) return existing;
|
|
1629
|
+
}
|
|
1630
|
+
const result = await offramp(
|
|
1631
|
+
this.walletClient,
|
|
1632
|
+
sanitizedParams,
|
|
1633
|
+
onProgress,
|
|
1634
|
+
flowTelemetry
|
|
1635
|
+
);
|
|
1636
|
+
if (storageKey) {
|
|
1637
|
+
writeIdempotencyResult(storageKey, result);
|
|
1638
|
+
}
|
|
1639
|
+
return result;
|
|
1640
|
+
}
|
|
1641
|
+
getQuote(input) {
|
|
1642
|
+
const amountUsdc = Number(input.amount);
|
|
1643
|
+
if (!Number.isFinite(amountUsdc) || amountUsdc <= 0) {
|
|
1644
|
+
throw new OfframpError("Amount must be a positive number", "VALIDATION");
|
|
1645
|
+
}
|
|
1646
|
+
if (!input.platform.currencies.includes(input.currency.code)) {
|
|
1332
1647
|
throw new OfframpError(
|
|
1333
|
-
|
|
1334
|
-
"
|
|
1335
|
-
"restricting",
|
|
1336
|
-
otcError,
|
|
1337
|
-
{ txHash: hash, depositId }
|
|
1648
|
+
`${input.currency.code} is not supported on ${input.platform.name}`,
|
|
1649
|
+
"UNSUPPORTED"
|
|
1338
1650
|
);
|
|
1339
1651
|
}
|
|
1652
|
+
const vaultSpreadBps = 0;
|
|
1653
|
+
const spreadFactor = Math.max(0, 1 - vaultSpreadBps / 1e4);
|
|
1654
|
+
const expectedFiat = amountUsdc * spreadFactor;
|
|
1655
|
+
const effectiveRate = amountUsdc === 0 ? 0 : expectedFiat / amountUsdc;
|
|
1656
|
+
return {
|
|
1657
|
+
amountUsdc,
|
|
1658
|
+
expectedFiat,
|
|
1659
|
+
effectiveRate,
|
|
1660
|
+
vaultSpreadBps
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
getVaultStatus() {
|
|
1664
|
+
return {
|
|
1665
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1666
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1667
|
+
feeRateBps: DELEGATE_MANAGER_FEE_BPS,
|
|
1668
|
+
escrow: ESCROW_ADDRESS,
|
|
1669
|
+
isActive: true
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
listCurrencies() {
|
|
1673
|
+
return buildCurrencyGroups();
|
|
1340
1674
|
}
|
|
1341
|
-
|
|
1342
|
-
|
|
1675
|
+
};
|
|
1676
|
+
function createOfframp(options) {
|
|
1677
|
+
return new Offramp(options);
|
|
1343
1678
|
}
|
|
1344
1679
|
|
|
1345
1680
|
export {
|
|
@@ -1353,6 +1688,13 @@ export {
|
|
|
1353
1688
|
close,
|
|
1354
1689
|
delegate,
|
|
1355
1690
|
undelegate,
|
|
1356
|
-
|
|
1691
|
+
sanitizeAttributionId,
|
|
1692
|
+
emitEvent,
|
|
1693
|
+
sendTelemetryEvent,
|
|
1694
|
+
createTelemetryContext,
|
|
1695
|
+
offramp,
|
|
1696
|
+
CURRENCIES,
|
|
1697
|
+
Offramp,
|
|
1698
|
+
createOfframp
|
|
1357
1699
|
};
|
|
1358
|
-
//# sourceMappingURL=chunk-
|
|
1700
|
+
//# sourceMappingURL=chunk-JQBKGGI2.js.map
|