@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/index.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,38 +15,41 @@ 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/index.ts
|
|
31
21
|
var index_exports = {};
|
|
32
22
|
__export(index_exports, {
|
|
33
23
|
CURRENCIES: () => CURRENCIES,
|
|
24
|
+
ESCROW_ADDRESS: () => ESCROW_ADDRESS,
|
|
25
|
+
OFFRAMP_ERROR_CODES: () => OFFRAMP_ERROR_CODES,
|
|
26
|
+
Offramp: () => Offramp,
|
|
34
27
|
OfframpError: () => OfframpError,
|
|
35
28
|
PLATFORMS: () => PLATFORMS,
|
|
36
29
|
close: () => close,
|
|
30
|
+
createOfframp: () => createOfframp,
|
|
31
|
+
createTelemetryContext: () => createTelemetryContext,
|
|
32
|
+
delegate: () => delegate,
|
|
37
33
|
deposits: () => deposits,
|
|
38
34
|
disableOtc: () => disableOtc,
|
|
35
|
+
emitEvent: () => emitEvent,
|
|
39
36
|
enableOtc: () => enableOtc,
|
|
40
37
|
getOtcLink: () => getOtcLink,
|
|
41
|
-
offramp: () => offramp
|
|
38
|
+
offramp: () => offramp,
|
|
39
|
+
sanitizeAttributionId: () => sanitizeAttributionId,
|
|
40
|
+
sendTelemetryEvent: () => sendTelemetryEvent,
|
|
41
|
+
undelegate: () => undelegate
|
|
42
42
|
});
|
|
43
43
|
module.exports = __toCommonJS(index_exports);
|
|
44
44
|
|
|
45
45
|
// src/deposit.ts
|
|
46
46
|
var import_viem3 = require("viem");
|
|
47
|
-
var
|
|
48
|
-
var
|
|
47
|
+
var import_chains3 = require("viem/chains");
|
|
48
|
+
var import_sdk5 = require("@zkp2p/sdk");
|
|
49
49
|
|
|
50
50
|
// src/config.ts
|
|
51
51
|
var import_sdk = require("@zkp2p/sdk");
|
|
52
|
+
var SDK_VERSION = "1.2.0";
|
|
52
53
|
var BASE_CHAIN_ID = 8453;
|
|
53
54
|
var RUNTIME_ENV = "production";
|
|
54
55
|
var API_BASE_URL = "https://api.zkp2p.xyz";
|
|
@@ -64,6 +65,7 @@ var GATING_SERVICE_ADDRESS = (0, import_sdk.getGatingServiceAddress)(
|
|
|
64
65
|
);
|
|
65
66
|
var DELEGATE_RATE_MANAGER_ID = "0x8666d6fb0f6797c56e95339fd7ca82fdd348b9db200e10a4c4aa0a0b879fc41c";
|
|
66
67
|
var RATE_MANAGER_REGISTRY_ADDRESS = "0xeed7db23e724ac4590d6db6f78fda6db203535f3";
|
|
68
|
+
var DELEGATE_MANAGER_FEE_BPS = 10;
|
|
67
69
|
var REFERRER = "galleonlabs";
|
|
68
70
|
var MIN_DEPOSIT_USDC = 1;
|
|
69
71
|
var MIN_ORDER_USDC = 1;
|
|
@@ -387,6 +389,7 @@ function isUserCancellation(error) {
|
|
|
387
389
|
|
|
388
390
|
// src/otc.ts
|
|
389
391
|
var import_viem = require("viem");
|
|
392
|
+
var import_chains = require("viem/chains");
|
|
390
393
|
|
|
391
394
|
// ../../node_modules/@zkp2p/contracts-v2/abis/base/WhitelistPreIntentHook.json
|
|
392
395
|
var WhitelistPreIntentHook_default = [
|
|
@@ -731,6 +734,18 @@ var WhitelistPreIntentHook_default = [
|
|
|
731
734
|
}
|
|
732
735
|
];
|
|
733
736
|
|
|
737
|
+
// src/sdk-client.ts
|
|
738
|
+
var import_sdk3 = require("@zkp2p/sdk");
|
|
739
|
+
function createSdkClient(walletClient) {
|
|
740
|
+
return new import_sdk3.OfframpClient({
|
|
741
|
+
walletClient,
|
|
742
|
+
chainId: BASE_CHAIN_ID,
|
|
743
|
+
runtimeEnv: RUNTIME_ENV,
|
|
744
|
+
rpcUrl: BASE_RPC_URL,
|
|
745
|
+
baseApiUrl: API_BASE_URL
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
|
|
734
749
|
// src/otc.ts
|
|
735
750
|
var WHITELIST_HOOK_ADDRESS = "0xda023Ea0d789A41BcF5866F7B6BBd2CaDF9b79B8";
|
|
736
751
|
var OTC_BASE_URL = "https://usdctofiat.xyz";
|
|
@@ -748,12 +763,7 @@ async function enableOtc(walletClient, depositId, takerAddress, escrowAddress) {
|
|
|
748
763
|
escrowAddress: escrow
|
|
749
764
|
});
|
|
750
765
|
if (currentHook !== import_viem.zeroAddress) {
|
|
751
|
-
const isAlready = await readIsWhitelisted(
|
|
752
|
-
walletClient,
|
|
753
|
-
escrow,
|
|
754
|
-
depositIdBig,
|
|
755
|
-
taker
|
|
756
|
-
);
|
|
766
|
+
const isAlready = await readIsWhitelisted(escrow, depositIdBig, taker);
|
|
757
767
|
if (isAlready) {
|
|
758
768
|
return {
|
|
759
769
|
depositId,
|
|
@@ -824,13 +834,8 @@ async function writeContract(walletClient, functionName, args) {
|
|
|
824
834
|
args
|
|
825
835
|
});
|
|
826
836
|
}
|
|
827
|
-
async function readIsWhitelisted(
|
|
828
|
-
const {
|
|
829
|
-
const { base: base2 } = await import("viem/chains");
|
|
830
|
-
const publicClient = createPublicClient2({
|
|
831
|
-
chain: base2,
|
|
832
|
-
transport: http2("https://mainnet.base.org")
|
|
833
|
-
});
|
|
837
|
+
async function readIsWhitelisted(escrow, depositId, taker) {
|
|
838
|
+
const publicClient = (0, import_viem.createPublicClient)({ chain: import_chains.base, transport: (0, import_viem.http)(BASE_RPC_URL) });
|
|
834
839
|
return publicClient.readContract({
|
|
835
840
|
address: WHITELIST_HOOK_ADDRESS,
|
|
836
841
|
abi: WhitelistPreIntentHook_default,
|
|
@@ -841,13 +846,14 @@ async function readIsWhitelisted(walletClient, escrow, depositId, taker) {
|
|
|
841
846
|
|
|
842
847
|
// src/queries.ts
|
|
843
848
|
var import_viem2 = require("viem");
|
|
844
|
-
var
|
|
849
|
+
var import_chains2 = require("viem/chains");
|
|
850
|
+
var import_sdk4 = require("@zkp2p/sdk");
|
|
845
851
|
var indexerService = null;
|
|
846
852
|
function getIndexerService() {
|
|
847
853
|
if (!indexerService) {
|
|
848
|
-
const endpoint = (0,
|
|
849
|
-
const client = new
|
|
850
|
-
indexerService = new
|
|
854
|
+
const endpoint = (0, import_sdk4.defaultIndexerEndpoint)("PRODUCTION");
|
|
855
|
+
const client = new import_sdk4.IndexerClient(endpoint);
|
|
856
|
+
indexerService = new import_sdk4.IndexerDepositService(client);
|
|
851
857
|
}
|
|
852
858
|
return indexerService;
|
|
853
859
|
}
|
|
@@ -881,7 +887,7 @@ function resolveMethodNames(hashes) {
|
|
|
881
887
|
return Array.from(names);
|
|
882
888
|
}
|
|
883
889
|
function mapDeposit(d) {
|
|
884
|
-
const delegationState = (0,
|
|
890
|
+
const delegationState = (0, import_sdk4.classifyDelegationState)(
|
|
885
891
|
d.rateManagerId ?? void 0,
|
|
886
892
|
d.rateManagerAddress ?? void 0,
|
|
887
893
|
DELEGATE_RATE_MANAGER_ID,
|
|
@@ -898,7 +904,7 @@ function mapDeposit(d) {
|
|
|
898
904
|
fulfilledIntents: d.fulfilledIntents ?? 0,
|
|
899
905
|
paymentMethods: resolveMethodNames(d.paymentMethods),
|
|
900
906
|
currencies: d.currencies.map((c) => {
|
|
901
|
-
const info = (0,
|
|
907
|
+
const info = (0, import_sdk4.getCurrencyInfoFromHash)(c.currencyCode);
|
|
902
908
|
return info?.currencyCode ?? c.currencyCode;
|
|
903
909
|
}),
|
|
904
910
|
rateSource: d.currencies[0]?.rateSource || "unknown",
|
|
@@ -914,6 +920,18 @@ function extractTxHash(result) {
|
|
|
914
920
|
return result.transactionHash;
|
|
915
921
|
throw new Error("Unexpected transaction result format");
|
|
916
922
|
}
|
|
923
|
+
var ESCROW_V2_ABI = [
|
|
924
|
+
{
|
|
925
|
+
name: "clearRateManager",
|
|
926
|
+
type: "function",
|
|
927
|
+
stateMutability: "nonpayable",
|
|
928
|
+
inputs: [{ name: "depositId", type: "uint256" }],
|
|
929
|
+
outputs: []
|
|
930
|
+
}
|
|
931
|
+
];
|
|
932
|
+
function appendReferrerAttribution(calldata) {
|
|
933
|
+
return (0, import_viem2.concatHex)([calldata, (0, import_sdk4.getAttributionDataSuffix)([REFERRER])]);
|
|
934
|
+
}
|
|
917
935
|
async function deposits(walletAddress) {
|
|
918
936
|
const service = getIndexerService();
|
|
919
937
|
const raw = await service.fetchDepositsWithRelations(
|
|
@@ -936,6 +954,159 @@ async function close(walletClient, depositId, escrowAddress) {
|
|
|
936
954
|
});
|
|
937
955
|
return extractTxHash(result);
|
|
938
956
|
}
|
|
957
|
+
async function delegate(walletClient, depositId, escrowAddress) {
|
|
958
|
+
const client = createSdkClient(walletClient);
|
|
959
|
+
const result = await client.setRateManager({
|
|
960
|
+
depositId: BigInt(depositId),
|
|
961
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
962
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
963
|
+
escrowAddress: escrowAddress || ESCROW_ADDRESS,
|
|
964
|
+
txOverrides: { referrer: [REFERRER] }
|
|
965
|
+
});
|
|
966
|
+
return extractTxHash(result);
|
|
967
|
+
}
|
|
968
|
+
async function undelegate(walletClient, depositId, escrowAddress) {
|
|
969
|
+
const account = walletClient.account;
|
|
970
|
+
if (!account) {
|
|
971
|
+
throw new Error("Wallet client has no account. Connect a wallet first.");
|
|
972
|
+
}
|
|
973
|
+
const data = appendReferrerAttribution(
|
|
974
|
+
(0, import_viem2.encodeFunctionData)({
|
|
975
|
+
abi: ESCROW_V2_ABI,
|
|
976
|
+
functionName: "clearRateManager",
|
|
977
|
+
args: [BigInt(depositId)]
|
|
978
|
+
})
|
|
979
|
+
);
|
|
980
|
+
const hash = await walletClient.sendTransaction({
|
|
981
|
+
account,
|
|
982
|
+
chain: import_chains2.base,
|
|
983
|
+
data,
|
|
984
|
+
to: escrowAddress || ESCROW_ADDRESS
|
|
985
|
+
});
|
|
986
|
+
const publicClient = (0, import_viem2.createPublicClient)({
|
|
987
|
+
chain: import_chains2.base,
|
|
988
|
+
transport: (0, import_viem2.http)(BASE_RPC_URL)
|
|
989
|
+
});
|
|
990
|
+
try {
|
|
991
|
+
const receipt = await publicClient.waitForTransactionReceipt({
|
|
992
|
+
hash,
|
|
993
|
+
confirmations: 1,
|
|
994
|
+
timeout: 12e4,
|
|
995
|
+
pollingInterval: 4e3
|
|
996
|
+
});
|
|
997
|
+
if (receipt.status !== "success") {
|
|
998
|
+
throw new Error("Undelegation transaction failed");
|
|
999
|
+
}
|
|
1000
|
+
} catch (err) {
|
|
1001
|
+
const isTimeout = err instanceof Error && err.message.toLowerCase().includes("timed out");
|
|
1002
|
+
if (!isTimeout) {
|
|
1003
|
+
throw err;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return hash;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/telemetry.ts
|
|
1010
|
+
var SDK_EVENTS_ENDPOINT = "https://usdctofiat.xyz/api/sdk-events";
|
|
1011
|
+
var MAX_BATCH_SIZE = 50;
|
|
1012
|
+
var FLUSH_DELAY_MS = 180;
|
|
1013
|
+
var TELEMETRY_TIMEOUT_MS = 2e3;
|
|
1014
|
+
var queuedEvents = [];
|
|
1015
|
+
var flushTimer = null;
|
|
1016
|
+
var flushInFlight = false;
|
|
1017
|
+
function sanitizeAttributionId(raw) {
|
|
1018
|
+
if (typeof raw !== "string") return void 0;
|
|
1019
|
+
const stripped = raw.trim().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
1020
|
+
if (!stripped) return void 0;
|
|
1021
|
+
if (stripped.length > 64) return void 0;
|
|
1022
|
+
return stripped;
|
|
1023
|
+
}
|
|
1024
|
+
function scheduleFlush() {
|
|
1025
|
+
if (flushTimer != null) return;
|
|
1026
|
+
flushTimer = setTimeout(() => {
|
|
1027
|
+
flushTimer = null;
|
|
1028
|
+
void flushQueue();
|
|
1029
|
+
}, FLUSH_DELAY_MS);
|
|
1030
|
+
}
|
|
1031
|
+
async function postBatch(batch) {
|
|
1032
|
+
if (!batch.length) return;
|
|
1033
|
+
const body = JSON.stringify(batch);
|
|
1034
|
+
try {
|
|
1035
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
1036
|
+
const blob = new Blob([body], { type: "application/json" });
|
|
1037
|
+
const accepted = navigator.sendBeacon(SDK_EVENTS_ENDPOINT, blob);
|
|
1038
|
+
if (accepted) return;
|
|
1039
|
+
}
|
|
1040
|
+
} catch {
|
|
1041
|
+
}
|
|
1042
|
+
try {
|
|
1043
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
|
|
1044
|
+
const timeout = controller ? setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS) : null;
|
|
1045
|
+
await fetch(SDK_EVENTS_ENDPOINT, {
|
|
1046
|
+
method: "POST",
|
|
1047
|
+
headers: { "Content-Type": "application/json" },
|
|
1048
|
+
body,
|
|
1049
|
+
keepalive: true,
|
|
1050
|
+
signal: controller?.signal
|
|
1051
|
+
}).catch(() => {
|
|
1052
|
+
});
|
|
1053
|
+
if (timeout) clearTimeout(timeout);
|
|
1054
|
+
} catch {
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
async function flushQueue() {
|
|
1058
|
+
if (flushInFlight) return;
|
|
1059
|
+
if (!queuedEvents.length) return;
|
|
1060
|
+
flushInFlight = true;
|
|
1061
|
+
try {
|
|
1062
|
+
while (queuedEvents.length > 0) {
|
|
1063
|
+
const batch = queuedEvents.slice(0, MAX_BATCH_SIZE);
|
|
1064
|
+
queuedEvents = queuedEvents.slice(batch.length);
|
|
1065
|
+
await postBatch(batch);
|
|
1066
|
+
}
|
|
1067
|
+
} finally {
|
|
1068
|
+
flushInFlight = false;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
function emitEvent(name, payload) {
|
|
1072
|
+
try {
|
|
1073
|
+
queuedEvents.push({
|
|
1074
|
+
name,
|
|
1075
|
+
payload,
|
|
1076
|
+
ts: Date.now()
|
|
1077
|
+
});
|
|
1078
|
+
if (queuedEvents.length >= MAX_BATCH_SIZE) {
|
|
1079
|
+
void flushQueue();
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
scheduleFlush();
|
|
1083
|
+
} catch {
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
function sendTelemetryEvent(name, payload) {
|
|
1087
|
+
emitEvent(name, payload);
|
|
1088
|
+
}
|
|
1089
|
+
function createTelemetryContext(options) {
|
|
1090
|
+
const integratorId = sanitizeAttributionId(options.integratorId);
|
|
1091
|
+
const referralId = sanitizeAttributionId(options.referralId);
|
|
1092
|
+
const enabled = options.telemetry !== false;
|
|
1093
|
+
const sdkVersion = options.sdkVersion;
|
|
1094
|
+
return {
|
|
1095
|
+
enabled,
|
|
1096
|
+
sdkVersion,
|
|
1097
|
+
integratorId,
|
|
1098
|
+
referralId,
|
|
1099
|
+
emit(name, payload = {}) {
|
|
1100
|
+
if (!enabled) return;
|
|
1101
|
+
emitEvent(name, {
|
|
1102
|
+
sdkVersion,
|
|
1103
|
+
integratorId,
|
|
1104
|
+
referralId,
|
|
1105
|
+
...payload
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
939
1110
|
|
|
940
1111
|
// src/deposit.ts
|
|
941
1112
|
function usdcToUnits(amount) {
|
|
@@ -1010,7 +1181,7 @@ function attachOracleConfig(entries, conversionRates) {
|
|
|
1010
1181
|
(group, gi) => group.map((entry, ci) => {
|
|
1011
1182
|
const currency = conversionRates[gi]?.[ci]?.currency;
|
|
1012
1183
|
if (!currency) return entry;
|
|
1013
|
-
const oracleConfig = (0,
|
|
1184
|
+
const oracleConfig = (0, import_sdk5.getSpreadOracleConfig)(currency);
|
|
1014
1185
|
if (!oracleConfig) return entry;
|
|
1015
1186
|
return {
|
|
1016
1187
|
...entry,
|
|
@@ -1032,15 +1203,6 @@ function extractTxHash2(result) {
|
|
|
1032
1203
|
return result.transactionHash;
|
|
1033
1204
|
throw new Error("Unexpected transaction result format");
|
|
1034
1205
|
}
|
|
1035
|
-
function createSdkClient(walletClient) {
|
|
1036
|
-
return new import_sdk4.OfframpClient({
|
|
1037
|
-
walletClient,
|
|
1038
|
-
chainId: BASE_CHAIN_ID,
|
|
1039
|
-
runtimeEnv: RUNTIME_ENV,
|
|
1040
|
-
rpcUrl: BASE_RPC_URL,
|
|
1041
|
-
baseApiUrl: API_BASE_URL
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
1206
|
async function findUndelegatedDeposit(walletAddress) {
|
|
1045
1207
|
try {
|
|
1046
1208
|
const service = getIndexerService();
|
|
@@ -1049,6 +1211,7 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1049
1211
|
{ limit: 50 }
|
|
1050
1212
|
);
|
|
1051
1213
|
const escrowLower = ESCROW_ADDRESS.toLowerCase();
|
|
1214
|
+
const delegateIdLower = DELEGATE_RATE_MANAGER_ID.toLowerCase();
|
|
1052
1215
|
const undelegated = (raw || []).filter((d) => {
|
|
1053
1216
|
if (d.status === "CLOSED") return false;
|
|
1054
1217
|
if (d.escrowAddress.toLowerCase() !== escrowLower) return false;
|
|
@@ -1060,266 +1223,326 @@ async function findUndelegatedDeposit(walletAddress) {
|
|
|
1060
1223
|
}
|
|
1061
1224
|
})();
|
|
1062
1225
|
if (remaining === 0n) return false;
|
|
1063
|
-
return d.rateManagerId !==
|
|
1226
|
+
return (d.rateManagerId ?? "").toLowerCase() !== delegateIdLower;
|
|
1227
|
+
});
|
|
1228
|
+
undelegated.sort((a, b) => {
|
|
1229
|
+
const left = BigInt(a.depositId);
|
|
1230
|
+
const right = BigInt(b.depositId);
|
|
1231
|
+
if (left === right) return 0;
|
|
1232
|
+
return left > right ? -1 : 1;
|
|
1064
1233
|
});
|
|
1065
|
-
undelegated.sort((a, b) => Number(BigInt(b.depositId) - BigInt(a.depositId)));
|
|
1066
1234
|
return undelegated[0] ?? null;
|
|
1067
1235
|
} catch {
|
|
1068
1236
|
return null;
|
|
1069
1237
|
}
|
|
1070
1238
|
}
|
|
1071
|
-
async function offramp(walletClient, params, onProgress) {
|
|
1239
|
+
async function offramp(walletClient, params, onProgress, telemetryContext) {
|
|
1072
1240
|
const { amount, platform, currency, identifier } = params;
|
|
1073
1241
|
const platformId = platform.id;
|
|
1074
1242
|
const currencyCode = currency.code;
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1243
|
+
const telemetry = telemetryContext ?? createTelemetryContext({
|
|
1244
|
+
sdkVersion: SDK_VERSION,
|
|
1245
|
+
telemetry: true,
|
|
1246
|
+
integratorId: params.integratorId,
|
|
1247
|
+
referralId: params.referralId
|
|
1248
|
+
});
|
|
1249
|
+
const flowStartMs = Date.now();
|
|
1250
|
+
const emitProgress = (progress) => {
|
|
1251
|
+
onProgress?.(progress);
|
|
1252
|
+
telemetry.emit("sdk.createDeposit.progress", {
|
|
1253
|
+
step: progress.step,
|
|
1254
|
+
txHash: progress.txHash
|
|
1255
|
+
});
|
|
1256
|
+
};
|
|
1257
|
+
try {
|
|
1258
|
+
if (!walletClient.account?.address) {
|
|
1259
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1260
|
+
}
|
|
1261
|
+
const walletAddress = walletClient.account.address.toLowerCase();
|
|
1262
|
+
telemetry.emit("sdk.createDeposit.start", {
|
|
1263
|
+
platform: platformId,
|
|
1264
|
+
currency: currencyCode,
|
|
1265
|
+
amount,
|
|
1266
|
+
wallet: walletAddress
|
|
1267
|
+
});
|
|
1268
|
+
const amt = parseFloat(amount);
|
|
1269
|
+
if (!Number.isFinite(amt) || amt < MIN_DEPOSIT_USDC) {
|
|
1270
|
+
throw new OfframpError(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC`, "VALIDATION");
|
|
1271
|
+
}
|
|
1272
|
+
if (!platform.currencies.includes(currencyCode)) {
|
|
1273
|
+
throw new OfframpError(`${currencyCode} is not supported on ${platform.name}`, "UNSUPPORTED");
|
|
1274
|
+
}
|
|
1275
|
+
const validation = platform.validate(identifier);
|
|
1276
|
+
if (!validation.valid) {
|
|
1277
|
+
throw new OfframpError(validation.error || "Invalid identifier", "VALIDATION");
|
|
1278
|
+
}
|
|
1279
|
+
const normalizedIdentifier = validation.normalized;
|
|
1280
|
+
const methodHash = getPaymentMethodHash(platformId);
|
|
1281
|
+
if (!methodHash) {
|
|
1282
|
+
throw new OfframpError(`${platform.name} is not currently supported`, "UNSUPPORTED");
|
|
1283
|
+
}
|
|
1284
|
+
const existing = await findUndelegatedDeposit(walletAddress);
|
|
1285
|
+
if (existing) {
|
|
1286
|
+
emitProgress({ step: "resuming", depositId: existing.depositId });
|
|
1287
|
+
const client2 = createSdkClient(walletClient);
|
|
1288
|
+
const txOverrides2 = { referrer: [REFERRER] };
|
|
1289
|
+
try {
|
|
1290
|
+
const result2 = await client2.setRateManager({
|
|
1291
|
+
depositId: BigInt(existing.depositId),
|
|
1292
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1293
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1294
|
+
escrowAddress: existing.escrowAddress,
|
|
1295
|
+
txOverrides: txOverrides2
|
|
1296
|
+
});
|
|
1297
|
+
const txHash = extractTxHash2(result2);
|
|
1298
|
+
emitProgress({ step: "done", txHash, depositId: existing.depositId });
|
|
1299
|
+
const resumedResult = { depositId: existing.depositId, txHash, resumed: true };
|
|
1300
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1301
|
+
depositId: resumedResult.depositId,
|
|
1302
|
+
txHash: resumedResult.txHash,
|
|
1303
|
+
wallet: walletAddress,
|
|
1304
|
+
resumed: true,
|
|
1305
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1306
|
+
});
|
|
1307
|
+
return resumedResult;
|
|
1308
|
+
} catch (err) {
|
|
1309
|
+
if (isUserCancellation(err))
|
|
1310
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "resuming", err);
|
|
1311
|
+
throw new OfframpError(
|
|
1312
|
+
"Found undelegated deposit but delegation failed. Try again.",
|
|
1313
|
+
"DELEGATION_FAILED",
|
|
1314
|
+
"resuming",
|
|
1315
|
+
err,
|
|
1316
|
+
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
|
|
1321
|
+
const amountUnits = usdcToUnits(truncatedAmount);
|
|
1322
|
+
const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
|
|
1323
|
+
const maxUnits = usdcToUnits(String(Math.min(amt, 2500)));
|
|
1100
1324
|
try {
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1325
|
+
const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains3.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
|
|
1326
|
+
const balance = await publicClient.readContract({
|
|
1327
|
+
address: USDC_ADDRESS,
|
|
1328
|
+
abi: [
|
|
1329
|
+
{
|
|
1330
|
+
name: "balanceOf",
|
|
1331
|
+
type: "function",
|
|
1332
|
+
stateMutability: "view",
|
|
1333
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1334
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1335
|
+
}
|
|
1336
|
+
],
|
|
1337
|
+
functionName: "balanceOf",
|
|
1338
|
+
args: [walletAddress]
|
|
1339
|
+
});
|
|
1340
|
+
if (balance < amountUnits) {
|
|
1341
|
+
const available = Number((0, import_viem3.formatUnits)(balance, 6));
|
|
1342
|
+
throw new OfframpError(
|
|
1343
|
+
`Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
|
|
1344
|
+
"VALIDATION"
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
} catch (err) {
|
|
1348
|
+
if (err instanceof OfframpError) throw err;
|
|
1349
|
+
}
|
|
1350
|
+
const client = createSdkClient(walletClient);
|
|
1351
|
+
const txOverrides = { referrer: [REFERRER] };
|
|
1352
|
+
emitProgress({ step: "approving" });
|
|
1353
|
+
try {
|
|
1354
|
+
await client.ensureAllowance({
|
|
1355
|
+
token: USDC_ADDRESS,
|
|
1356
|
+
amount: amountUnits,
|
|
1357
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1358
|
+
maxApprove: false,
|
|
1359
|
+
txOverrides
|
|
1107
1360
|
});
|
|
1108
|
-
const txHash = extractTxHash2(result);
|
|
1109
|
-
onProgress?.({ step: "done", txHash, depositId: existing.depositId });
|
|
1110
|
-
return { depositId: existing.depositId, txHash, resumed: true };
|
|
1111
1361
|
} catch (err) {
|
|
1112
1362
|
if (isUserCancellation(err))
|
|
1113
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "
|
|
1363
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1364
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1114
1365
|
throw new OfframpError(
|
|
1115
|
-
|
|
1116
|
-
"
|
|
1117
|
-
"
|
|
1118
|
-
err
|
|
1119
|
-
{ depositId: existing.depositId, txHash: existing.txHash }
|
|
1366
|
+
`USDC approval failed: ${detail}`,
|
|
1367
|
+
"APPROVAL_FAILED",
|
|
1368
|
+
"approving",
|
|
1369
|
+
err
|
|
1120
1370
|
);
|
|
1121
1371
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
const balance = await publicClient.readContract({
|
|
1130
|
-
address: USDC_ADDRESS,
|
|
1131
|
-
abi: [
|
|
1132
|
-
{
|
|
1133
|
-
name: "balanceOf",
|
|
1134
|
-
type: "function",
|
|
1135
|
-
stateMutability: "view",
|
|
1136
|
-
inputs: [{ name: "account", type: "address" }],
|
|
1137
|
-
outputs: [{ name: "", type: "uint256" }]
|
|
1138
|
-
}
|
|
1139
|
-
],
|
|
1140
|
-
functionName: "balanceOf",
|
|
1141
|
-
args: [walletAddress]
|
|
1142
|
-
});
|
|
1143
|
-
if (balance < amountUnits) {
|
|
1144
|
-
const available = Number((0, import_viem3.formatUnits)(balance, 6));
|
|
1372
|
+
emitProgress({ step: "registering" });
|
|
1373
|
+
let hashedOnchainId;
|
|
1374
|
+
try {
|
|
1375
|
+
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1376
|
+
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1377
|
+
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1378
|
+
} catch (err) {
|
|
1145
1379
|
throw new OfframpError(
|
|
1146
|
-
|
|
1147
|
-
"
|
|
1380
|
+
"Payee registration failed",
|
|
1381
|
+
"REGISTRATION_FAILED",
|
|
1382
|
+
"registering",
|
|
1383
|
+
err
|
|
1148
1384
|
);
|
|
1149
1385
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
await client.ensureAllowance({
|
|
1158
|
-
token: USDC_ADDRESS,
|
|
1159
|
-
amount: amountUnits,
|
|
1160
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1161
|
-
maxApprove: false,
|
|
1162
|
-
txOverrides
|
|
1163
|
-
});
|
|
1164
|
-
} catch (err) {
|
|
1165
|
-
if (isUserCancellation(err))
|
|
1166
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "approving", err);
|
|
1167
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1168
|
-
throw new OfframpError(`USDC approval failed: ${detail}`, "APPROVAL_FAILED", "approving", err);
|
|
1169
|
-
}
|
|
1170
|
-
onProgress?.({ step: "registering" });
|
|
1171
|
-
let hashedOnchainId;
|
|
1172
|
-
try {
|
|
1173
|
-
const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
|
|
1174
|
-
const depositData = buildDepositData(platformId, normalizedIdentifier);
|
|
1175
|
-
hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
|
|
1176
|
-
} catch (err) {
|
|
1177
|
-
throw new OfframpError("Payee registration failed", "REGISTRATION_FAILED", "registering", err);
|
|
1178
|
-
}
|
|
1179
|
-
onProgress?.({ step: "depositing" });
|
|
1180
|
-
const conversionRates = [
|
|
1181
|
-
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1182
|
-
];
|
|
1183
|
-
const baseCurrenciesOverride = (0, import_sdk4.mapConversionRatesToOnchainMinRate)(conversionRates, 1);
|
|
1184
|
-
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1185
|
-
let hash;
|
|
1186
|
-
try {
|
|
1187
|
-
const result = await client.createDeposit({
|
|
1188
|
-
token: USDC_ADDRESS,
|
|
1189
|
-
amount: amountUnits,
|
|
1190
|
-
retainOnEmpty: false,
|
|
1191
|
-
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1192
|
-
processorNames: [platformId],
|
|
1193
|
-
conversionRates,
|
|
1194
|
-
payeeDetailsHashes: [hashedOnchainId],
|
|
1195
|
-
paymentMethodsOverride: [methodHash],
|
|
1196
|
-
paymentMethodDataOverride: [
|
|
1197
|
-
{
|
|
1198
|
-
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1199
|
-
payeeDetails: hashedOnchainId,
|
|
1200
|
-
data: "0x"
|
|
1201
|
-
}
|
|
1202
|
-
],
|
|
1203
|
-
currenciesOverride,
|
|
1204
|
-
escrowAddress: ESCROW_ADDRESS,
|
|
1205
|
-
txOverrides
|
|
1206
|
-
});
|
|
1207
|
-
if (!result?.hash) throw new Error("No transaction hash returned");
|
|
1208
|
-
hash = result.hash;
|
|
1209
|
-
} catch (err) {
|
|
1210
|
-
if (isUserCancellation(err))
|
|
1211
|
-
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1212
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
1213
|
-
throw new OfframpError(
|
|
1214
|
-
`Deposit transaction failed: ${detail}`,
|
|
1215
|
-
"DEPOSIT_FAILED",
|
|
1216
|
-
"depositing",
|
|
1217
|
-
err
|
|
1218
|
-
);
|
|
1219
|
-
}
|
|
1220
|
-
onProgress?.({ step: "confirming", txHash: hash });
|
|
1221
|
-
let depositId = "";
|
|
1222
|
-
const receiptClient = client;
|
|
1223
|
-
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1386
|
+
emitProgress({ step: "depositing" });
|
|
1387
|
+
const conversionRates = [
|
|
1388
|
+
[{ currency: currencyCode, conversionRate: "1" }]
|
|
1389
|
+
];
|
|
1390
|
+
const baseCurrenciesOverride = (0, import_sdk5.mapConversionRatesToOnchainMinRate)(conversionRates, 1);
|
|
1391
|
+
const currenciesOverride = attachOracleConfig(baseCurrenciesOverride, conversionRates);
|
|
1392
|
+
let hash;
|
|
1224
1393
|
try {
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
|
|
1394
|
+
const result2 = await client.createDeposit({
|
|
1395
|
+
token: USDC_ADDRESS,
|
|
1396
|
+
amount: amountUnits,
|
|
1397
|
+
retainOnEmpty: false,
|
|
1398
|
+
intentAmountRange: { min: minUnits, max: maxUnits },
|
|
1399
|
+
processorNames: [platformId],
|
|
1400
|
+
conversionRates,
|
|
1401
|
+
payeeDetailsHashes: [hashedOnchainId],
|
|
1402
|
+
paymentMethodsOverride: [methodHash],
|
|
1403
|
+
paymentMethodDataOverride: [
|
|
1404
|
+
{
|
|
1405
|
+
intentGatingService: GATING_SERVICE_ADDRESS,
|
|
1406
|
+
payeeDetails: hashedOnchainId,
|
|
1407
|
+
data: "0x"
|
|
1408
|
+
}
|
|
1409
|
+
],
|
|
1410
|
+
currenciesOverride,
|
|
1411
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1412
|
+
txOverrides
|
|
1413
|
+
});
|
|
1414
|
+
if (!result2?.hash) throw new Error("No transaction hash returned");
|
|
1415
|
+
hash = result2.hash;
|
|
1416
|
+
} catch (err) {
|
|
1417
|
+
if (isUserCancellation(err))
|
|
1418
|
+
throw new OfframpError("User cancelled", "USER_CANCELLED", "depositing", err);
|
|
1419
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1420
|
+
throw new OfframpError(
|
|
1421
|
+
`Deposit transaction failed: ${detail}`,
|
|
1422
|
+
"DEPOSIT_FAILED",
|
|
1423
|
+
"depositing",
|
|
1424
|
+
err
|
|
1425
|
+
);
|
|
1228
1426
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1427
|
+
emitProgress({ step: "confirming", txHash: hash });
|
|
1428
|
+
let depositId = "";
|
|
1429
|
+
const receiptClient = client;
|
|
1430
|
+
if (typeof receiptClient.waitForTransactionReceipt === "function") {
|
|
1233
1431
|
try {
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
{ limit: 25 }
|
|
1237
|
-
);
|
|
1238
|
-
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1239
|
-
if (hit) {
|
|
1240
|
-
depositId = String(hit.depositId);
|
|
1241
|
-
break;
|
|
1242
|
-
}
|
|
1432
|
+
const receipt = await receiptClient.waitForTransactionReceipt({ hash, confirmations: 1 });
|
|
1433
|
+
depositId = extractDepositIdFromLogs(receipt.logs) || "";
|
|
1243
1434
|
} catch {
|
|
1244
1435
|
}
|
|
1245
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
1246
|
-
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1247
1436
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
if (isUserCancellation(delegationError)) {
|
|
1437
|
+
if (!depositId) {
|
|
1438
|
+
let delay = INDEXER_INITIAL_DELAY_MS;
|
|
1439
|
+
for (let attempt = 0; attempt < INDEXER_MAX_ATTEMPTS && !depositId; attempt++) {
|
|
1440
|
+
try {
|
|
1441
|
+
const deps = await client.indexer.getDepositsWithRelations(
|
|
1442
|
+
{ depositor: walletAddress },
|
|
1443
|
+
{ limit: 25 }
|
|
1444
|
+
);
|
|
1445
|
+
const hit = deps.find((d) => (d?.txHash || "").toLowerCase() === hash.toLowerCase());
|
|
1446
|
+
if (hit) {
|
|
1447
|
+
depositId = String(hit.depositId);
|
|
1448
|
+
break;
|
|
1449
|
+
}
|
|
1450
|
+
} catch {
|
|
1451
|
+
}
|
|
1452
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1453
|
+
delay = Math.min(INDEXER_MAX_DELAY_MS, Math.floor(delay * 1.7));
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (!depositId) {
|
|
1269
1457
|
throw new OfframpError(
|
|
1270
|
-
"
|
|
1271
|
-
"
|
|
1272
|
-
"
|
|
1273
|
-
|
|
1274
|
-
{ txHash: hash
|
|
1458
|
+
"Deposit created on-chain but could not confirm deposit ID. Your funds are safe. Call offramp() again to resume delegation.",
|
|
1459
|
+
"CONFIRMATION_FAILED",
|
|
1460
|
+
"confirming",
|
|
1461
|
+
void 0,
|
|
1462
|
+
{ txHash: hash }
|
|
1275
1463
|
);
|
|
1276
1464
|
}
|
|
1277
|
-
|
|
1278
|
-
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1279
|
-
"DELEGATION_FAILED",
|
|
1280
|
-
"delegating",
|
|
1281
|
-
delegationError,
|
|
1282
|
-
{ txHash: hash, depositId }
|
|
1283
|
-
);
|
|
1284
|
-
}
|
|
1285
|
-
let otcLink;
|
|
1286
|
-
if (params.otcTaker) {
|
|
1287
|
-
onProgress?.({ step: "restricting", txHash: hash, depositId });
|
|
1465
|
+
emitProgress({ step: "delegating", txHash: hash, depositId });
|
|
1288
1466
|
try {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1467
|
+
await client.setRateManager({
|
|
1468
|
+
depositId: BigInt(depositId),
|
|
1469
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1470
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1471
|
+
escrowAddress: ESCROW_ADDRESS,
|
|
1472
|
+
txOverrides
|
|
1473
|
+
});
|
|
1474
|
+
} catch (delegationError) {
|
|
1475
|
+
if (isUserCancellation(delegationError)) {
|
|
1293
1476
|
throw new OfframpError(
|
|
1294
|
-
"User cancelled
|
|
1477
|
+
"User cancelled delegation",
|
|
1295
1478
|
"USER_CANCELLED",
|
|
1296
|
-
"
|
|
1297
|
-
|
|
1298
|
-
{
|
|
1299
|
-
txHash: hash,
|
|
1300
|
-
depositId
|
|
1301
|
-
}
|
|
1479
|
+
"delegating",
|
|
1480
|
+
delegationError,
|
|
1481
|
+
{ txHash: hash, depositId }
|
|
1302
1482
|
);
|
|
1303
1483
|
}
|
|
1304
1484
|
throw new OfframpError(
|
|
1305
|
-
"Deposit created
|
|
1306
|
-
"
|
|
1307
|
-
"
|
|
1308
|
-
|
|
1485
|
+
"Deposit created but delegation failed. Call offramp() again to resume delegation.",
|
|
1486
|
+
"DELEGATION_FAILED",
|
|
1487
|
+
"delegating",
|
|
1488
|
+
delegationError,
|
|
1309
1489
|
{ txHash: hash, depositId }
|
|
1310
1490
|
);
|
|
1311
1491
|
}
|
|
1492
|
+
let otcLink;
|
|
1493
|
+
if (params.otcTaker) {
|
|
1494
|
+
emitProgress({ step: "restricting", txHash: hash, depositId });
|
|
1495
|
+
try {
|
|
1496
|
+
const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
|
|
1497
|
+
otcLink = otcResult.otcLink;
|
|
1498
|
+
} catch (otcError) {
|
|
1499
|
+
if (isUserCancellation(otcError)) {
|
|
1500
|
+
throw new OfframpError(
|
|
1501
|
+
"User cancelled OTC restriction",
|
|
1502
|
+
"USER_CANCELLED",
|
|
1503
|
+
"restricting",
|
|
1504
|
+
otcError,
|
|
1505
|
+
{
|
|
1506
|
+
txHash: hash,
|
|
1507
|
+
depositId
|
|
1508
|
+
}
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
throw new OfframpError(
|
|
1512
|
+
"Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
|
|
1513
|
+
"DEPOSIT_FAILED",
|
|
1514
|
+
"restricting",
|
|
1515
|
+
otcError,
|
|
1516
|
+
{ txHash: hash, depositId }
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
emitProgress({ step: "done", txHash: hash, depositId });
|
|
1521
|
+
const result = { depositId, txHash: hash, resumed: false, otcLink };
|
|
1522
|
+
telemetry.emit("sdk.createDeposit.success", {
|
|
1523
|
+
depositId: result.depositId,
|
|
1524
|
+
txHash: result.txHash,
|
|
1525
|
+
wallet: walletAddress,
|
|
1526
|
+
resumed: false,
|
|
1527
|
+
wallDurationMs: Date.now() - flowStartMs
|
|
1528
|
+
});
|
|
1529
|
+
return result;
|
|
1530
|
+
} catch (error) {
|
|
1531
|
+
const known = error instanceof OfframpError ? error : null;
|
|
1532
|
+
telemetry.emit("sdk.createDeposit.error", {
|
|
1533
|
+
code: known?.code ?? "DEPOSIT_FAILED",
|
|
1534
|
+
step: known?.step ?? "unknown"
|
|
1535
|
+
});
|
|
1536
|
+
throw error;
|
|
1312
1537
|
}
|
|
1313
|
-
onProgress?.({ step: "done", txHash: hash, depositId });
|
|
1314
|
-
return { depositId, txHash: hash, resumed: false, otcLink };
|
|
1315
1538
|
}
|
|
1316
1539
|
|
|
1317
1540
|
// src/currencies.ts
|
|
1318
|
-
var
|
|
1541
|
+
var import_sdk6 = require("@zkp2p/sdk");
|
|
1319
1542
|
function buildCurrencies() {
|
|
1320
1543
|
const entries = {};
|
|
1321
1544
|
for (const [code, info] of Object.entries(
|
|
1322
|
-
|
|
1545
|
+
import_sdk6.currencyInfo
|
|
1323
1546
|
)) {
|
|
1324
1547
|
entries[code] = {
|
|
1325
1548
|
code: info.currencyCode ?? code,
|
|
@@ -1331,16 +1554,191 @@ function buildCurrencies() {
|
|
|
1331
1554
|
return entries;
|
|
1332
1555
|
}
|
|
1333
1556
|
var CURRENCIES = buildCurrencies();
|
|
1557
|
+
|
|
1558
|
+
// src/offramp.ts
|
|
1559
|
+
var IDEMPOTENCY_TTL_MS = 10 * 60 * 1e3;
|
|
1560
|
+
var IDEMPOTENCY_STORAGE_PREFIX = "offramp:idempotency:";
|
|
1561
|
+
function getWalletAddress(walletClient) {
|
|
1562
|
+
const address = walletClient.account?.address;
|
|
1563
|
+
if (!address) {
|
|
1564
|
+
throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
|
|
1565
|
+
}
|
|
1566
|
+
return address.toLowerCase();
|
|
1567
|
+
}
|
|
1568
|
+
function getIdempotencyStorageKey(walletAddress, idempotencyKey) {
|
|
1569
|
+
return `${IDEMPOTENCY_STORAGE_PREFIX}${walletAddress}:${idempotencyKey}`;
|
|
1570
|
+
}
|
|
1571
|
+
function readIdempotencyResult(storageKey) {
|
|
1572
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
1573
|
+
try {
|
|
1574
|
+
const raw = sessionStorage.getItem(storageKey);
|
|
1575
|
+
if (!raw) return null;
|
|
1576
|
+
const parsed = JSON.parse(raw);
|
|
1577
|
+
const expiresAt = typeof parsed.expiresAt === "number" && Number.isFinite(parsed.expiresAt) ? parsed.expiresAt : 0;
|
|
1578
|
+
if (expiresAt <= Date.now()) {
|
|
1579
|
+
sessionStorage.removeItem(storageKey);
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
const result = parsed.result;
|
|
1583
|
+
if (!result?.depositId || !result.txHash) {
|
|
1584
|
+
sessionStorage.removeItem(storageKey);
|
|
1585
|
+
return null;
|
|
1586
|
+
}
|
|
1587
|
+
return result;
|
|
1588
|
+
} catch {
|
|
1589
|
+
return null;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
function writeIdempotencyResult(storageKey, result) {
|
|
1593
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1594
|
+
try {
|
|
1595
|
+
const record = {
|
|
1596
|
+
result,
|
|
1597
|
+
expiresAt: Date.now() + IDEMPOTENCY_TTL_MS
|
|
1598
|
+
};
|
|
1599
|
+
sessionStorage.setItem(storageKey, JSON.stringify(record));
|
|
1600
|
+
} catch {
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
function createPerFlowTelemetry(base4, integratorId, referralId) {
|
|
1604
|
+
return createTelemetryContext({
|
|
1605
|
+
sdkVersion: base4.sdkVersion,
|
|
1606
|
+
telemetry: base4.enabled,
|
|
1607
|
+
integratorId: sanitizeAttributionId(integratorId) ?? base4.integratorId,
|
|
1608
|
+
referralId: sanitizeAttributionId(referralId) ?? base4.referralId
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
function normalizeCurrencyEntry(code) {
|
|
1612
|
+
const fromCatalog = CURRENCIES[code];
|
|
1613
|
+
if (fromCatalog) return fromCatalog;
|
|
1614
|
+
return {
|
|
1615
|
+
code,
|
|
1616
|
+
name: code,
|
|
1617
|
+
symbol: code,
|
|
1618
|
+
countryCode: ""
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function buildCurrencyGroups() {
|
|
1622
|
+
const grouped = {};
|
|
1623
|
+
for (const platform of Object.values(PLATFORMS)) {
|
|
1624
|
+
grouped[platform.id] = platform.currencies.map(normalizeCurrencyEntry);
|
|
1625
|
+
}
|
|
1626
|
+
return grouped;
|
|
1627
|
+
}
|
|
1628
|
+
var Offramp = class {
|
|
1629
|
+
walletClient;
|
|
1630
|
+
telemetry;
|
|
1631
|
+
constructor(options) {
|
|
1632
|
+
this.walletClient = options.walletClient;
|
|
1633
|
+
this.telemetry = createTelemetryContext({
|
|
1634
|
+
sdkVersion: SDK_VERSION,
|
|
1635
|
+
telemetry: options.telemetry,
|
|
1636
|
+
integratorId: options.integratorId,
|
|
1637
|
+
referralId: options.referralId
|
|
1638
|
+
});
|
|
1639
|
+
this.telemetry.emit("sdk.init", {
|
|
1640
|
+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown"
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
async createDeposit(params, onProgress) {
|
|
1644
|
+
const flowTelemetry = createPerFlowTelemetry(
|
|
1645
|
+
this.telemetry,
|
|
1646
|
+
params.integratorId,
|
|
1647
|
+
params.referralId
|
|
1648
|
+
);
|
|
1649
|
+
const sanitizedParams = {
|
|
1650
|
+
...params,
|
|
1651
|
+
integratorId: flowTelemetry.integratorId,
|
|
1652
|
+
referralId: flowTelemetry.referralId
|
|
1653
|
+
};
|
|
1654
|
+
const sanitizedKey = sanitizeAttributionId(params.idempotencyKey);
|
|
1655
|
+
const walletAddress = sanitizedKey ? getWalletAddress(this.walletClient) : null;
|
|
1656
|
+
const storageKey = walletAddress && sanitizedKey ? getIdempotencyStorageKey(walletAddress, sanitizedKey) : null;
|
|
1657
|
+
if (storageKey) {
|
|
1658
|
+
const existing = readIdempotencyResult(storageKey);
|
|
1659
|
+
if (existing) return existing;
|
|
1660
|
+
}
|
|
1661
|
+
const result = await offramp(
|
|
1662
|
+
this.walletClient,
|
|
1663
|
+
sanitizedParams,
|
|
1664
|
+
onProgress,
|
|
1665
|
+
flowTelemetry
|
|
1666
|
+
);
|
|
1667
|
+
if (storageKey) {
|
|
1668
|
+
writeIdempotencyResult(storageKey, result);
|
|
1669
|
+
}
|
|
1670
|
+
return result;
|
|
1671
|
+
}
|
|
1672
|
+
getQuote(input) {
|
|
1673
|
+
const amountUsdc = Number(input.amount);
|
|
1674
|
+
if (!Number.isFinite(amountUsdc) || amountUsdc <= 0) {
|
|
1675
|
+
throw new OfframpError("Amount must be a positive number", "VALIDATION");
|
|
1676
|
+
}
|
|
1677
|
+
if (!input.platform.currencies.includes(input.currency.code)) {
|
|
1678
|
+
throw new OfframpError(
|
|
1679
|
+
`${input.currency.code} is not supported on ${input.platform.name}`,
|
|
1680
|
+
"UNSUPPORTED"
|
|
1681
|
+
);
|
|
1682
|
+
}
|
|
1683
|
+
const vaultSpreadBps = 0;
|
|
1684
|
+
const spreadFactor = Math.max(0, 1 - vaultSpreadBps / 1e4);
|
|
1685
|
+
const expectedFiat = amountUsdc * spreadFactor;
|
|
1686
|
+
const effectiveRate = amountUsdc === 0 ? 0 : expectedFiat / amountUsdc;
|
|
1687
|
+
return {
|
|
1688
|
+
amountUsdc,
|
|
1689
|
+
expectedFiat,
|
|
1690
|
+
effectiveRate,
|
|
1691
|
+
vaultSpreadBps
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
getVaultStatus() {
|
|
1695
|
+
return {
|
|
1696
|
+
rateManagerId: DELEGATE_RATE_MANAGER_ID,
|
|
1697
|
+
rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
|
|
1698
|
+
feeRateBps: DELEGATE_MANAGER_FEE_BPS,
|
|
1699
|
+
escrow: ESCROW_ADDRESS,
|
|
1700
|
+
isActive: true
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
listCurrencies() {
|
|
1704
|
+
return buildCurrencyGroups();
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
function createOfframp(options) {
|
|
1708
|
+
return new Offramp(options);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// src/types.ts
|
|
1712
|
+
var OFFRAMP_ERROR_CODES = {
|
|
1713
|
+
VALIDATION: "VALIDATION",
|
|
1714
|
+
APPROVAL_FAILED: "APPROVAL_FAILED",
|
|
1715
|
+
REGISTRATION_FAILED: "REGISTRATION_FAILED",
|
|
1716
|
+
DEPOSIT_FAILED: "DEPOSIT_FAILED",
|
|
1717
|
+
CONFIRMATION_FAILED: "CONFIRMATION_FAILED",
|
|
1718
|
+
DELEGATION_FAILED: "DELEGATION_FAILED",
|
|
1719
|
+
USER_CANCELLED: "USER_CANCELLED",
|
|
1720
|
+
UNSUPPORTED: "UNSUPPORTED"
|
|
1721
|
+
};
|
|
1334
1722
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1335
1723
|
0 && (module.exports = {
|
|
1336
1724
|
CURRENCIES,
|
|
1725
|
+
ESCROW_ADDRESS,
|
|
1726
|
+
OFFRAMP_ERROR_CODES,
|
|
1727
|
+
Offramp,
|
|
1337
1728
|
OfframpError,
|
|
1338
1729
|
PLATFORMS,
|
|
1339
1730
|
close,
|
|
1731
|
+
createOfframp,
|
|
1732
|
+
createTelemetryContext,
|
|
1733
|
+
delegate,
|
|
1340
1734
|
deposits,
|
|
1341
1735
|
disableOtc,
|
|
1736
|
+
emitEvent,
|
|
1342
1737
|
enableOtc,
|
|
1343
1738
|
getOtcLink,
|
|
1344
|
-
offramp
|
|
1739
|
+
offramp,
|
|
1740
|
+
sanitizeAttributionId,
|
|
1741
|
+
sendTelemetryEvent,
|
|
1742
|
+
undelegate
|
|
1345
1743
|
});
|
|
1346
1744
|
//# sourceMappingURL=index.cjs.map
|