@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/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/platforms.ts
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, import_sdk2.getPaymentMethodsCatalog)(BASE_CHAIN_ID, RUNTIME_ENV);
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, import_sdk2.getCurrencyInfoFromHash)(hash);
153
- if (info?.currencyCode && import_sdk2.currencyInfo[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, import_sdk2.resolvePaymentMethodHash)(name);
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, import_sdk2.resolvePaymentMethodHash)("zelle");
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(walletClient, escrow, depositId, taker) {
802
- const { createPublicClient: createPublicClient2, http: http2 } = await import("viem");
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 import_sdk3 = require("@zkp2p/sdk");
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, import_sdk3.defaultIndexerEndpoint)("PRODUCTION");
823
- const client = new import_sdk3.IndexerClient(endpoint);
824
- indexerService = new import_sdk3.IndexerDepositService(client);
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, import_sdk3.classifyDelegationState)(
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, import_sdk3.getCurrencyInfoFromHash)(c.currencyCode);
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, import_sdk4.getSpreadOracleConfig)(currency);
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 !== DELEGATE_RATE_MANAGER_ID;
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
- if (!walletClient.account?.address) {
1050
- throw new OfframpError("Wallet client has no account. Connect a wallet first.", "VALIDATION");
1051
- }
1052
- const walletAddress = walletClient.account.address;
1053
- const amt = parseFloat(amount);
1054
- if (!Number.isFinite(amt) || amt < MIN_DEPOSIT_USDC) {
1055
- throw new OfframpError(`Minimum deposit is ${MIN_DEPOSIT_USDC} USDC`, "VALIDATION");
1056
- }
1057
- if (!platform.currencies.includes(currencyCode)) {
1058
- throw new OfframpError(`${currencyCode} is not supported on ${platform.name}`, "UNSUPPORTED");
1059
- }
1060
- const validation = platform.validate(identifier);
1061
- if (!validation.valid) {
1062
- throw new OfframpError(validation.error || "Invalid identifier", "VALIDATION");
1063
- }
1064
- const normalizedIdentifier = validation.normalized;
1065
- const methodHash = getPaymentMethodHash(platformId);
1066
- if (!methodHash) {
1067
- throw new OfframpError(`${platform.name} is not currently supported`, "UNSUPPORTED");
1068
- }
1069
- const existing = await findUndelegatedDeposit(walletAddress);
1070
- if (existing) {
1071
- onProgress?.({ step: "resuming", depositId: existing.depositId });
1072
- const client2 = createSdkClient(walletClient);
1073
- const txOverrides2 = { referrer: [REFERRER] };
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 result = await client2.setRateManager({
1076
- depositId: BigInt(existing.depositId),
1077
- rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
1078
- rateManagerId: DELEGATE_RATE_MANAGER_ID,
1079
- escrowAddress: existing.escrowAddress,
1080
- txOverrides: txOverrides2
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", "resuming", err);
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
- "Found undelegated deposit but delegation failed. Try again.",
1090
- "DELEGATION_FAILED",
1091
- "resuming",
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
- const truncatedAmount = amt.toFixed(6).replace(/\.?0+$/, "");
1098
- const amountUnits = usdcToUnits(truncatedAmount);
1099
- const minUnits = usdcToUnits(String(MIN_ORDER_USDC));
1100
- const maxUnits = usdcToUnits(String(Math.min(amt, 2500)));
1101
- try {
1102
- const publicClient = (0, import_viem3.createPublicClient)({ chain: import_chains.base, transport: (0, import_viem3.http)(BASE_RPC_URL) });
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
- `Insufficient USDC balance. Have ${available.toFixed(2)}, need ${truncatedAmount}.`,
1121
- "VALIDATION"
1296
+ "Payee registration failed",
1297
+ "REGISTRATION_FAILED",
1298
+ "registering",
1299
+ err
1122
1300
  );
1123
1301
  }
1124
- } catch (err) {
1125
- if (err instanceof OfframpError) throw err;
1126
- }
1127
- const client = createSdkClient(walletClient);
1128
- const txOverrides = { referrer: [REFERRER] };
1129
- onProgress?.({ step: "approving" });
1130
- try {
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 receipt = await receiptClient.waitForTransactionReceipt({ hash, confirmations: 1 });
1200
- depositId = extractDepositIdFromLogs(receipt.logs) || "";
1201
- } catch {
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
- if (!depositId) {
1205
- let delay = INDEXER_INITIAL_DELAY_MS;
1206
- for (let attempt = 0; attempt < INDEXER_MAX_ATTEMPTS && !depositId; attempt++) {
1343
+ emitProgress({ step: "confirming", txHash: hash });
1344
+ let depositId = "";
1345
+ const receiptClient = client;
1346
+ if (typeof receiptClient.waitForTransactionReceipt === "function") {
1207
1347
  try {
1208
- const deps = await client.indexer.getDepositsWithRelations(
1209
- { depositor: walletAddress },
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
- if (!depositId) {
1224
- throw new OfframpError(
1225
- "Deposit created on-chain but could not confirm deposit ID. Your funds are safe. Call offramp() again to resume delegation.",
1226
- "CONFIRMATION_FAILED",
1227
- "confirming",
1228
- void 0,
1229
- { txHash: hash }
1230
- );
1231
- }
1232
- onProgress?.({ step: "delegating", txHash: hash, depositId });
1233
- try {
1234
- await client.setRateManager({
1235
- depositId: BigInt(depositId),
1236
- rateManagerAddress: RATE_MANAGER_REGISTRY_ADDRESS,
1237
- rateManagerId: DELEGATE_RATE_MANAGER_ID,
1238
- escrowAddress: ESCROW_ADDRESS,
1239
- txOverrides
1240
- });
1241
- } catch (delegationError) {
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
- "User cancelled delegation",
1245
- "USER_CANCELLED",
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
- throw new OfframpError(
1252
- "Deposit created but delegation failed. Call offramp() again to resume delegation.",
1253
- "DELEGATION_FAILED",
1254
- "delegating",
1255
- delegationError,
1256
- { txHash: hash, depositId }
1257
- );
1258
- }
1259
- let otcLink;
1260
- if (params.otcTaker) {
1261
- onProgress?.({ step: "restricting", txHash: hash, depositId });
1262
- try {
1263
- const otcResult = await enableOtc(walletClient, depositId, params.otcTaker);
1264
- otcLink = otcResult.otcLink;
1265
- } catch (otcError) {
1266
- if (isUserCancellation(otcError)) {
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
- "User cancelled OTC restriction",
1269
- "USER_CANCELLED",
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
- "Deposit created and delegated but OTC restriction failed. Use enableOtc() to retry.",
1280
- "DEPOSIT_FAILED",
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
- onProgress?.({ step: "done", txHash: hash, depositId });
1288
- return { depositId, txHash: hash, resumed: false, otcLink };
1604
+ };
1605
+ function createOfframp(options) {
1606
+ return new Offramp(options);
1289
1607
  }
1290
1608
 
1291
1609
  // src/hooks/useOfframp.ts
1292
- function useOfframp() {
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 [error, setError] = (0, import_react.useState)(null);
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
- setError(null);
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
- setError(null);
1637
+ setState("idle");
1638
+ setLastError(null);
1313
1639
  setStep(null);
1314
1640
  setTxHash(null);
1315
1641
  setDepositId(null);
1316
1642
  try {
1317
- const result = await offramp(walletClient, params, (progress) => {
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
- setError(offrampError);
1331
- throw err;
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
- error,
1679
+ lastError,
1680
+ error: lastError,
1344
1681
  isLoading,
1345
1682
  offramp: offramp2,
1346
1683
  deposits,