@usdctofiat/offramp 1.1.3 → 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/README.md +1 -1
- package/dist/{chunk-QELK2M73.js → chunk-JQBKGGI2.js} +738 -338
- 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 +657 -259
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -4
- package/dist/index.d.ts +53 -4
- package/dist/index.js +34 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +608 -271
- 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-QELK2M73.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/react.ts
|
|
@@ -37,13 +27,9 @@ module.exports = __toCommonJS(react_exports);
|
|
|
37
27
|
// src/hooks/useOfframp.ts
|
|
38
28
|
var import_react = require("react");
|
|
39
29
|
|
|
40
|
-
// src/deposit.ts
|
|
41
|
-
var import_viem3 = require("viem");
|
|
42
|
-
var import_chains = require("viem/chains");
|
|
43
|
-
var import_sdk4 = require("@zkp2p/sdk");
|
|
44
|
-
|
|
45
30
|
// src/config.ts
|
|
46
31
|
var import_sdk = require("@zkp2p/sdk");
|
|
32
|
+
var SDK_VERSION = "1.2.0";
|
|
47
33
|
var BASE_CHAIN_ID = 8453;
|
|
48
34
|
var RUNTIME_ENV = "production";
|
|
49
35
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
@@ -59,6 +45,7 @@ var GATING_SERVICE_ADDRESS = (0, import_sdk.getGatingServiceAddress)(
|
|
|
59
45
|
);
|
|
60
46
|
var DELEGATE_RATE_MANAGER_ID = "0x8666d6fb0f6797c56e95339fd7ca82fdd348b9db200e10a4c4aa0a0b879fc41c";
|
|
61
47
|
var RATE_MANAGER_REGISTRY_ADDRESS = "0xeed7db23e724ac4590d6db6f78fda6db203535f3";
|
|
48
|
+
var DELEGATE_MANAGER_FEE_BPS = 10;
|
|
62
49
|
var REFERRER = "galleonlabs";
|
|
63
50
|
var MIN_DEPOSIT_USDC = 1;
|
|
64
51
|
var MIN_ORDER_USDC = 1;
|
|
@@ -67,10 +54,33 @@ var INDEXER_INITIAL_DELAY_MS = 1e3;
|
|
|
67
54
|
var INDEXER_MAX_DELAY_MS = 1e4;
|
|
68
55
|
var PAYEE_REGISTRATION_TIMEOUT_MS = 8e3;
|
|
69
56
|
|
|
70
|
-
// src/
|
|
57
|
+
// src/currencies.ts
|
|
71
58
|
var import_sdk2 = require("@zkp2p/sdk");
|
|
59
|
+
function buildCurrencies() {
|
|
60
|
+
const entries = {};
|
|
61
|
+
for (const [code, info] of Object.entries(
|
|
62
|
+
import_sdk2.currencyInfo
|
|
63
|
+
)) {
|
|
64
|
+
entries[code] = {
|
|
65
|
+
code: info.currencyCode ?? code,
|
|
66
|
+
name: info.currencyName ?? code,
|
|
67
|
+
symbol: info.currencySymbol ?? code,
|
|
68
|
+
countryCode: info.countryCode ?? ""
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return entries;
|
|
72
|
+
}
|
|
73
|
+
var CURRENCIES = buildCurrencies();
|
|
74
|
+
|
|
75
|
+
// src/deposit.ts
|
|
76
|
+
var import_viem3 = require("viem");
|
|
77
|
+
var import_chains3 = require("viem/chains");
|
|
78
|
+
var import_sdk6 = require("@zkp2p/sdk");
|
|
79
|
+
|
|
80
|
+
// src/platforms.ts
|
|
81
|
+
var import_sdk3 = require("@zkp2p/sdk");
|
|
72
82
|
var import_zod = require("zod");
|
|
73
|
-
var PAYMENT_CATALOG = (0,
|
|
83
|
+
var PAYMENT_CATALOG = (0, import_sdk3.getPaymentMethodsCatalog)(BASE_CHAIN_ID, RUNTIME_ENV);
|
|
74
84
|
var ZELLE_HASH_LOOKUP_NAMES = ["zelle", "zelle-bofa", "zelle-chase", "zelle-citi"];
|
|
75
85
|
var FALLBACK_CURRENCIES = {
|
|
76
86
|
venmo: ["USD"],
|
|
@@ -149,8 +159,8 @@ function gatherCatalogHashes(platform) {
|
|
|
149
159
|
function resolveSupportedCurrencies(platform) {
|
|
150
160
|
const codes = /* @__PURE__ */ new Set();
|
|
151
161
|
for (const hash of gatherCatalogHashes(platform)) {
|
|
152
|
-
const info = (0,
|
|
153
|
-
if (info?.currencyCode &&
|
|
162
|
+
const info = (0, import_sdk3.getCurrencyInfoFromHash)(hash);
|
|
163
|
+
if (info?.currencyCode && import_sdk3.currencyInfo[info.currencyCode]) {
|
|
154
164
|
codes.add(info.currencyCode);
|
|
155
165
|
}
|
|
156
166
|
}
|
|
@@ -310,7 +320,7 @@ function getPaymentMethodHash(platform) {
|
|
|
310
320
|
const catalogEntry = PAYMENT_CATALOG[name];
|
|
311
321
|
if (catalogEntry?.paymentMethodHash) return catalogEntry.paymentMethodHash;
|
|
312
322
|
if (name === "zelle" || name.startsWith("zelle-")) return resolveCanonicalZelleHash();
|
|
313
|
-
const sdkHash = (0,
|
|
323
|
+
const sdkHash = (0, import_sdk3.resolvePaymentMethodHash)(name);
|
|
314
324
|
return sdkHash ? sdkHash : null;
|
|
315
325
|
}
|
|
316
326
|
function getPaymentMethodHashes(platform) {
|
|
@@ -319,7 +329,7 @@ function getPaymentMethodHashes(platform) {
|
|
|
319
329
|
return hash ? [hash] : [];
|
|
320
330
|
}
|
|
321
331
|
const hashes = /* @__PURE__ */ new Set();
|
|
322
|
-
const generic = (0,
|
|
332
|
+
const generic = (0, import_sdk3.resolvePaymentMethodHash)("zelle");
|
|
323
333
|
if (generic) hashes.add(generic);
|
|
324
334
|
const canonical = resolveCanonicalZelleHash();
|
|
325
335
|
if (canonical) hashes.add(canonical);
|
|
@@ -382,6 +392,7 @@ function isUserCancellation(error) {
|
|
|
382
392
|
|
|
383
393
|
// src/otc.ts
|
|
384
394
|
var import_viem = require("viem");
|
|
395
|
+
var import_chains = require("viem/chains");
|
|
385
396
|
|
|
386
397
|
// ../../node_modules/@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json
|
|
387
398
|
var WhitelistPreIntentHook_default = [
|
|
@@ -726,6 +737,18 @@ var WhitelistPreIntentHook_default = [
|
|
|
726
737
|
}
|
|
727
738
|
];
|
|
728
739
|
|
|
740
|
+
// src/sdk-client.ts
|
|
741
|
+
var import_sdk4 = require("@zkp2p/sdk");
|
|
742
|
+
function createSdkClient(walletClient) {
|
|
743
|
+
return new import_sdk4.OfframpClient({
|
|
744
|
+
walletClient,
|
|
745
|
+
chainId: BASE_CHAIN_ID,
|
|
746
|
+
runtimeEnv: RUNTIME_ENV,
|
|
747
|
+
rpcUrl: BASE_RPC_URL,
|
|
748
|
+
baseApiUrl: API_BASE_URL
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
|
|
729
752
|
// src/otc.ts
|
|
730
753
|
var WHITELIST_HOOK_ADDRESS = "0xda023Ea0d789A41BcF5866F7B6BBd2CaDF9b79B8";
|
|
731
754
|
var OTC_BASE_URL = "https://usdctofiat.xyz";
|
|
@@ -743,12 +766,7 @@ async function enableOtc(walletClient, depositId, takerAddress, escrowAddress) {
|
|
|
743
766
|
escrowAddress: escrow
|
|
744
767
|
});
|
|
745
768
|
if (currentHook !== import_viem.zeroAddress) {
|
|
746
|
-
const isAlready = await readIsWhitelisted(
|
|
747
|
-
walletClient,
|
|
748
|
-
escrow,
|
|
749
|
-
depositIdBig,
|
|
750
|
-
taker
|
|
751
|
-
);
|
|
769
|
+
const isAlready = await readIsWhitelisted(escrow, depositIdBig, taker);
|
|
752
770
|
if (isAlready) {
|
|
753
771
|
return {
|
|
754
772
|
depositId,
|
|
@@ -798,13 +816,8 @@ async function writeContract(walletClient, functionName, args) {
|
|
|
798
816
|
args
|
|
799
817
|
});
|
|
800
818
|
}
|
|
801
|
-
async function readIsWhitelisted(
|
|
802
|
-
const {
|
|
803
|
-
const { base: base2 } = await import("viem/chains");
|
|
804
|
-
const publicClient = createPublicClient2({
|
|
805
|
-
chain: base2,
|
|
806
|
-
transport: http2("https://mainnet.base.org")
|
|
807
|
-
});
|
|
819
|
+
async function readIsWhitelisted(escrow, depositId, taker) {
|
|
820
|
+
const publicClient = (0, import_viem.createPublicClient)({ chain: import_chains.base, transport: (0, import_viem.http)(BASE_RPC_URL) });
|
|
808
821
|
return publicClient.readContract({
|
|
809
822
|
address: WHITELIST_HOOK_ADDRESS,
|
|
810
823
|
abi: WhitelistPreIntentHook_default,
|
|
@@ -815,13 +828,14 @@ async function readIsWhitelisted(walletClient, escrow, depositId, taker) {
|
|
|
815
828
|
|
|
816
829
|
// src/queries.ts
|
|
817
830
|
var import_viem2 = require("viem");
|
|
818
|
-
var
|
|
831
|
+
var import_chains2 = require("viem/chains");
|
|
832
|
+
var import_sdk5 = require("@zkp2p/sdk");
|
|
819
833
|
var indexerService = null;
|
|
820
834
|
function getIndexerService() {
|
|
821
835
|
if (!indexerService) {
|
|
822
|
-
const endpoint = (0,
|
|
823
|
-
const client = new
|
|
824
|
-
indexerService = new
|
|
836
|
+
const endpoint = (0, import_sdk5.defaultIndexerEndpoint)("PRODUCTION");
|
|
837
|
+
const client = new import_sdk5.IndexerClient(endpoint);
|
|
838
|
+
indexerService = new import_sdk5.IndexerDepositService(client);
|
|
825
839
|
}
|
|
826
840
|
return indexerService;
|
|
827
841
|
}
|
|
@@ -855,7 +869,7 @@ function resolveMethodNames(hashes) {
|
|
|
855
869
|
return Array.from(names);
|
|
856
870
|
}
|
|
857
871
|
function mapDeposit(d) {
|
|
858
|
-
const delegationState = (0,
|
|
872
|
+
const delegationState = (0, import_sdk5.classifyDelegationState)(
|
|
859
873
|
d.rateManagerId ?? void 0,
|
|
860
874
|
d.rateManagerAddress ?? void 0,
|
|
861
875
|
DELEGATE_RATE_MANAGER_ID,
|
|
@@ -872,7 +886,7 @@ function mapDeposit(d) {
|
|
|
872
886
|
fulfilledIntents: d.fulfilledIntents ?? 0,
|
|
873
887
|
paymentMethods: resolveMethodNames(d.paymentMethods),
|
|
874
888
|
currencies: d.currencies.map((c) => {
|
|
875
|
-
const info = (0,
|
|
889
|
+
const info = (0, import_sdk5.getCurrencyInfoFromHash)(c.currencyCode);
|
|
876
890
|
return info?.currencyCode ?? c.currencyCode;
|
|
877
891
|
}),
|
|
878
892
|
rateSource: d.currencies[0]?.rateSource || "unknown",
|
|
@@ -911,6 +925,105 @@ async function close(walletClient, depositId, escrowAddress) {
|
|
|
911
925
|
return extractTxHash(result);
|
|
912
926
|
}
|
|
913
927
|
|
|
928
|
+
// src/telemetry.ts
|
|
929
|
+
var SDK_EVENTS_ENDPOINT = "https://usdctofiat.xyz/api/sdk-events";
|
|
930
|
+
var MAX_BATCH_SIZE = 50;
|
|
931
|
+
var FLUSH_DELAY_MS = 180;
|
|
932
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
933
|
+
var queuedEvents = [];
|
|
934
|
+
var flushTimer = null;
|
|
935
|
+
var flushInFlight = false;
|
|
936
|
+
function sanitizeAttributionId(raw) {
|
|
937
|
+
if (typeof raw !== "string") return void 0;
|
|
938
|
+
const stripped = raw.trim().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
939
|
+
if (!stripped) return void 0;
|
|
940
|
+
if (stripped.length > 64) return void 0;
|
|
941
|
+
return stripped;
|
|
942
|
+
}
|
|
943
|
+
function scheduleFlush() {
|
|
944
|
+
if (flushTimer != null) return;
|
|
945
|
+
flushTimer = setTimeout(() => {
|
|
946
|
+
flushTimer = null;
|
|
947
|
+
void flushQueue();
|
|
948
|
+
}, FLUSH_DELAY_MS);
|
|
949
|
+
}
|
|
950
|
+
async function postBatch(batch) {
|
|
951
|
+
if (!batch.length) return;
|
|
952
|
+
const body = JSON.stringify(batch);
|
|
953
|
+
try {
|
|
954
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
955
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
956
|
+
const accepted = navigator.sendBeacon(SDK_EVENTS_ENDPOINT, blob);
|
|
957
|
+
if (accepted) return;
|
|
958
|
+
}
|
|
959
|
+
} catch {
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
963
|
+
const timeout = controller ? setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS) : null;
|
|
964
|
+
await fetch(SDK_EVENTS_ENDPOINT, {
|
|
965
|
+
method: "POST",
|
|
966
|
+
headers: { "Content-Type": "application/json" },
|
|
967
|
+
body,
|
|
968
|
+
keepalive: true,
|
|
969
|
+
signal: controller?.signal
|
|
970
|
+
}).catch(() => {
|
|
971
|
+
});
|
|
972
|
+
if (timeout) clearTimeout(timeout);
|
|
973
|
+
} catch {
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
async function flushQueue() {
|
|
977
|
+
if (flushInFlight) return;
|
|
978
|
+
if (!queuedEvents.length) return;
|
|
979
|
+
flushInFlight = true;
|
|
980
|
+
try {
|
|
981
|
+
while (queuedEvents.length > 0) {
|
|
982
|
+
const batch = queuedEvents.slice(0, MAX_BATCH_SIZE);
|
|
983
|
+
queuedEvents = queuedEvents.slice(batch.length);
|
|
984
|
+
await postBatch(batch);
|
|
985
|
+
}
|
|
986
|
+
} finally {
|
|
987
|
+
flushInFlight = false;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
function emitEvent(name, payload) {
|
|
991
|
+
try {
|
|
992
|
+
queuedEvents.push({
|
|
993
|
+
name,
|
|
994
|
+
payload,
|
|
995
|
+
ts: Date.now()
|
|
996
|
+
});
|
|
997
|
+
if (queuedEvents.length >= MAX_BATCH_SIZE) {
|
|
998
|
+
void flushQueue();
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
scheduleFlush();
|
|
1002
|
+
} catch {
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function createTelemetryContext(options) {
|
|
1006
|
+
const integratorId = sanitizeAttributionId(options.integratorId);
|
|
1007
|
+
const referralId = sanitizeAttributionId(options.referralId);
|
|
1008
|
+
const enabled = options.telemetry !== false;
|
|
1009
|
+
const sdkVersion = options.sdkVersion;
|
|
1010
|
+
return {
|
|
1011
|
+
enabled,
|
|
1012
|
+
sdkVersion,
|
|
1013
|
+
integratorId,
|
|
1014
|
+
referralId,
|
|
1015
|
+
emit(name, payload = {}) {
|
|
1016
|
+
if (!enabled) return;
|
|
1017
|
+
emitEvent(name, {
|
|
1018
|
+
sdkVersion,
|
|
1019
|
+
integratorId,
|
|
1020
|
+
referralId,
|
|
1021
|
+
...payload
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
|
|
914
1027
|
// src/deposit.ts
|
|
915
1028
|
function usdcToUnits(amount) {
|
|
916
1029
|
return (0, import_viem3.parseUnits)(amount, 6);
|
|
@@ -984,7 +1097,7 @@ function attachOracleConfig(entries, conversionRates) {
|
|
|
984
1097
|
(group, gi) => group.map((entry, ci) => {
|
|
985
1098
|
const currency = conversionRates[gi]?.[ci]?.currency;
|
|
986
1099
|
if (!currency) return entry;
|
|
987
|
-
const oracleConfig = (0,
|
|
1100
|
+
const oracleConfig = (0, import_sdk6.getSpreadOracleConfig)(currency);
|
|
988
1101
|
if (!oracleConfig) return entry;
|
|
989
1102
|
return {
|
|
990
1103
|
...entry,
|
|
@@ -1006,15 +1119,6 @@ function extractTxHash2(result) {
|
|
|
1006
1119
|
return result.transactionHash;
|
|
1007
1120
|
throw new Error("Unexpected transaction result format");
|
|
1008
1121
|
}
|
|
1009
|
-
function createSdkClient(walletClient) {
|
|
1010
|
-
return new import_sdk4.OfframpClient({
|
|
1011
|
-
walletClient,
|
|
1012
|
-
chainId: BASE_CHAIN_ID,
|
|
1013
|
-
runtimeEnv: RUNTIME_ENV,
|
|
1014
|
-
rpcUrl: BASE_RPC_URL,
|
|
1015
|
-
baseApiUrl: API_BASE_URL
|
|
1016
|
-
});
|
|
1017
|
-
}
|
|
1018
1122
|
async function findUndelegatedDeposit(walletAddress) {
|
|
1019
1123
|
try {
|
|
1020
1124
|
const service = getIndexerService();
|
|
@@ -1023,6 +1127,7 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1023
1127
|
{ limit: 50 }
|
|
1024
1128
|
);
|
|
1025
1129
|
const escrowLower = ESCROW_ADDRESS.toLowerCase();
|
|
1130
|
+
const delegateIdLower = DELEGATE_RATE_MANAGER_ID.toLowerCase();
|
|
1026
1131
|
const undelegated = (raw || []).filter((d) => {
|
|
1027
1132
|
if (d.status === "CLOSED") return false;
|
|
1028
1133
|
if (d.escrowAddress.toLowerCase() !== escrowLower) return false;
|
|
@@ -1034,273 +1139,493 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1034
1139
|
}
|
|
1035
1140
|
})();
|
|
1036
1141
|
if (remaining === 0n) return false;
|
|
1037
|
-
return d.rateManagerId !==
|
|
1142
|
+
return (d.rateManagerId ?? "").toLowerCase() !== delegateIdLower;
|
|
1143
|
+
});
|
|
1144
|
+
undelegated.sort((a, b) => {
|
|
1145
|
+
const left = BigInt(a.depositId);
|
|
1146
|
+
const right = BigInt(b.depositId);
|
|
1147
|
+
if (left === right) return 0;
|
|
1148
|
+
return left > right ? -1 : 1;
|
|
1038
1149
|
});
|
|
1039
|
-
undelegated.sort((a, b) => Number(BigInt(b.depositId) - BigInt(a.depositId)));
|
|
1040
1150
|
return undelegated[0] ?? null;
|
|
1041
1151
|
} catch {
|
|
1042
1152
|
return null;
|
|
1043
1153
|
}
|
|
1044
1154
|
}
|
|
1045
|
-
async function offramp(walletClient, params, onProgress) {
|
|
1155
|
+
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1046
1156
|
const { amount, platform, currency, identifier } = params;
|
|
1047
1157
|
const platformId = platform.id;
|
|
1048
1158
|
const currencyCode = currency.code;
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1159
|
+
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1160
|
+
sdkVersion: SDK_VERSION,
|
|
1161
|
+
telemetry: true,
|
|
1162
|
+
integratorId: params.integratorId,
|
|
1163
|
+
referralId: params.referralId
|
|
1164
|
+
});
|
|
1165
|
+
const flowStartMs = Date.now();
|
|
1166
|
+
const emitProgress = (progress) => {
|
|
1167
|
+
onProgress?.(progress);
|
|
1168
|
+
telemetry.emit("sdk.createDeposit.progress", {
|
|
1169
|
+
step: progress.step,
|
|
1170
|
+
txHash: progress.txHash
|
|
1171
|
+
});
|
|
1172
|
+
};
|
|
1173
|
+
try {
|
|
1174
|
+
if (!walletClient.account?.address) {
|
|
1175
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1176
|
+
}
|
|
1177
|
+
const walletAddress = walletClient.account.address.toLowerCase();
|
|
1178
|
+
telemetry.emit("sdk.createDeposit.start", {
|
|
1179
|
+
platform: platformId,
|
|
1180
|
+
currency: currencyCode,
|
|
1181
|
+
amount,
|
|
1182
|
+
wallet: walletAddress
|
|
1183
|
+
});
|
|
1184
|
+
const amt = parseFloat(amount);
|
|
1185
|
+
if (!Number.isFinite(amt) || amt < MIN_DEPOSIT_USDC) {
|
|
1186
|
+
throw new OfframpError(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC`, "VALIDATION");
|
|
1187
|
+
}
|
|
1188
|
+
if (!platform.currencies.includes(currencyCode)) {
|
|
1189
|
+
throw new OfframpError(`${currencyCode} is not supported on ${platform.name}`, "UNSUPPORTED");
|
|
1190
|
+
}
|
|
1191
|
+
const validation = platform.validate(identifier);
|
|
1192
|
+
if (!validation.valid) {
|
|
1193
|
+
throw new OfframpError(validation.error || "Invalid identifier", "VALIDATION");
|
|
1194
|
+
}
|
|
1195
|
+
const normalizedIdentifier = validation.normalized;
|
|
1196
|
+
const methodHash = getPaymentMethodHash(platformId);
|
|
1197
|
+
if (!methodHash) {
|
|
1198
|
+
throw new OfframpError(`${platform.name} is not currently supported`, "UNSUPPORTED");
|
|
1199
|
+
}
|
|
1200
|
+
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1201
|
+
if (existing) {
|
|
1202
|
+
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1203
|
+
const client2 = createSdkClient(walletClient);
|
|
1204
|
+
const txOverrides2 = { referrer: [REFERRER] };
|
|
1205
|
+
try {
|
|
1206
|
+
const result2 = await client2.setRateManager({
|
|
1207
|
+
depositId: BigInt(existing.depositId),
|
|
1208
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1209
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1210
|
+
escrowAddress: existing.escrowAddress,
|
|
1211
|
+
txOverrides: txOverrides2
|
|
1212
|
+
});
|
|
1213
|
+
const txHash = extractTxHash2(result2);
|
|
1214
|
+
emitProgress({ step: "done", txHash, depositId: existing.depositId });
|
|
1215
|
+
const resumedResult = { depositId: existing.depositId, txHash, resumed: true };
|
|
1216
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1217
|
+
depositId: resumedResult.depositId,
|
|
1218
|
+
txHash: resumedResult.txHash,
|
|
1219
|
+
wallet: walletAddress,
|
|
1220
|
+
resumed: true,
|
|
1221
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1222
|
+
});
|
|
1223
|
+
return resumedResult;
|
|
1224
|
+
} catch (err) {
|
|
1225
|
+
if (isUserCancellation(err))
|
|
1226
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
|
|
1227
|
+
throw new OfframpError(
|
|
1228
|
+
"Found undelegated deposit but delegation failed. Try again.",
|
|
1229
|
+
"DELEGATION_FAILED",
|
|
1230
|
+
"resuming",
|
|
1231
|
+
err,
|
|
1232
|
+
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1237
|
+
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1238
|
+
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1239
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, 2500)));
|
|
1074
1240
|
try {
|
|
1075
|
-
const
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1241
|
+
const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains3.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
|
|
1242
|
+
const balance = await publicClient.readContract({
|
|
1243
|
+
address: USDC_ADDRESS,
|
|
1244
|
+
abi: [
|
|
1245
|
+
{
|
|
1246
|
+
name: "balanceOf",
|
|
1247
|
+
type: "function",
|
|
1248
|
+
stateMutability: "view",
|
|
1249
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1250
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1251
|
+
}
|
|
1252
|
+
],
|
|
1253
|
+
functionName: "balanceOf",
|
|
1254
|
+
args: [walletAddress]
|
|
1255
|
+
});
|
|
1256
|
+
if (balance < amountUnits) {
|
|
1257
|
+
const available = Number((0, import_viem3.formatUnits)(balance, 6));
|
|
1258
|
+
throw new OfframpError(
|
|
1259
|
+
`Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
|
|
1260
|
+
"VALIDATION"
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
} catch (err) {
|
|
1264
|
+
if (err instanceof OfframpError) throw err;
|
|
1265
|
+
}
|
|
1266
|
+
const client = createSdkClient(walletClient);
|
|
1267
|
+
const txOverrides = { referrer: [REFERRER] };
|
|
1268
|
+
emitProgress({ step: "approving" });
|
|
1269
|
+
try {
|
|
1270
|
+
await client.ensureAllowance({
|
|
1271
|
+
token: USDC_ADDRESS,
|
|
1272
|
+
amount: amountUnits,
|
|
1273
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1274
|
+
maxApprove: false,
|
|
1275
|
+
txOverrides
|
|
1081
1276
|
});
|
|
1082
|
-
const txHash = extractTxHash2(result);
|
|
1083
|
-
onProgress?.({ step: "done", txHash, depositId: existing.depositId });
|
|
1084
|
-
return { depositId: existing.depositId, txHash, resumed: true };
|
|
1085
1277
|
} catch (err) {
|
|
1086
1278
|
if (isUserCancellation(err))
|
|
1087
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "
|
|
1279
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1280
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1088
1281
|
throw new OfframpError(
|
|
1089
|
-
|
|
1090
|
-
"
|
|
1091
|
-
"
|
|
1092
|
-
err
|
|
1093
|
-
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1282
|
+
`USDC approval failed: ${detail}`,
|
|
1283
|
+
"APPROVAL_FAILED",
|
|
1284
|
+
"approving",
|
|
1285
|
+
err
|
|
1094
1286
|
);
|
|
1095
1287
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
const balance = await publicClient.readContract({
|
|
1104
|
-
address: USDC_ADDRESS,
|
|
1105
|
-
abi: [
|
|
1106
|
-
{
|
|
1107
|
-
name: "balanceOf",
|
|
1108
|
-
type: "function",
|
|
1109
|
-
stateMutability: "view",
|
|
1110
|
-
inputs: [{ name: "account", type: "address" }],
|
|
1111
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
1112
|
-
}
|
|
1113
|
-
],
|
|
1114
|
-
functionName: "balanceOf",
|
|
1115
|
-
args: [walletAddress]
|
|
1116
|
-
});
|
|
1117
|
-
if (balance < amountUnits) {
|
|
1118
|
-
const available = Number((0, import_viem3.formatUnits)(balance, 6));
|
|
1288
|
+
emitProgress({ step: "registering" });
|
|
1289
|
+
let hashedOnchainId;
|
|
1290
|
+
try {
|
|
1291
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1292
|
+
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1293
|
+
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1294
|
+
} catch (err) {
|
|
1119
1295
|
throw new OfframpError(
|
|
1120
|
-
|
|
1121
|
-
"
|
|
1296
|
+
"Payee registration failed",
|
|
1297
|
+
"REGISTRATION_FAILED",
|
|
1298
|
+
"registering",
|
|
1299
|
+
err
|
|
1122
1300
|
);
|
|
1123
1301
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
await client.ensureAllowance({
|
|
1132
|
-
token: USDC_ADDRESS,
|
|
1133
|
-
amount: amountUnits,
|
|
1134
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1135
|
-
maxApprove: false,
|
|
1136
|
-
txOverrides
|
|
1137
|
-
});
|
|
1138
|
-
} catch (err) {
|
|
1139
|
-
if (isUserCancellation(err))
|
|
1140
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1141
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1142
|
-
throw new OfframpError(`USDC approval failed: ${detail}`, "APPROVAL_FAILED", "approving", err);
|
|
1143
|
-
}
|
|
1144
|
-
onProgress?.({ step: "registering" });
|
|
1145
|
-
let hashedOnchainId;
|
|
1146
|
-
try {
|
|
1147
|
-
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1148
|
-
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1149
|
-
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1150
|
-
} catch (err) {
|
|
1151
|
-
throw new OfframpError("Payee registration failed", "REGISTRATION_FAILED", "registering", err);
|
|
1152
|
-
}
|
|
1153
|
-
onProgress?.({ step: "depositing" });
|
|
1154
|
-
const conversionRates = [
|
|
1155
|
-
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1156
|
-
];
|
|
1157
|
-
const baseCurrenciesOverride = (0, import_sdk4.mapConversionRatesToOnchainMinRate)(conversionRates, 1);
|
|
1158
|
-
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1159
|
-
let hash;
|
|
1160
|
-
try {
|
|
1161
|
-
const result = await client.createDeposit({
|
|
1162
|
-
token: USDC_ADDRESS,
|
|
1163
|
-
amount: amountUnits,
|
|
1164
|
-
retainOnEmpty: false,
|
|
1165
|
-
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1166
|
-
processorNames: [platformId],
|
|
1167
|
-
conversionRates,
|
|
1168
|
-
payeeDetailsHashes: [hashedOnchainId],
|
|
1169
|
-
paymentMethodsOverride: [methodHash],
|
|
1170
|
-
paymentMethodDataOverride: [
|
|
1171
|
-
{
|
|
1172
|
-
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1173
|
-
payeeDetails: hashedOnchainId,
|
|
1174
|
-
data: "0x"
|
|
1175
|
-
}
|
|
1176
|
-
],
|
|
1177
|
-
currenciesOverride,
|
|
1178
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1179
|
-
txOverrides
|
|
1180
|
-
});
|
|
1181
|
-
if (!result?.hash) throw new Error("No transaction hash returned");
|
|
1182
|
-
hash = result.hash;
|
|
1183
|
-
} catch (err) {
|
|
1184
|
-
if (isUserCancellation(err))
|
|
1185
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1186
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1187
|
-
throw new OfframpError(
|
|
1188
|
-
`Deposit transaction failed: ${detail}`,
|
|
1189
|
-
"DEPOSIT_FAILED",
|
|
1190
|
-
"depositing",
|
|
1191
|
-
err
|
|
1192
|
-
);
|
|
1193
|
-
}
|
|
1194
|
-
onProgress?.({ step: "confirming", txHash: hash });
|
|
1195
|
-
let depositId = "";
|
|
1196
|
-
const receiptClient = client;
|
|
1197
|
-
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1302
|
+
emitProgress({ step: "depositing" });
|
|
1303
|
+
const conversionRates = [
|
|
1304
|
+
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1305
|
+
];
|
|
1306
|
+
const baseCurrenciesOverride = (0, import_sdk6.mapConversionRatesToOnchainMinRate)(conversionRates, 1);
|
|
1307
|
+
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1308
|
+
let hash;
|
|
1198
1309
|
try {
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1310
|
+
const result2 = await client.createDeposit({
|
|
1311
|
+
token: USDC_ADDRESS,
|
|
1312
|
+
amount: amountUnits,
|
|
1313
|
+
retainOnEmpty: false,
|
|
1314
|
+
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1315
|
+
processorNames: [platformId],
|
|
1316
|
+
conversionRates,
|
|
1317
|
+
payeeDetailsHashes: [hashedOnchainId],
|
|
1318
|
+
paymentMethodsOverride: [methodHash],
|
|
1319
|
+
paymentMethodDataOverride: [
|
|
1320
|
+
{
|
|
1321
|
+
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1322
|
+
payeeDetails: hashedOnchainId,
|
|
1323
|
+
data: "0x"
|
|
1324
|
+
}
|
|
1325
|
+
],
|
|
1326
|
+
currenciesOverride,
|
|
1327
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1328
|
+
txOverrides
|
|
1329
|
+
});
|
|
1330
|
+
if (!result2?.hash) throw new Error("No transaction hash returned");
|
|
1331
|
+
hash = result2.hash;
|
|
1332
|
+
} catch (err) {
|
|
1333
|
+
if (isUserCancellation(err))
|
|
1334
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1335
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1336
|
+
throw new OfframpError(
|
|
1337
|
+
`Deposit transaction failed: ${detail}`,
|
|
1338
|
+
"DEPOSIT_FAILED",
|
|
1339
|
+
"depositing",
|
|
1340
|
+
err
|
|
1341
|
+
);
|
|
1202
1342
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1343
|
+
emitProgress({ step: "confirming", txHash: hash });
|
|
1344
|
+
let depositId = "";
|
|
1345
|
+
const receiptClient = client;
|
|
1346
|
+
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1207
1347
|
try {
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
{ limit: 25 }
|
|
1211
|
-
);
|
|
1212
|
-
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1213
|
-
if (hit) {
|
|
1214
|
-
depositId = String(hit.depositId);
|
|
1215
|
-
break;
|
|
1216
|
-
}
|
|
1348
|
+
const receipt = await receiptClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1349
|
+
depositId = extractDepositIdFromLogs(receipt.logs) || "";
|
|
1217
1350
|
} catch {
|
|
1218
1351
|
}
|
|
1219
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
1220
|
-
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1221
1352
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
if (isUserCancellation(delegationError)) {
|
|
1353
|
+
if (!depositId) {
|
|
1354
|
+
let delay = INDEXER_INITIAL_DELAY_MS;
|
|
1355
|
+
for (let attempt = 0; attempt < INDEXER_MAX_ATTEMPTS && !depositId; attempt++) {
|
|
1356
|
+
try {
|
|
1357
|
+
const deps = await client.indexer.getDepositsWithRelations(
|
|
1358
|
+
{ depositor: walletAddress },
|
|
1359
|
+
{ limit: 25 }
|
|
1360
|
+
);
|
|
1361
|
+
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1362
|
+
if (hit) {
|
|
1363
|
+
depositId = String(hit.depositId);
|
|
1364
|
+
break;
|
|
1365
|
+
}
|
|
1366
|
+
} catch {
|
|
1367
|
+
}
|
|
1368
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1369
|
+
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
if (!depositId) {
|
|
1243
1373
|
throw new OfframpError(
|
|
1244
|
-
"
|
|
1245
|
-
"
|
|
1374
|
+
"Deposit created on-chain but could not confirm deposit ID. Your funds are safe. Call offramp() again to resume delegation.",
|
|
1375
|
+
"CONFIRMATION_FAILED",
|
|
1376
|
+
"confirming",
|
|
1377
|
+
void 0,
|
|
1378
|
+
{ txHash: hash }
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
emitProgress({ step: "delegating", txHash: hash, depositId });
|
|
1382
|
+
try {
|
|
1383
|
+
await client.setRateManager({
|
|
1384
|
+
depositId: BigInt(depositId),
|
|
1385
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1386
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1387
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1388
|
+
txOverrides
|
|
1389
|
+
});
|
|
1390
|
+
} catch (delegationError) {
|
|
1391
|
+
if (isUserCancellation(delegationError)) {
|
|
1392
|
+
throw new OfframpError(
|
|
1393
|
+
"User cancelled delegation",
|
|
1394
|
+
"USER_CANCELLED",
|
|
1395
|
+
"delegating",
|
|
1396
|
+
delegationError,
|
|
1397
|
+
{ txHash: hash, depositId }
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
throw new OfframpError(
|
|
1401
|
+
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1402
|
+
"DELEGATION_FAILED",
|
|
1246
1403
|
"delegating",
|
|
1247
1404
|
delegationError,
|
|
1248
1405
|
{ txHash: hash, depositId }
|
|
1249
1406
|
);
|
|
1250
1407
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
"
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1408
|
+
let otcLink;
|
|
1409
|
+
if (params.otcTaker) {
|
|
1410
|
+
emitProgress({ step: "restricting", txHash: hash, depositId });
|
|
1411
|
+
try {
|
|
1412
|
+
const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
|
|
1413
|
+
otcLink = otcResult.otcLink;
|
|
1414
|
+
} catch (otcError) {
|
|
1415
|
+
if (isUserCancellation(otcError)) {
|
|
1416
|
+
throw new OfframpError(
|
|
1417
|
+
"User cancelled OTC restriction",
|
|
1418
|
+
"USER_CANCELLED",
|
|
1419
|
+
"restricting",
|
|
1420
|
+
otcError,
|
|
1421
|
+
{
|
|
1422
|
+
txHash: hash,
|
|
1423
|
+
depositId
|
|
1424
|
+
}
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1267
1427
|
throw new OfframpError(
|
|
1268
|
-
"
|
|
1269
|
-
"
|
|
1428
|
+
"Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
|
|
1429
|
+
"DEPOSIT_FAILED",
|
|
1270
1430
|
"restricting",
|
|
1271
1431
|
otcError,
|
|
1272
|
-
{
|
|
1273
|
-
txHash: hash,
|
|
1274
|
-
depositId
|
|
1275
|
-
}
|
|
1432
|
+
{ txHash: hash, depositId }
|
|
1276
1433
|
);
|
|
1277
1434
|
}
|
|
1435
|
+
}
|
|
1436
|
+
emitProgress({ step: "done", txHash: hash, depositId });
|
|
1437
|
+
const result = { depositId, txHash: hash, resumed: false, otcLink };
|
|
1438
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1439
|
+
depositId: result.depositId,
|
|
1440
|
+
txHash: result.txHash,
|
|
1441
|
+
wallet: walletAddress,
|
|
1442
|
+
resumed: false,
|
|
1443
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1444
|
+
});
|
|
1445
|
+
return result;
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
const known = error instanceof OfframpError ? error : null;
|
|
1448
|
+
telemetry.emit("sdk.createDeposit.error", {
|
|
1449
|
+
code: known?.code ?? "DEPOSIT_FAILED",
|
|
1450
|
+
step: known?.step ?? "unknown"
|
|
1451
|
+
});
|
|
1452
|
+
throw error;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
// src/offramp.ts
|
|
1457
|
+
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1458
|
+
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1459
|
+
function getWalletAddress(walletClient) {
|
|
1460
|
+
const address = walletClient.account?.address;
|
|
1461
|
+
if (!address) {
|
|
1462
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1463
|
+
}
|
|
1464
|
+
return address.toLowerCase();
|
|
1465
|
+
}
|
|
1466
|
+
function getIdempotencyStorageKey(walletAddress, idempotencyKey) {
|
|
1467
|
+
return `${IDEMPOTENCY_STORAGE_PREFIX}${walletAddress}:${idempotencyKey}`;
|
|
1468
|
+
}
|
|
1469
|
+
function readIdempotencyResult(storageKey) {
|
|
1470
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1471
|
+
try {
|
|
1472
|
+
const raw = sessionStorage.getItem(storageKey);
|
|
1473
|
+
if (!raw) return null;
|
|
1474
|
+
const parsed = JSON.parse(raw);
|
|
1475
|
+
const expiresAt = typeof parsed.expiresAt === "number" && Number.isFinite(parsed.expiresAt) ? parsed.expiresAt : 0;
|
|
1476
|
+
if (expiresAt <= Date.now()) {
|
|
1477
|
+
sessionStorage.removeItem(storageKey);
|
|
1478
|
+
return null;
|
|
1479
|
+
}
|
|
1480
|
+
const result = parsed.result;
|
|
1481
|
+
if (!result?.depositId || !result.txHash) {
|
|
1482
|
+
sessionStorage.removeItem(storageKey);
|
|
1483
|
+
return null;
|
|
1484
|
+
}
|
|
1485
|
+
return result;
|
|
1486
|
+
} catch {
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
function writeIdempotencyResult(storageKey, result) {
|
|
1491
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1492
|
+
try {
|
|
1493
|
+
const record = {
|
|
1494
|
+
result,
|
|
1495
|
+
expiresAt: Date.now() + IDEMPOTENCY_TTL_MS
|
|
1496
|
+
};
|
|
1497
|
+
sessionStorage.setItem(storageKey, JSON.stringify(record));
|
|
1498
|
+
} catch {
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
function createPerFlowTelemetry(base4, integratorId, referralId) {
|
|
1502
|
+
return createTelemetryContext({
|
|
1503
|
+
sdkVersion: base4.sdkVersion,
|
|
1504
|
+
telemetry: base4.enabled,
|
|
1505
|
+
integratorId: sanitizeAttributionId(integratorId) ?? base4.integratorId,
|
|
1506
|
+
referralId: sanitizeAttributionId(referralId) ?? base4.referralId
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
function normalizeCurrencyEntry(code) {
|
|
1510
|
+
const fromCatalog = CURRENCIES[code];
|
|
1511
|
+
if (fromCatalog) return fromCatalog;
|
|
1512
|
+
return {
|
|
1513
|
+
code,
|
|
1514
|
+
name: code,
|
|
1515
|
+
symbol: code,
|
|
1516
|
+
countryCode: ""
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
function buildCurrencyGroups() {
|
|
1520
|
+
const grouped = {};
|
|
1521
|
+
for (const platform of Object.values(PLATFORMS)) {
|
|
1522
|
+
grouped[platform.id] = platform.currencies.map(normalizeCurrencyEntry);
|
|
1523
|
+
}
|
|
1524
|
+
return grouped;
|
|
1525
|
+
}
|
|
1526
|
+
var Offramp = class {
|
|
1527
|
+
walletClient;
|
|
1528
|
+
telemetry;
|
|
1529
|
+
constructor(options) {
|
|
1530
|
+
this.walletClient = options.walletClient;
|
|
1531
|
+
this.telemetry = createTelemetryContext({
|
|
1532
|
+
sdkVersion: SDK_VERSION,
|
|
1533
|
+
telemetry: options.telemetry,
|
|
1534
|
+
integratorId: options.integratorId,
|
|
1535
|
+
referralId: options.referralId
|
|
1536
|
+
});
|
|
1537
|
+
this.telemetry.emit("sdk.init", {
|
|
1538
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown"
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
async createDeposit(params, onProgress) {
|
|
1542
|
+
const flowTelemetry = createPerFlowTelemetry(
|
|
1543
|
+
this.telemetry,
|
|
1544
|
+
params.integratorId,
|
|
1545
|
+
params.referralId
|
|
1546
|
+
);
|
|
1547
|
+
const sanitizedParams = {
|
|
1548
|
+
...params,
|
|
1549
|
+
integratorId: flowTelemetry.integratorId,
|
|
1550
|
+
referralId: flowTelemetry.referralId
|
|
1551
|
+
};
|
|
1552
|
+
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1553
|
+
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1554
|
+
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1555
|
+
if (storageKey) {
|
|
1556
|
+
const existing = readIdempotencyResult(storageKey);
|
|
1557
|
+
if (existing) return existing;
|
|
1558
|
+
}
|
|
1559
|
+
const result = await offramp(
|
|
1560
|
+
this.walletClient,
|
|
1561
|
+
sanitizedParams,
|
|
1562
|
+
onProgress,
|
|
1563
|
+
flowTelemetry
|
|
1564
|
+
);
|
|
1565
|
+
if (storageKey) {
|
|
1566
|
+
writeIdempotencyResult(storageKey, result);
|
|
1567
|
+
}
|
|
1568
|
+
return result;
|
|
1569
|
+
}
|
|
1570
|
+
getQuote(input) {
|
|
1571
|
+
const amountUsdc = Number(input.amount);
|
|
1572
|
+
if (!Number.isFinite(amountUsdc) || amountUsdc <= 0) {
|
|
1573
|
+
throw new OfframpError("Amount must be a positive number", "VALIDATION");
|
|
1574
|
+
}
|
|
1575
|
+
if (!input.platform.currencies.includes(input.currency.code)) {
|
|
1278
1576
|
throw new OfframpError(
|
|
1279
|
-
|
|
1280
|
-
"
|
|
1281
|
-
"restricting",
|
|
1282
|
-
otcError,
|
|
1283
|
-
{ txHash: hash, depositId }
|
|
1577
|
+
`${input.currency.code} is not supported on ${input.platform.name}`,
|
|
1578
|
+
"UNSUPPORTED"
|
|
1284
1579
|
);
|
|
1285
1580
|
}
|
|
1581
|
+
const vaultSpreadBps = 0;
|
|
1582
|
+
const spreadFactor = Math.max(0, 1 - vaultSpreadBps / 1e4);
|
|
1583
|
+
const expectedFiat = amountUsdc * spreadFactor;
|
|
1584
|
+
const effectiveRate = amountUsdc === 0 ? 0 : expectedFiat / amountUsdc;
|
|
1585
|
+
return {
|
|
1586
|
+
amountUsdc,
|
|
1587
|
+
expectedFiat,
|
|
1588
|
+
effectiveRate,
|
|
1589
|
+
vaultSpreadBps
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
getVaultStatus() {
|
|
1593
|
+
return {
|
|
1594
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1595
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1596
|
+
feeRateBps: DELEGATE_MANAGER_FEE_BPS,
|
|
1597
|
+
escrow: ESCROW_ADDRESS,
|
|
1598
|
+
isActive: true
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
listCurrencies() {
|
|
1602
|
+
return buildCurrencyGroups();
|
|
1286
1603
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1604
|
+
};
|
|
1605
|
+
function createOfframp(options) {
|
|
1606
|
+
return new Offramp(options);
|
|
1289
1607
|
}
|
|
1290
1608
|
|
|
1291
1609
|
// src/hooks/useOfframp.ts
|
|
1292
|
-
function
|
|
1610
|
+
function mapStepToState(step) {
|
|
1611
|
+
if (step === "resuming" || step === "restricting") return "delegating";
|
|
1612
|
+
if (step === "done") return "done";
|
|
1613
|
+
return step;
|
|
1614
|
+
}
|
|
1615
|
+
function useOfframp(defaultOptions = {}) {
|
|
1616
|
+
const [state, setState] = (0, import_react.useState)("idle");
|
|
1293
1617
|
const [step, setStep] = (0, import_react.useState)(null);
|
|
1294
1618
|
const [txHash, setTxHash] = (0, import_react.useState)(null);
|
|
1295
1619
|
const [depositId, setDepositId] = (0, import_react.useState)(null);
|
|
1296
|
-
const [
|
|
1620
|
+
const [lastError, setLastError] = (0, import_react.useState)(null);
|
|
1297
1621
|
const [isLoading, setIsLoading] = (0, import_react.useState)(false);
|
|
1298
1622
|
const lockRef = (0, import_react.useRef)(false);
|
|
1299
1623
|
const reset = (0, import_react.useCallback)(() => {
|
|
1624
|
+
setState("idle");
|
|
1300
1625
|
setStep(null);
|
|
1301
1626
|
setTxHash(null);
|
|
1302
1627
|
setDepositId(null);
|
|
1303
|
-
|
|
1628
|
+
setLastError(null);
|
|
1304
1629
|
setIsLoading(false);
|
|
1305
1630
|
lockRef.current = false;
|
|
1306
1631
|
}, []);
|
|
@@ -1309,16 +1634,25 @@ function useOfframp() {
|
|
|
1309
1634
|
if (lockRef.current) throw new OfframpError("Deposit already in progress", "VALIDATION");
|
|
1310
1635
|
lockRef.current = true;
|
|
1311
1636
|
setIsLoading(true);
|
|
1312
|
-
|
|
1637
|
+
setState("idle");
|
|
1638
|
+
setLastError(null);
|
|
1313
1639
|
setStep(null);
|
|
1314
1640
|
setTxHash(null);
|
|
1315
1641
|
setDepositId(null);
|
|
1316
1642
|
try {
|
|
1317
|
-
const
|
|
1643
|
+
const sdk = createOfframp({
|
|
1644
|
+
walletClient,
|
|
1645
|
+
integratorId: params.integratorId ?? defaultOptions.integratorId,
|
|
1646
|
+
referralId: params.referralId ?? defaultOptions.referralId,
|
|
1647
|
+
telemetry: defaultOptions.telemetry
|
|
1648
|
+
});
|
|
1649
|
+
const result = await sdk.createDeposit(params, (progress) => {
|
|
1318
1650
|
setStep(progress.step);
|
|
1651
|
+
setState(mapStepToState(progress.step));
|
|
1319
1652
|
if (progress.txHash) setTxHash(progress.txHash);
|
|
1320
1653
|
if (progress.depositId) setDepositId(progress.depositId);
|
|
1321
1654
|
});
|
|
1655
|
+
setState("done");
|
|
1322
1656
|
setDepositId(result.depositId);
|
|
1323
1657
|
setTxHash(result.txHash);
|
|
1324
1658
|
return result;
|
|
@@ -1327,20 +1661,23 @@ function useOfframp() {
|
|
|
1327
1661
|
err instanceof Error ? err.message : "Offramp failed",
|
|
1328
1662
|
"DEPOSIT_FAILED"
|
|
1329
1663
|
);
|
|
1330
|
-
|
|
1331
|
-
|
|
1664
|
+
setLastError(offrampError);
|
|
1665
|
+
setState("error");
|
|
1666
|
+
throw offrampError;
|
|
1332
1667
|
} finally {
|
|
1333
1668
|
setIsLoading(false);
|
|
1334
1669
|
lockRef.current = false;
|
|
1335
1670
|
}
|
|
1336
1671
|
},
|
|
1337
|
-
[]
|
|
1672
|
+
[defaultOptions.integratorId, defaultOptions.referralId, defaultOptions.telemetry]
|
|
1338
1673
|
);
|
|
1339
1674
|
return {
|
|
1675
|
+
state,
|
|
1340
1676
|
step,
|
|
1341
1677
|
txHash,
|
|
1342
1678
|
depositId,
|
|
1343
|
-
|
|
1679
|
+
lastError,
|
|
1680
|
+
error: lastError,
|
|
1344
1681
|
isLoading,
|
|
1345
1682
|
offramp: offramp2,
|
|
1346
1683
|
deposits,
|