@coti-io/coti-wallet-plugin 0.2.4 → 0.2.5

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/index.js CHANGED
@@ -898,6 +898,190 @@ var init_podSdkConfig = __esm({
898
898
  }
899
899
  });
900
900
 
901
+ // src/lib/rpcProvider.ts
902
+ async function waitForTransactionResilient(chainId, txHash, options = {}) {
903
+ const {
904
+ confirmations = 1,
905
+ timeoutMs = 18e4,
906
+ pollIntervalMs = 2e3,
907
+ primary,
908
+ createProvider = createJsonRpcProvider
909
+ } = options;
910
+ if (!txHash) {
911
+ throw new Error("waitForTransactionResilient: missing transaction hash");
912
+ }
913
+ const deadline = Date.now() + timeoutMs;
914
+ let lastError;
915
+ if (primary) {
916
+ const remaining = Math.max(0, deadline - Date.now());
917
+ const primaryTimeout = Math.min(12e3, remaining);
918
+ if (primaryTimeout > 0) {
919
+ try {
920
+ const receipt = await primary.waitForTransaction(txHash, confirmations, primaryTimeout);
921
+ if (receipt) return receipt;
922
+ } catch (error) {
923
+ lastError = error;
924
+ if (isTransactionRevertError(error)) throw error;
925
+ if (!isTransientRpcError(error) && !isWaitTimeoutError(error)) throw error;
926
+ logger.warn(
927
+ `[rpc] primary waitForTransaction failed for ${txHash} on chain ${chainId}; polling via fallback RPCs`
928
+ );
929
+ }
930
+ }
931
+ }
932
+ let delay = pollIntervalMs;
933
+ while (Date.now() < deadline) {
934
+ try {
935
+ const receipt = await getTransactionReceiptAcrossRpcs(chainId, txHash, createProvider);
936
+ if (receipt) {
937
+ if (confirmations <= 1) return receipt;
938
+ const blockNumber = await withRpcFallback(chainId, (provider) => provider.getBlockNumber());
939
+ if (receipt.blockNumber + (confirmations - 1) <= blockNumber) {
940
+ return receipt;
941
+ }
942
+ }
943
+ } catch (error) {
944
+ lastError = error;
945
+ if (!isTransientRpcError(error)) throw error;
946
+ logger.warn(
947
+ `[rpc] receipt poll failed for ${txHash} on chain ${chainId}; retrying after backoff`
948
+ );
949
+ }
950
+ const remaining = deadline - Date.now();
951
+ if (remaining <= 0) break;
952
+ await sleep(Math.min(delay, remaining));
953
+ delay = Math.min(Math.floor(delay * 1.5), 8e3);
954
+ }
955
+ throw lastError instanceof Error ? lastError : new Error(
956
+ `Timed out waiting for transaction ${txHash} on chain ${chainId} after ${timeoutMs}ms`
957
+ );
958
+ }
959
+ async function getTransactionReceiptAcrossRpcs(chainId, txHash, createProvider = createJsonRpcProvider) {
960
+ const urls = resolveRpcUrlsForChain(chainId);
961
+ let lastError;
962
+ let sawNotFound = false;
963
+ for (const url of urls) {
964
+ const provider = createProvider(url, chainId);
965
+ try {
966
+ const receipt = await provider.getTransactionReceipt(txHash);
967
+ if (receipt) return receipt;
968
+ sawNotFound = true;
969
+ } catch (error) {
970
+ lastError = error;
971
+ if (!isTransientRpcError(error)) throw error;
972
+ logger.warn(`[rpc] ${url} getTransactionReceipt failed for chain ${chainId}, trying fallback`);
973
+ }
974
+ }
975
+ if (sawNotFound) return null;
976
+ throw lastError instanceof Error ? lastError : new Error(`All RPC endpoints failed reading receipt for ${txHash} on chain ${chainId}`);
977
+ }
978
+ var import_ethers5, resolveRpcUrlsForChain, collectErrorText, readNestedRpcCode, readNestedHttpStatus, isTransientRpcError, createJsonRpcProvider, isWaitTimeoutError, isTransactionRevertError, sleep, createResilientJsonRpcProvider, withRpcFallback;
979
+ var init_rpcProvider = __esm({
980
+ "src/lib/rpcProvider.ts"() {
981
+ "use strict";
982
+ import_ethers5 = require("ethers");
983
+ init_plugin();
984
+ init_coti();
985
+ init_rpcUrls();
986
+ init_sepolia();
987
+ init_logger();
988
+ resolveRpcUrlsForChain = (chainId) => {
989
+ const base = getRpcUrlsForChain(chainId);
990
+ const numericId = chainId == null ? void 0 : Number(chainId);
991
+ if (numericId == null || !Number.isFinite(numericId)) return base;
992
+ const plugin = getPluginConfig();
993
+ let override;
994
+ if (numericId === SEPOLIA_CHAIN_ID && plugin.sepoliaRpcUrl) {
995
+ override = plugin.sepoliaRpcUrl;
996
+ } else if (numericId === COTI_TESTNET_CHAIN_ID && plugin.cotiTestnetRpcUrl) {
997
+ override = plugin.cotiTestnetRpcUrl;
998
+ }
999
+ if (!override) return base;
1000
+ return [.../* @__PURE__ */ new Set([override, ...base])];
1001
+ };
1002
+ collectErrorText = (error) => {
1003
+ if (!error) return "";
1004
+ if (typeof error === "string") return error;
1005
+ if (error instanceof Error) return error.message;
1006
+ try {
1007
+ return JSON.stringify(error);
1008
+ } catch {
1009
+ return String(error);
1010
+ }
1011
+ };
1012
+ readNestedRpcCode = (error) => {
1013
+ const e = error;
1014
+ return e.code ?? e.error?.code ?? e.info?.error?.code;
1015
+ };
1016
+ readNestedHttpStatus = (error) => {
1017
+ const e = error;
1018
+ return e.data?.httpStatus ?? e.error?.data?.httpStatus ?? e.info?.responseStatus;
1019
+ };
1020
+ isTransientRpcError = (error) => {
1021
+ const text = collectErrorText(error);
1022
+ const lower = text.toLowerCase();
1023
+ if (lower.includes("too many requests") || lower.includes("rate limit") || text.includes("-32005") || text.includes("ECONNRESET") || text.includes("ETIMEDOUT") || lower.includes("timeout") || text.includes("503") || text.includes("502") || text.includes("429")) {
1024
+ return true;
1025
+ }
1026
+ if (error && typeof error === "object") {
1027
+ const code = readNestedRpcCode(error);
1028
+ if (code === "SERVER_ERROR" || code === "TIMEOUT" || code === "NETWORK_ERROR" || code === -32005 || code === "-32005") {
1029
+ return true;
1030
+ }
1031
+ const httpStatus = readNestedHttpStatus(error);
1032
+ if (httpStatus === 429 || httpStatus === "429") {
1033
+ return true;
1034
+ }
1035
+ }
1036
+ return false;
1037
+ };
1038
+ createJsonRpcProvider = (url, chainId) => new import_ethers5.ethers.JsonRpcProvider(url, chainId);
1039
+ isWaitTimeoutError = (error) => {
1040
+ const text = collectErrorText(error).toLowerCase();
1041
+ return text.includes("timeout") || text.includes("timed out") || text.includes("waitfortx");
1042
+ };
1043
+ isTransactionRevertError = (error) => {
1044
+ if (!error || typeof error !== "object") return false;
1045
+ const code = error.code;
1046
+ return code === "CALL_EXCEPTION" || code === "TRANSACTION_REPLACED";
1047
+ };
1048
+ sleep = (ms) => new Promise((resolve) => {
1049
+ setTimeout(resolve, ms);
1050
+ });
1051
+ createResilientJsonRpcProvider = async (chainId) => {
1052
+ const urls = resolveRpcUrlsForChain(chainId);
1053
+ let lastError;
1054
+ for (const url of urls) {
1055
+ const provider = createJsonRpcProvider(url, chainId);
1056
+ try {
1057
+ await provider.getNetwork();
1058
+ return provider;
1059
+ } catch (error) {
1060
+ lastError = error;
1061
+ if (!isTransientRpcError(error)) throw error;
1062
+ logger.warn(`[rpc] ${url} unavailable for chain ${chainId}, trying fallback`);
1063
+ }
1064
+ }
1065
+ throw lastError instanceof Error ? lastError : new Error(`No RPC available for chain ${chainId}`);
1066
+ };
1067
+ withRpcFallback = async (chainId, fn) => {
1068
+ const urls = resolveRpcUrlsForChain(chainId);
1069
+ let lastError;
1070
+ for (const url of urls) {
1071
+ const provider = createJsonRpcProvider(url, chainId);
1072
+ try {
1073
+ return await fn(provider);
1074
+ } catch (error) {
1075
+ lastError = error;
1076
+ if (!isTransientRpcError(error)) throw error;
1077
+ logger.warn(`[rpc] ${url} request failed for chain ${chainId}, trying fallback`);
1078
+ }
1079
+ }
1080
+ throw lastError instanceof Error ? lastError : new Error(`All RPC endpoints failed for chain ${chainId}`);
1081
+ };
1082
+ }
1083
+ });
1084
+
901
1085
  // src/chains/portal/podPTokenBlockingDiagnostics.ts
902
1086
  async function diagnoseBlockingPodRequest(params) {
903
1087
  const {
@@ -967,11 +1151,11 @@ async function diagnoseBlockingPodRequest(params) {
967
1151
  eventScan
968
1152
  };
969
1153
  }
970
- var import_ethers5, import_pod_sdk2, TERMINAL_POD_REQUEST_STATUSES, pTokenIface, portalIface, formatBlockingPodLogSummary, chainIdToExplorerSlug2, withExplorerUrl, tokenMatchesContext, summarizeInFlightLocalPodRequests, summarizeLocalCandidates, isPodRequestStillInFlight, enrichWithPodTracking, POD_EVENT_LOOKBACK_STEPS, fetchLogsWithLookback, paddedAddressTopic, fetchRecentPTokenEvents, fetchRecentPortalEvents, dedupeCandidates, pickBlockingRequest;
1154
+ var import_ethers6, import_pod_sdk2, TERMINAL_POD_REQUEST_STATUSES, pTokenIface, portalIface, formatBlockingPodLogSummary, chainIdToExplorerSlug2, withExplorerUrl, tokenMatchesContext, summarizeInFlightLocalPodRequests, summarizeLocalCandidates, isPodRequestStillInFlight, enrichWithPodTracking, POD_EVENT_LOOKBACK_STEPS, fetchLogsWithLookback, paddedAddressTopic, fetchRecentPTokenEvents, fetchRecentPortalEvents, dedupeCandidates, pickBlockingRequest;
971
1155
  var init_podPTokenBlockingDiagnostics = __esm({
972
1156
  "src/chains/portal/podPTokenBlockingDiagnostics.ts"() {
973
1157
  "use strict";
974
- import_ethers5 = require("ethers");
1158
+ import_ethers6 = require("ethers");
975
1159
  import_pod_sdk2 = require("@coti-io/pod-sdk");
976
1160
  init_pod();
977
1161
  init_podPortalRequestsStorage();
@@ -982,8 +1166,8 @@ var init_podPTokenBlockingDiagnostics = __esm({
982
1166
  "callback-errored",
983
1167
  "burn-debt"
984
1168
  ]);
985
- pTokenIface = new import_ethers5.ethers.Interface(POD_PTOKEN_ABI);
986
- portalIface = new import_ethers5.ethers.Interface(PRIVACY_PORTAL_ABI);
1169
+ pTokenIface = new import_ethers6.ethers.Interface(POD_PTOKEN_ABI);
1170
+ portalIface = new import_ethers6.ethers.Interface(PRIVACY_PORTAL_ABI);
987
1171
  formatBlockingPodLogSummary = (blockingRequest, action) => {
988
1172
  if (!blockingRequest?.requestId) {
989
1173
  return `PoD ${action} blocked: wallet has on-chain pending=true but no requestId was resolved (check eventScan / candidateRequests in the log object below).`;
@@ -1112,7 +1296,7 @@ var init_podPTokenBlockingDiagnostics = __esm({
1112
1296
  }
1113
1297
  return { logs: [], lookbackBlocks: Number(lookbackSteps[lookbackSteps.length - 1]), errors };
1114
1298
  };
1115
- paddedAddressTopic = (address) => import_ethers5.ethers.zeroPadValue(import_ethers5.ethers.getAddress(address), 32);
1299
+ paddedAddressTopic = (address) => import_ethers6.ethers.zeroPadValue(import_ethers6.ethers.getAddress(address), 32);
1116
1300
  fetchRecentPTokenEvents = async (provider, pTokenAddress, account) => {
1117
1301
  const accountTopic = paddedAddressTopic(account);
1118
1302
  const errors = [];
@@ -1250,17 +1434,17 @@ var fees_exports = {};
1250
1434
  __export(fees_exports, {
1251
1435
  quotePodPortalTransactionFees: () => quotePodPortalTransactionFees
1252
1436
  });
1253
- var import_ethers6, quotePodPortalTransactionFees;
1437
+ var import_ethers7, quotePodPortalTransactionFees;
1254
1438
  var init_fees = __esm({
1255
1439
  "src/chains/portal/fees.ts"() {
1256
1440
  "use strict";
1257
- import_ethers6 = require("ethers");
1441
+ import_ethers7 = require("ethers");
1258
1442
  init_chains();
1259
1443
  init_logger();
1260
1444
  init_podPortalFees();
1261
1445
  quotePodPortalTransactionFees = async (params) => {
1262
1446
  const dec = params.pubTok?.decimals ?? 18;
1263
- const amountWei = import_ethers6.ethers.parseUnits(params.amount, dec);
1447
+ const amountWei = import_ethers7.ethers.parseUnits(params.amount, dec);
1264
1448
  const provider = "provider" in params.runner && params.runner.provider ? params.runner.provider : params.runner;
1265
1449
  const gasPrice = params.gasPrice ?? await resolvePodTxGasPrice(provider);
1266
1450
  const nativeSymbol = getChainConfig(params.chainId)?.walletNetwork.nativeCurrency.symbol ?? "ETH";
@@ -1333,11 +1517,11 @@ var init_fees = __esm({
1333
1517
  });
1334
1518
 
1335
1519
  // src/chains/portal/podPortalFees.ts
1336
- var import_ethers7, import_pod_sdk3, resolveFeeRunnerProvider, POD_GAS_PRICE_BUFFER_BPS, getPodGasPrice, resolvePodTxGasPrice, getSepoliaGasPrice, quotePortalFeeOnly, formatPortalFeeDisplay, formatPodFeeDisplay, resolvePodFeeEstimationConfig, resolvePodPortalMethod, buildPodMethodArgs, createPodContract, fallbackExecutionGasLimit, estimatePodExecutionGasWei, estimatePodFee, estimatePodPortalFees, buildPodPortalTxGasOverrides, sendPodPortalMethod;
1520
+ var import_ethers8, import_pod_sdk3, resolveFeeRunnerProvider, POD_GAS_PRICE_BUFFER_BPS, getPodGasPrice, resolvePodTxGasPrice, getSepoliaGasPrice, quotePortalFeeOnly, formatPortalFeeDisplay, formatPodFeeDisplay, resolvePodFeeEstimationConfig, resolvePodPortalMethod, buildPodMethodArgs, createPodContract, fallbackExecutionGasLimit, estimatePodExecutionGasWei, estimatePodFee, estimatePodPortalFees, buildPodPortalTxGasOverrides, sendPodPortalMethod;
1337
1521
  var init_podPortalFees = __esm({
1338
1522
  "src/chains/portal/podPortalFees.ts"() {
1339
1523
  "use strict";
1340
- import_ethers7 = require("ethers");
1524
+ import_ethers8 = require("ethers");
1341
1525
  import_pod_sdk3 = require("@coti-io/pod-sdk");
1342
1526
  init_pod();
1343
1527
  init_chains();
@@ -1370,7 +1554,7 @@ var init_podPortalFees = __esm({
1370
1554
  quotePortalFeeOnly = async (runner, portalAddress, amount, direction, gasPrice) => {
1371
1555
  const provider = resolveFeeRunnerProvider(runner);
1372
1556
  const resolvedGasPrice = gasPrice ?? await resolvePodTxGasPrice(provider);
1373
- const portal = new import_ethers7.ethers.Contract(portalAddress, PRIVACY_PORTAL_ABI, runner);
1557
+ const portal = new import_ethers8.ethers.Contract(portalAddress, PRIVACY_PORTAL_ABI, runner);
1374
1558
  if (direction === "to-private") {
1375
1559
  const [portalFee2, usedDynamicPricing2] = await portal.estimateDepositFees(amount);
1376
1560
  const quote2 = {
@@ -1402,8 +1586,8 @@ var init_podPortalFees = __esm({
1402
1586
  });
1403
1587
  return quote;
1404
1588
  };
1405
- formatPortalFeeDisplay = (portalFee, _usedDynamicPricing) => import_ethers7.ethers.formatEther(portalFee).replace(/\.?0+$/, "") || "0";
1406
- formatPodFeeDisplay = (totalFee) => import_ethers7.ethers.formatEther(totalFee).replace(/\.?0+$/, "") || "0";
1589
+ formatPortalFeeDisplay = (portalFee, _usedDynamicPricing) => import_ethers8.ethers.formatEther(portalFee).replace(/\.?0+$/, "") || "0";
1590
+ formatPodFeeDisplay = (totalFee) => import_ethers8.ethers.formatEther(totalFee).replace(/\.?0+$/, "") || "0";
1407
1591
  resolvePodFeeEstimationConfig = (chainId, direction, gasPrice) => {
1408
1592
  const limits = getChainConfig(chainId)?.podFeeEstimation?.[direction === "to-private" ? "deposit" : "withdraw"];
1409
1593
  if (!limits) {
@@ -1454,8 +1638,8 @@ var init_podPortalFees = __esm({
1454
1638
  { type: import_pod_sdk3.DataType.Uint256, value: "0", isCallBackFee: true },
1455
1639
  { type: import_pod_sdk3.DataType.Uint256, value: placeholderDeadline.toString(), isCallBackFee: false },
1456
1640
  { type: import_pod_sdk3.DataType.Uint8, value: "0", isCallBackFee: false },
1457
- { type: import_pod_sdk3.DataType.String, value: import_ethers7.ethers.ZeroHash, isCallBackFee: false },
1458
- { type: import_pod_sdk3.DataType.String, value: import_ethers7.ethers.ZeroHash, isCallBackFee: false }
1641
+ { type: import_pod_sdk3.DataType.String, value: import_ethers8.ethers.ZeroHash, isCallBackFee: false },
1642
+ { type: import_pod_sdk3.DataType.String, value: import_ethers8.ethers.ZeroHash, isCallBackFee: false }
1459
1643
  ];
1460
1644
  }
1461
1645
  return [
@@ -1483,8 +1667,8 @@ var init_podPortalFees = __esm({
1483
1667
  const fallbackLimit = fallbackExecutionGasLimit(params.chainId, params.direction);
1484
1668
  try {
1485
1669
  const rpcUrl = getRpcUrlForChain(params.chainId);
1486
- const rpcProvider = new import_ethers7.ethers.JsonRpcProvider(rpcUrl);
1487
- const portal = new import_ethers7.ethers.Contract(params.portalAddress, PRIVACY_PORTAL_ABI, rpcProvider);
1670
+ const rpcProvider = new import_ethers8.ethers.JsonRpcProvider(rpcUrl);
1671
+ const portal = new import_ethers8.ethers.Contract(params.portalAddress, PRIVACY_PORTAL_ABI, rpcProvider);
1488
1672
  if (params.direction === "to-private") {
1489
1673
  const method = resolvePodPortalMethod("to-private", params.isNativeDeposit);
1490
1674
  const nativeAmount = params.isNativeDeposit ? params.amountWei : 0n;
@@ -1501,8 +1685,8 @@ var init_podPortalFees = __esm({
1501
1685
  const permit = params.withdrawPermit;
1502
1686
  const deadline = permit ? BigInt(permit.deadline) : BigInt(Math.floor(Date.now() / 1e3) + 60 * 30);
1503
1687
  const v = permit?.v ?? 0;
1504
- const r = permit?.r ?? import_ethers7.ethers.ZeroHash;
1505
- const s = permit?.s ?? import_ethers7.ethers.ZeroHash;
1688
+ const r = permit?.r ?? import_ethers8.ethers.ZeroHash;
1689
+ const s = permit?.s ?? import_ethers8.ethers.ZeroHash;
1506
1690
  const gasLimit = await portal.requestWithdrawWithPermit.estimateGas(
1507
1691
  params.wallet,
1508
1692
  params.amountWei,
@@ -1605,7 +1789,7 @@ var init_podPortalFees = __esm({
1605
1789
  async function signPodWithdrawPermit(params) {
1606
1790
  const { signer, pTokenAddress, portalAddress, amountWei } = params;
1607
1791
  const wallet = await signer.getAddress();
1608
- const pToken = new import_ethers8.ethers.Contract(pTokenAddress, POD_PTOKEN_ABI, signer);
1792
+ const pToken = new import_ethers9.ethers.Contract(pTokenAddress, POD_PTOKEN_ABI, signer);
1609
1793
  const signingChainId = params.chainId ?? Number((await signer.provider.getNetwork()).chainId);
1610
1794
  const resolvedTokenSymbol = params.tokenSymbol ?? await pToken.symbol().catch(() => void 0);
1611
1795
  await assertPodPTokenReady(pToken, wallet, "withdraw", {
@@ -1675,9 +1859,9 @@ async function executePodPortalTransaction(params) {
1675
1859
  throw new Error("PoD portal is not configured for this token");
1676
1860
  }
1677
1861
  const wallet = await signer.getAddress();
1678
- const amountWei = import_ethers8.ethers.parseUnits(txAmount, decimals);
1679
- const pToken = new import_ethers8.ethers.Contract(pTokenAddress, POD_PTOKEN_ABI, signer);
1680
- const portalIface2 = new import_ethers8.ethers.Interface(PRIVACY_PORTAL_ABI);
1862
+ const amountWei = import_ethers9.ethers.parseUnits(txAmount, decimals);
1863
+ const pToken = new import_ethers9.ethers.Contract(pTokenAddress, POD_PTOKEN_ABI, signer);
1864
+ const portalIface2 = new import_ethers9.ethers.Interface(PRIVACY_PORTAL_ABI);
1681
1865
  const gasPrice = await resolvePodTxGasPrice(provider);
1682
1866
  if (txDirection === "to-private") {
1683
1867
  await assertPodPTokenReady(pToken, wallet, "deposit", {
@@ -1706,7 +1890,7 @@ async function executePodPortalTransaction(params) {
1706
1890
  });
1707
1891
  let gasLimit2;
1708
1892
  try {
1709
- const portal = new import_ethers8.ethers.Contract(portalAddress, PRIVACY_PORTAL_ABI, signer);
1893
+ const portal = new import_ethers9.ethers.Contract(portalAddress, PRIVACY_PORTAL_ABI, signer);
1710
1894
  const nativeAmount = isNativeDeposit ? amountWei : 0n;
1711
1895
  const estimated = await portal[method2].estimateGas(
1712
1896
  wallet,
@@ -1738,7 +1922,9 @@ async function executePodPortalTransaction(params) {
1738
1922
  fee: podFee2
1739
1923
  });
1740
1924
  onProgress?.("transfer-start", tx2.hash);
1741
- const receipt2 = await tx2.wait();
1925
+ const receipt2 = await waitForTransactionResilient(chainId, tx2.hash, {
1926
+ primary: provider ?? signer.provider ?? void 0
1927
+ });
1742
1928
  if (!receipt2 || receipt2.status !== 1) {
1743
1929
  const failed = new Error("PoD deposit transaction failed");
1744
1930
  failed.txHash = tx2.hash;
@@ -1795,7 +1981,7 @@ async function executePodPortalTransaction(params) {
1795
1981
  });
1796
1982
  let gasLimit;
1797
1983
  try {
1798
- const portal = new import_ethers8.ethers.Contract(portalAddress, PRIVACY_PORTAL_ABI, signer);
1984
+ const portal = new import_ethers9.ethers.Contract(portalAddress, PRIVACY_PORTAL_ABI, signer);
1799
1985
  const estimated = await portal.requestWithdrawWithPermit.estimateGas(
1800
1986
  wallet,
1801
1987
  amountWei,
@@ -1829,7 +2015,9 @@ async function executePodPortalTransaction(params) {
1829
2015
  fee: podFee
1830
2016
  });
1831
2017
  onProgress?.("transfer-start", tx.hash);
1832
- const receipt = await tx.wait();
2018
+ const receipt = await waitForTransactionResilient(chainId, tx.hash, {
2019
+ primary: provider ?? signer.provider ?? void 0
2020
+ });
1833
2021
  if (!receipt || receipt.status !== 1) {
1834
2022
  const failed = new Error("Sepolia withdraw transaction failed");
1835
2023
  failed.txHash = tx.hash;
@@ -1857,20 +2045,21 @@ async function executePodPortalTransaction(params) {
1857
2045
  }
1858
2046
  };
1859
2047
  }
1860
- var import_ethers8, getErrorMessage, pTokenErrorIface, extractRevertData, parseTransferAlreadyPendingRevert, pendingProbeFromRevert, findParsedEvent, splitSignature, POD_PTOKEN_FLAT_STATUS_ABI, POD_PTOKEN_PLAIN_STATUS_ABI, serializePodBalanceStatusResponse, readContractResultField, buildBalanceWithStateRaw, buildBalanceOfWithStatusRaw, formatBalanceStatusRawForLog, logBalanceStatusRawResponse, getContractProvider, logPodPTokenReadinessProbe, logPodPTokenReadinessBlocked, resolveBlockingRequestId, readPodPTokenPendingState, assertPodPTokenReady;
2048
+ var import_ethers9, getErrorMessage, pTokenErrorIface, extractRevertData, parseTransferAlreadyPendingRevert, pendingProbeFromRevert, findParsedEvent, splitSignature, POD_PTOKEN_FLAT_STATUS_ABI, POD_PTOKEN_PLAIN_STATUS_ABI, serializePodBalanceStatusResponse, readContractResultField, buildBalanceWithStateRaw, buildBalanceOfWithStatusRaw, formatBalanceStatusRawForLog, logBalanceStatusRawResponse, getContractProvider, logPodPTokenReadinessProbe, logPodPTokenReadinessBlocked, resolveBlockingRequestId, readPodPTokenPendingState, assertPodPTokenReady;
1861
2049
  var init_executePodPortalTransaction = __esm({
1862
2050
  "src/chains/portal/executePodPortalTransaction.ts"() {
1863
2051
  "use strict";
1864
- import_ethers8 = require("ethers");
2052
+ import_ethers9 = require("ethers");
1865
2053
  init_pod();
1866
2054
  init_logger();
2055
+ init_rpcProvider();
1867
2056
  init_podPTokenBlockingDiagnostics();
1868
2057
  init_podPortalFees();
1869
2058
  init_podSdkConfig();
1870
2059
  init_podPortalFees();
1871
2060
  init_fees();
1872
2061
  getErrorMessage = (error) => error && typeof error === "object" && "message" in error && typeof error.message === "string" ? error.message : "";
1873
- pTokenErrorIface = new import_ethers8.ethers.Interface(POD_PTOKEN_ABI);
2062
+ pTokenErrorIface = new import_ethers9.ethers.Interface(POD_PTOKEN_ABI);
1874
2063
  extractRevertData = (error) => {
1875
2064
  if (!error || typeof error !== "object") return null;
1876
2065
  const err = error;
@@ -1926,7 +2115,7 @@ var init_executePodPortalTransaction = __esm({
1926
2115
  return null;
1927
2116
  };
1928
2117
  splitSignature = (signature) => {
1929
- const parsed = import_ethers8.ethers.Signature.from(signature);
2118
+ const parsed = import_ethers9.ethers.Signature.from(signature);
1930
2119
  return { v: parsed.v, r: parsed.r, s: parsed.s };
1931
2120
  };
1932
2121
  POD_PTOKEN_FLAT_STATUS_ABI = [
@@ -2073,7 +2262,7 @@ var init_executePodPortalTransaction = __esm({
2073
2262
  if (pendingFromRevert) return pendingFromRevert;
2074
2263
  probeErrors.push(`balanceWithState: ${getErrorMessage(error) || String(error)}`);
2075
2264
  }
2076
- const flatStatusToken = new import_ethers8.ethers.Contract(
2265
+ const flatStatusToken = new import_ethers9.ethers.Contract(
2077
2266
  pTokenAddress,
2078
2267
  POD_PTOKEN_FLAT_STATUS_ABI,
2079
2268
  pToken.runner
@@ -2108,7 +2297,7 @@ var init_executePodPortalTransaction = __esm({
2108
2297
  if (pendingFromRevert) return pendingFromRevert;
2109
2298
  probeErrors.push(`balanceOfWithStatus: ${getErrorMessage(error) || String(error)}`);
2110
2299
  }
2111
- const plainToken = new import_ethers8.ethers.Contract(
2300
+ const plainToken = new import_ethers9.ethers.Contract(
2112
2301
  pTokenAddress,
2113
2302
  POD_PTOKEN_PLAIN_STATUS_ABI,
2114
2303
  pToken.runner
@@ -2315,11 +2504,11 @@ var init_ethereum = __esm({
2315
2504
  });
2316
2505
 
2317
2506
  // src/chains/portal/podTransferFees.ts
2318
- var import_ethers9, import_pod_sdk4, POD_TRANSFER_METHOD, POD_TRANSFER_FORWARD_DATA_SIZE, POD_TRANSFER_L1_EXECUTION_GAS_FALLBACK, createPodPTokenContract, buildPodTransferMethodArgs, resolvePodTransferFeeEstimationConfig, estimatePodTransferFee, fallbackTransferGasLimit, estimatePodTransferExecutionGasWei, resolveNativeFeeSymbol, quotePodTransferFees, sendPodTransferMethod;
2507
+ var import_ethers10, import_pod_sdk4, POD_TRANSFER_METHOD, POD_TRANSFER_FORWARD_DATA_SIZE, POD_TRANSFER_L1_EXECUTION_GAS_FALLBACK, createPodPTokenContract, buildPodTransferMethodArgs, resolvePodTransferFeeEstimationConfig, estimatePodTransferFee, fallbackTransferGasLimit, estimatePodTransferExecutionGasWei, resolveNativeFeeSymbol, quotePodTransferFees, sendPodTransferMethod;
2319
2508
  var init_podTransferFees = __esm({
2320
2509
  "src/chains/portal/podTransferFees.ts"() {
2321
2510
  "use strict";
2322
- import_ethers9 = require("ethers");
2511
+ import_ethers10 = require("ethers");
2323
2512
  import_pod_sdk4 = require("@coti-io/pod-sdk");
2324
2513
  init_pod();
2325
2514
  init_chains();
@@ -2441,8 +2630,8 @@ var init_podTransferFees = __esm({
2441
2630
  if (!overrides.gasLimit) {
2442
2631
  try {
2443
2632
  const rpcUrl = getRpcUrlForChain(params.chainId);
2444
- const rpcProvider = new import_ethers9.ethers.JsonRpcProvider(rpcUrl);
2445
- const pToken = new import_ethers9.ethers.Contract(params.pTokenAddress, POD_PTOKEN_ABI, rpcProvider);
2633
+ const rpcProvider = new import_ethers10.ethers.JsonRpcProvider(rpcUrl);
2634
+ const pToken = new import_ethers10.ethers.Contract(params.pTokenAddress, POD_PTOKEN_ABI, rpcProvider);
2446
2635
  const estimated = await pToken.getFunction(POD_TRANSFER_METHOD).estimateGas(...vals, {
2447
2636
  from: userAddress,
2448
2637
  value: fee.totalFee,
@@ -2546,10 +2735,10 @@ function resolvePrivateTokenTransferTarget(chainId, symbol) {
2546
2735
  };
2547
2736
  }
2548
2737
  function validatePrivateTransferInputs(tokenAddress, recipient, amount, walletAddress) {
2549
- if (!import_ethers10.ethers.isAddress(tokenAddress)) {
2738
+ if (!import_ethers11.ethers.isAddress(tokenAddress)) {
2550
2739
  throw new Error("Invalid token contract address");
2551
2740
  }
2552
- if (!import_ethers10.ethers.isAddress(recipient)) {
2741
+ if (!import_ethers11.ethers.isAddress(recipient)) {
2553
2742
  throw new Error("Invalid recipient address");
2554
2743
  }
2555
2744
  if (recipient.toLowerCase() === walletAddress.toLowerCase()) {
@@ -2562,7 +2751,7 @@ function validatePrivateTransferInputs(tokenAddress, recipient, amount, walletAd
2562
2751
  function parseTransferAmountWei(amount, decimals) {
2563
2752
  let amountWei;
2564
2753
  try {
2565
- amountWei = import_ethers10.ethers.parseUnits(amount, decimals);
2754
+ amountWei = import_ethers11.ethers.parseUnits(amount, decimals);
2566
2755
  } catch {
2567
2756
  throw new Error("Invalid amount for token decimals");
2568
2757
  }
@@ -2598,7 +2787,9 @@ async function submitPrivateTokenTransferTx(params) {
2598
2787
  recipient: shortHash(recipient),
2599
2788
  amount
2600
2789
  });
2601
- const browserProvider = new import_ethers10.ethers.BrowserProvider(eip1193);
2790
+ const browserProvider = new import_ethers11.ethers.BrowserProvider(eip1193);
2791
+ const network = await browserProvider.getNetwork();
2792
+ const chainId = Number(network.chainId);
2602
2793
  const rawTxHash = await eip1193.request({
2603
2794
  method: "eth_sendTransaction",
2604
2795
  params: [
@@ -2611,7 +2802,9 @@ async function submitPrivateTokenTransferTx(params) {
2611
2802
  ]
2612
2803
  });
2613
2804
  logger.log("Waiting for private transfer tx", { txHash: shortHash(rawTxHash) });
2614
- const receipt = await browserProvider.waitForTransaction(rawTxHash);
2805
+ const receipt = await waitForTransactionResilient(chainId, rawTxHash, {
2806
+ primary: browserProvider
2807
+ });
2615
2808
  if (!receipt || receipt.status !== 1) {
2616
2809
  throw new Error("Private token transfer failed");
2617
2810
  }
@@ -2649,10 +2842,10 @@ async function sendPrivateTokenTransfer(params) {
2649
2842
  if (!eip1193) {
2650
2843
  throw new Error("No wallet found");
2651
2844
  }
2652
- const browserProvider = new import_ethers10.ethers.BrowserProvider(eip1193);
2845
+ const browserProvider = new import_ethers11.ethers.BrowserProvider(eip1193);
2653
2846
  const signer = await browserProvider.getSigner(walletAddress);
2654
2847
  const amountWei = parseTransferAmountWei(amount, target.decimals);
2655
- const transferSig = import_ethers10.ethers.id(PRIVATE_ERC20_TRANSFER_256_SIG).slice(0, 10);
2848
+ const transferSig = import_ethers11.ethers.id(PRIVATE_ERC20_TRANSFER_256_SIG).slice(0, 10);
2656
2849
  let aesKey = sessionAesKey ? normalizeAesKeyHex(sessionAesKey) : null;
2657
2850
  if (!aesKey && hasSnap && getAESKeyFromSnap) {
2658
2851
  const snapKey = await getAESKeyFromSnap(walletAddress);
@@ -2680,14 +2873,15 @@ async function sendPrivateTokenTransfer(params) {
2680
2873
  provider: eip1193
2681
2874
  });
2682
2875
  }
2683
- var import_ethers10, PRIVATE_ERC20_TRANSFER_256_SIG, GAS_ESTIMATE_BUFFER_PERCENT, CONFIDENTIAL_TRANSFER_GAS_LIMIT, TRANSFER_INTERFACE;
2876
+ var import_ethers11, PRIVATE_ERC20_TRANSFER_256_SIG, GAS_ESTIMATE_BUFFER_PERCENT, CONFIDENTIAL_TRANSFER_GAS_LIMIT, TRANSFER_INTERFACE;
2684
2877
  var init_executePrivateTokenTransfer = __esm({
2685
2878
  "src/hooks/privacyBridge/executePrivateTokenTransfer.ts"() {
2686
2879
  "use strict";
2687
- import_ethers10 = require("ethers");
2880
+ import_ethers11 = require("ethers");
2688
2881
  init_aesKey();
2689
2882
  init_ethereum();
2690
2883
  init_logger();
2884
+ init_rpcProvider();
2691
2885
  init_config();
2692
2886
  init_chains();
2693
2887
  init_chains();
@@ -2696,7 +2890,7 @@ var init_executePrivateTokenTransfer = __esm({
2696
2890
  PRIVATE_ERC20_TRANSFER_256_SIG = "transfer(address,((uint256,uint256),bytes))";
2697
2891
  GAS_ESTIMATE_BUFFER_PERCENT = 105n;
2698
2892
  CONFIDENTIAL_TRANSFER_GAS_LIMIT = 2000000n;
2699
- TRANSFER_INTERFACE = new import_ethers10.ethers.Interface([
2893
+ TRANSFER_INTERFACE = new import_ethers11.ethers.Interface([
2700
2894
  "function transfer(address to, tuple(tuple(uint256 ciphertextHigh, uint256 ciphertextLow) ciphertext, bytes signature) value) returns (uint256)"
2701
2895
  ]);
2702
2896
  }
@@ -2711,10 +2905,10 @@ __export(executePodPrivateTokenTransfer_exports, {
2711
2905
  quotePodTransferFees: () => quotePodTransferFees
2712
2906
  });
2713
2907
  function validatePodTransferInputs(tokenAddress, recipient, amount, walletAddress) {
2714
- if (!import_ethers11.ethers.isAddress(tokenAddress)) {
2908
+ if (!import_ethers12.ethers.isAddress(tokenAddress)) {
2715
2909
  throw new Error("Invalid token contract address");
2716
2910
  }
2717
- if (!import_ethers11.ethers.isAddress(recipient)) {
2911
+ if (!import_ethers12.ethers.isAddress(recipient)) {
2718
2912
  throw new Error("Invalid recipient address");
2719
2913
  }
2720
2914
  if (recipient.toLowerCase() === walletAddress.toLowerCase()) {
@@ -2739,13 +2933,13 @@ async function executePodPrivateTokenTransfer(params) {
2739
2933
  if (!eip1193) {
2740
2934
  throw new Error("No wallet found");
2741
2935
  }
2742
- const browserProvider = new import_ethers11.ethers.BrowserProvider(eip1193);
2936
+ const browserProvider = new import_ethers12.ethers.BrowserProvider(eip1193);
2743
2937
  const signer = await browserProvider.getSigner(walletAddress);
2744
- const amountWei = import_ethers11.ethers.parseUnits(amount, target.decimals);
2938
+ const amountWei = import_ethers12.ethers.parseUnits(amount, target.decimals);
2745
2939
  if (amountWei <= 0n) {
2746
2940
  throw new Error("Amount must be greater than zero");
2747
2941
  }
2748
- const pToken = new import_ethers11.ethers.Contract(target.tokenAddress, POD_PTOKEN_ABI, signer);
2942
+ const pToken = new import_ethers12.ethers.Contract(target.tokenAddress, POD_PTOKEN_ABI, signer);
2749
2943
  await assertPodPTokenReady(pToken, walletAddress, "transfer", {
2750
2944
  chainId,
2751
2945
  tokenSymbol: symbol,
@@ -2777,13 +2971,15 @@ async function executePodPrivateTokenTransfer(params) {
2777
2971
  gasPrice,
2778
2972
  fee: podFee
2779
2973
  });
2780
- const receipt = await tx.wait();
2974
+ const receipt = await waitForTransactionResilient(chainId, tx.hash, {
2975
+ primary: browserProvider
2976
+ });
2781
2977
  if (!receipt || receipt.status !== 1) {
2782
2978
  const failed = new Error("PoD private token transfer failed");
2783
2979
  failed.txHash = tx.hash;
2784
2980
  throw failed;
2785
2981
  }
2786
- const event = findParsedEvent2(receipt, new import_ethers11.ethers.Interface(POD_PTOKEN_ABI), "TransferRequestSubmitted");
2982
+ const event = findParsedEvent2(receipt, new import_ethers12.ethers.Interface(POD_PTOKEN_ABI), "TransferRequestSubmitted");
2787
2983
  const requestId = event?.args?.requestId;
2788
2984
  return {
2789
2985
  txHash: tx.hash,
@@ -2813,10 +3009,10 @@ async function quotePodPrivateTokenTransferFees(params) {
2813
3009
  if (!eip1193) {
2814
3010
  throw new Error("No wallet found");
2815
3011
  }
2816
- const browserProvider = new import_ethers11.ethers.BrowserProvider(eip1193);
3012
+ const browserProvider = new import_ethers12.ethers.BrowserProvider(eip1193);
2817
3013
  const signer = await browserProvider.getSigner(params.walletAddress);
2818
- const amountWei = import_ethers11.ethers.parseUnits(params.amount, target.decimals);
2819
- const recipient = import_ethers11.ethers.isAddress(params.recipient) && params.recipient !== import_ethers11.ethers.ZeroAddress ? params.recipient : params.walletAddress;
3014
+ const amountWei = import_ethers12.ethers.parseUnits(params.amount, target.decimals);
3015
+ const recipient = import_ethers12.ethers.isAddress(params.recipient) && params.recipient !== import_ethers12.ethers.ZeroAddress ? params.recipient : params.walletAddress;
2820
3016
  return quotePodTransferFees({
2821
3017
  runner: signer,
2822
3018
  chainId: params.chainId,
@@ -2825,15 +3021,16 @@ async function quotePodPrivateTokenTransferFees(params) {
2825
3021
  amountWei
2826
3022
  });
2827
3023
  }
2828
- var import_ethers11, findParsedEvent2;
3024
+ var import_ethers12, findParsedEvent2;
2829
3025
  var init_executePodPrivateTokenTransfer = __esm({
2830
3026
  "src/chains/portal/executePodPrivateTokenTransfer.ts"() {
2831
3027
  "use strict";
2832
- import_ethers11 = require("ethers");
3028
+ import_ethers12 = require("ethers");
2833
3029
  init_pod();
2834
3030
  init_chains();
2835
3031
  init_ethereum();
2836
3032
  init_logger();
3033
+ init_rpcProvider();
2837
3034
  init_executePodPortalTransaction();
2838
3035
  init_podPortalFees();
2839
3036
  init_podTransferFees();
@@ -3554,7 +3751,7 @@ init_executePodPrivateTokenTransfer();
3554
3751
  // src/hooks/privacyBridge/usePodTransferFees.ts
3555
3752
  var import_react = require("react");
3556
3753
  var import_wagmi = require("wagmi");
3557
- var import_ethers12 = require("ethers");
3754
+ var import_ethers13 = require("ethers");
3558
3755
  init_chains();
3559
3756
  init_executePodPrivateTokenTransfer();
3560
3757
  init_logger();
@@ -3614,7 +3811,7 @@ var usePodTransferFees = ({
3614
3811
  const quote = await quotePodPrivateTokenTransferFees({
3615
3812
  chainId,
3616
3813
  symbol,
3617
- recipient: recipient && import_ethers12.ethers.isAddress(recipient) ? recipient : walletAddress,
3814
+ recipient: recipient && import_ethers13.ethers.isAddress(recipient) ? recipient : walletAddress,
3618
3815
  amount: currentAmount,
3619
3816
  walletAddress,
3620
3817
  provider: injected6
@@ -3660,7 +3857,7 @@ var usePodTransferFees = ({
3660
3857
  };
3661
3858
 
3662
3859
  // src/chains/podPriceOracle.ts
3663
- var import_ethers13 = require("ethers");
3860
+ var import_ethers14 = require("ethers");
3664
3861
  init_chains();
3665
3862
  init_rpcUrls();
3666
3863
  init_logger();
@@ -3688,17 +3885,17 @@ async function fetchPodOracleTokenUsdPrice(symbol, chainId, provider) {
3688
3885
  return null;
3689
3886
  }
3690
3887
  const rpcUrls = provider ? [] : getRpcUrlsForChain(chainId);
3691
- const providers = provider ? [provider] : rpcUrls.map((url) => new import_ethers13.ethers.JsonRpcProvider(url));
3888
+ const providers = provider ? [provider] : rpcUrls.map((url) => new import_ethers14.ethers.JsonRpcProvider(url));
3692
3889
  let lastError;
3693
3890
  for (const rpcProvider of providers) {
3694
3891
  try {
3695
- const oracle = new import_ethers13.ethers.Contract(oracleAddress, POD_PRICE_ORACLE_ABI, rpcProvider);
3892
+ const oracle = new import_ethers14.ethers.Contract(oracleAddress, POD_PRICE_ORACLE_ABI, rpcProvider);
3696
3893
  const raw = await oracle.getLivePrice(tokenAddress);
3697
3894
  if (raw === 0n) {
3698
3895
  logger.warn(`PoD price oracle has no live feed for ${symbol} on chain ${chainId}`);
3699
3896
  return null;
3700
3897
  }
3701
- return Number(import_ethers13.ethers.formatEther(raw));
3898
+ return Number(import_ethers14.ethers.formatEther(raw));
3702
3899
  } catch (err) {
3703
3900
  lastError = err;
3704
3901
  }
@@ -3709,7 +3906,7 @@ async function fetchPodOracleTokenUsdPrice(symbol, chainId, provider) {
3709
3906
  }
3710
3907
 
3711
3908
  // src/chains/portal/podPortalAdminData.ts
3712
- var import_ethers14 = require("ethers");
3909
+ var import_ethers15 = require("ethers");
3713
3910
  init_pod();
3714
3911
  init_chains();
3715
3912
  init_rpcUrls();
@@ -3717,7 +3914,7 @@ init_logger();
3717
3914
  var ERC20_BALANCE_ABI = ["function balanceOf(address) view returns (uint256)"];
3718
3915
  var FEE_DIVISOR = 1000000n;
3719
3916
  var POD_NO_MAX_FEE_SENTINEL = (1n << 128n) - 1n;
3720
- var formatFee = (value) => value >= POD_NO_MAX_FEE_SENTINEL ? "0" : import_ethers14.ethers.formatEther(value);
3917
+ var formatFee = (value) => value >= POD_NO_MAX_FEE_SENTINEL ? "0" : import_ethers15.ethers.formatEther(value);
3721
3918
  async function fetchPortalRow(token, config, provider, nativeSymbol) {
3722
3919
  const portalAddress = config.addresses[token.bridgeAddressKey];
3723
3920
  const privateToken = config.tokens.find(
@@ -3744,12 +3941,12 @@ async function fetchPortalRow(token, config, provider, nativeSymbol) {
3744
3941
  isLoading: false
3745
3942
  };
3746
3943
  try {
3747
- const portal = new import_ethers14.ethers.Contract(portalAddress, POD_PORTAL_ADMIN_ABI, provider);
3944
+ const portal = new import_ethers15.ethers.Contract(portalAddress, POD_PORTAL_ADMIN_ABI, provider);
3748
3945
  const [depCfg, wdCfg, accFees, balance] = await Promise.all([
3749
3946
  portal.getFeeConfig(true),
3750
3947
  portal.getFeeConfig(false),
3751
3948
  portal.accumulatedPortalFees().catch(() => 0n),
3752
- token.isNative ? provider.getBalance(portalAddress) : token.addressKey && config.addresses[token.addressKey] ? new import_ethers14.ethers.Contract(config.addresses[token.addressKey], ERC20_BALANCE_ABI, provider).balanceOf(portalAddress).catch(() => 0n) : Promise.resolve(0n)
3949
+ token.isNative ? provider.getBalance(portalAddress) : token.addressKey && config.addresses[token.addressKey] ? new import_ethers15.ethers.Contract(config.addresses[token.addressKey], ERC20_BALANCE_ABI, provider).balanceOf(portalAddress).catch(() => 0n) : Promise.resolve(0n)
3753
3950
  ]);
3754
3951
  return {
3755
3952
  ...base,
@@ -3759,8 +3956,8 @@ async function fetchPortalRow(token, config, provider, nativeSymbol) {
3759
3956
  withdrawFixedFee: formatFee(wdCfg[0]),
3760
3957
  withdrawPercentageBps: wdCfg[1].toString(),
3761
3958
  withdrawMaxFee: formatFee(wdCfg[2]),
3762
- accumulatedCotiFees: import_ethers14.ethers.formatEther(accFees),
3763
- bridgeBalance: import_ethers14.ethers.formatUnits(balance, token.decimals),
3959
+ accumulatedCotiFees: import_ethers15.ethers.formatEther(accFees),
3960
+ bridgeBalance: import_ethers15.ethers.formatUnits(balance, token.decimals),
3764
3961
  error: null
3765
3962
  };
3766
3963
  } catch (err) {
@@ -3788,7 +3985,7 @@ async function fetchPodBridgeData(chainId) {
3788
3985
  );
3789
3986
  let lastError;
3790
3987
  for (const rpcUrl of getRpcUrlsForChain(chainId)) {
3791
- const provider = new import_ethers14.ethers.JsonRpcProvider(rpcUrl);
3988
+ const provider = new import_ethers15.ethers.JsonRpcProvider(rpcUrl);
3792
3989
  try {
3793
3990
  await provider.getBlockNumber();
3794
3991
  return await Promise.all(
@@ -3807,7 +4004,7 @@ async function simulatePodPortalFee(chainId, tokenSymbol, amount, fixedFee, perc
3807
4004
  if (!config) return { fee: "\u2014", explanation: "Unsupported chain" };
3808
4005
  const token = config.tokens.find((t) => t.symbol === tokenSymbol && !t.isPrivate);
3809
4006
  if (!token) return { fee: "\u2014", explanation: "Unknown token" };
3810
- const amountWei = import_ethers14.ethers.parseUnits(amount, token.decimals);
4007
+ const amountWei = import_ethers15.ethers.parseUnits(amount, token.decimals);
3811
4008
  let valueInNative;
3812
4009
  if (token.isNative) {
3813
4010
  valueInNative = amountWei;
@@ -3818,27 +4015,27 @@ async function simulatePodPortalFee(chainId, tokenSymbol, amount, fixedFee, perc
3818
4015
  fetchPodOracleTokenUsdPrice(nativeSymbol, chainId)
3819
4016
  ]);
3820
4017
  if (!tokenUsd || !nativeUsd) {
3821
- const fixed = import_ethers14.ethers.parseEther(fixedFee || "0");
3822
- const cap = import_ethers14.ethers.parseEther(maxFee || "0");
4018
+ const fixed = import_ethers15.ethers.parseEther(fixedFee || "0");
4019
+ const cap = import_ethers15.ethers.parseEther(maxFee || "0");
3823
4020
  const fee2 = cap > 0n && fixed > cap ? cap : fixed;
3824
4021
  return {
3825
- fee: parseFloat(import_ethers14.ethers.formatEther(fee2)).toFixed(6),
4022
+ fee: parseFloat(import_ethers15.ethers.formatEther(fee2)).toFixed(6),
3826
4023
  explanation: "Fixed fee applied (no live oracle price)"
3827
4024
  };
3828
4025
  }
3829
- const tokenUsdWei = import_ethers14.ethers.parseEther(tokenUsd.toString());
3830
- const nativeUsdWei = import_ethers14.ethers.parseEther(nativeUsd.toString());
4026
+ const tokenUsdWei = import_ethers15.ethers.parseEther(tokenUsd.toString());
4027
+ const nativeUsdWei = import_ethers15.ethers.parseEther(nativeUsd.toString());
3831
4028
  const amount18 = amountWei * 10n ** BigInt(18 - token.decimals);
3832
4029
  valueInNative = amount18 * tokenUsdWei / nativeUsdWei;
3833
4030
  }
3834
- const fixedFeeWei = import_ethers14.ethers.parseEther(fixedFee || "0");
3835
- const maxFeeWei = import_ethers14.ethers.parseEther(maxFee || "0");
4031
+ const fixedFeeWei = import_ethers15.ethers.parseEther(fixedFee || "0");
4032
+ const maxFeeWei = import_ethers15.ethers.parseEther(maxFee || "0");
3836
4033
  const bps = BigInt(parseInt(percentageBps || "0", 10));
3837
4034
  const percentageFee = valueInNative * bps / FEE_DIVISOR;
3838
4035
  let fee = percentageFee > fixedFeeWei ? percentageFee : fixedFeeWei;
3839
4036
  if (maxFeeWei > 0n && fee > maxFeeWei) fee = maxFeeWei;
3840
4037
  const explanation = maxFeeWei > 0n && fee === maxFeeWei && fee !== fixedFeeWei ? "Max fee cap applied" : fee === fixedFeeWei ? "Fixed fee floor applied" : "Percentage fee applied";
3841
- return { fee: parseFloat(import_ethers14.ethers.formatEther(fee)).toFixed(6), explanation };
4038
+ return { fee: parseFloat(import_ethers15.ethers.formatEther(fee)).toFixed(6), explanation };
3842
4039
  } catch (err) {
3843
4040
  logger.error("simulatePodPortalFee error:", err);
3844
4041
  return { fee: "\u2014", explanation: "Simulation failed" };
@@ -3859,7 +4056,7 @@ var LIMITS = {
3859
4056
 
3860
4057
  // src/hooks/useMetamask.ts
3861
4058
  var import_react2 = require("react");
3862
- var import_ethers15 = require("ethers");
4059
+ var import_ethers16 = require("ethers");
3863
4060
  init_plugin();
3864
4061
  init_ethereum();
3865
4062
  init_logger();
@@ -4145,7 +4342,7 @@ var useMetamask = ({
4145
4342
  });
4146
4343
  const accounts = await eth.request({ method: "eth_requestAccounts" });
4147
4344
  if (!accounts || accounts.length === 0) return false;
4148
- const provider = new import_ethers15.ethers.BrowserProvider(eth);
4345
+ const provider = new import_ethers16.ethers.BrowserProvider(eth);
4149
4346
  const network = await provider.getNetwork();
4150
4347
  const envDefaultNetwork = getPluginConfig().defaultNetworkId;
4151
4348
  if (accounts.length > 0) {
@@ -4162,7 +4359,7 @@ var useMetamask = ({
4162
4359
  const eth = getMetaMaskProvider();
4163
4360
  if (!eth) return;
4164
4361
  try {
4165
- const provider = new import_ethers15.ethers.BrowserProvider(eth);
4362
+ const provider = new import_ethers16.ethers.BrowserProvider(eth);
4166
4363
  await checkNetwork(provider);
4167
4364
  if (onNetworkChanged) {
4168
4365
  await onNetworkChanged();
@@ -4396,97 +4593,8 @@ function decryptCtUint256(ciphertext, aesKey, options) {
4396
4593
  }
4397
4594
  }
4398
4595
 
4399
- // src/lib/rpcProvider.ts
4400
- var import_ethers16 = require("ethers");
4401
- init_plugin();
4402
- init_coti();
4403
- init_rpcUrls();
4404
- init_sepolia();
4405
- init_logger();
4406
- var resolveRpcUrlsForChain = (chainId) => {
4407
- const base = getRpcUrlsForChain(chainId);
4408
- const numericId = chainId == null ? void 0 : Number(chainId);
4409
- if (numericId == null || !Number.isFinite(numericId)) return base;
4410
- const plugin = getPluginConfig();
4411
- let override;
4412
- if (numericId === SEPOLIA_CHAIN_ID && plugin.sepoliaRpcUrl) {
4413
- override = plugin.sepoliaRpcUrl;
4414
- } else if (numericId === COTI_TESTNET_CHAIN_ID && plugin.cotiTestnetRpcUrl) {
4415
- override = plugin.cotiTestnetRpcUrl;
4416
- }
4417
- if (!override) return base;
4418
- return [.../* @__PURE__ */ new Set([override, ...base])];
4419
- };
4420
- var collectErrorText = (error) => {
4421
- if (!error) return "";
4422
- if (typeof error === "string") return error;
4423
- if (error instanceof Error) return error.message;
4424
- try {
4425
- return JSON.stringify(error);
4426
- } catch {
4427
- return String(error);
4428
- }
4429
- };
4430
- var readNestedRpcCode = (error) => {
4431
- const e = error;
4432
- return e.code ?? e.error?.code ?? e.info?.error?.code;
4433
- };
4434
- var readNestedHttpStatus = (error) => {
4435
- const e = error;
4436
- return e.data?.httpStatus ?? e.error?.data?.httpStatus ?? e.info?.responseStatus;
4437
- };
4438
- var isTransientRpcError = (error) => {
4439
- const text = collectErrorText(error);
4440
- const lower = text.toLowerCase();
4441
- if (lower.includes("too many requests") || lower.includes("rate limit") || text.includes("-32005") || text.includes("ECONNRESET") || text.includes("ETIMEDOUT") || lower.includes("timeout") || text.includes("503") || text.includes("502") || text.includes("429")) {
4442
- return true;
4443
- }
4444
- if (error && typeof error === "object") {
4445
- const code = readNestedRpcCode(error);
4446
- if (code === "SERVER_ERROR" || code === "TIMEOUT" || code === "NETWORK_ERROR" || code === -32005 || code === "-32005") {
4447
- return true;
4448
- }
4449
- const httpStatus = readNestedHttpStatus(error);
4450
- if (httpStatus === 429 || httpStatus === "429") {
4451
- return true;
4452
- }
4453
- }
4454
- return false;
4455
- };
4456
- var createJsonRpcProvider = (url, chainId) => new import_ethers16.ethers.JsonRpcProvider(url, chainId);
4457
- var createResilientJsonRpcProvider = async (chainId) => {
4458
- const urls = resolveRpcUrlsForChain(chainId);
4459
- let lastError;
4460
- for (const url of urls) {
4461
- const provider = createJsonRpcProvider(url, chainId);
4462
- try {
4463
- await provider.getNetwork();
4464
- return provider;
4465
- } catch (error) {
4466
- lastError = error;
4467
- if (!isTransientRpcError(error)) throw error;
4468
- logger.warn(`[rpc] ${url} unavailable for chain ${chainId}, trying fallback`);
4469
- }
4470
- }
4471
- throw lastError instanceof Error ? lastError : new Error(`No RPC available for chain ${chainId}`);
4472
- };
4473
- var withRpcFallback = async (chainId, fn) => {
4474
- const urls = resolveRpcUrlsForChain(chainId);
4475
- let lastError;
4476
- for (const url of urls) {
4477
- const provider = createJsonRpcProvider(url, chainId);
4478
- try {
4479
- return await fn(provider);
4480
- } catch (error) {
4481
- lastError = error;
4482
- if (!isTransientRpcError(error)) throw error;
4483
- logger.warn(`[rpc] ${url} request failed for chain ${chainId}, trying fallback`);
4484
- }
4485
- }
4486
- throw lastError instanceof Error ? lastError : new Error(`All RPC endpoints failed for chain ${chainId}`);
4487
- };
4488
-
4489
4596
  // src/hooks/usePrivateTokenBalance.ts
4597
+ init_rpcProvider();
4490
4598
  init_plugin();
4491
4599
  init_logger();
4492
4600
  var PLAIN_BALANCE_ABI = [
@@ -4599,6 +4707,7 @@ var usePrivateTokenBalance = () => {
4599
4707
  var import_react4 = require("react");
4600
4708
  var import_ethers19 = require("ethers");
4601
4709
  init_config();
4710
+ init_rpcProvider();
4602
4711
 
4603
4712
  // src/lib/utils.ts
4604
4713
  var TOKEN_BALANCE_DISPLAY_DECIMALS = {
@@ -4692,6 +4801,7 @@ var import_ethers18 = require("ethers");
4692
4801
  init_aesKey();
4693
4802
  init_logger();
4694
4803
  init_config();
4804
+ init_rpcProvider();
4695
4805
  var ROUND_TRIP_TEST_VALUE = 0x0123456789abcdefn;
4696
4806
  var FLAT_BALANCE_ABI2 = [
4697
4807
  "function balanceOf(address) view returns (tuple(uint256 ciphertextHigh, uint256 ciphertextLow))"
@@ -5410,6 +5520,7 @@ init_config();
5410
5520
  init_executePodPortalTransaction();
5411
5521
  init_chains();
5412
5522
  init_logger();
5523
+ init_rpcProvider();
5413
5524
  init_encryptValue256();
5414
5525
  init_utils();
5415
5526
  init_ethereum();
@@ -6389,7 +6500,9 @@ var usePrivacyBridgeAllowance = ({
6389
6500
  }]
6390
6501
  });
6391
6502
  logger.log("\u{1F510} [Approve] Tx submitted, waiting for confirmation", { txHash: shortHash(rawTxHash) });
6392
- await provider.waitForTransaction(rawTxHash);
6503
+ await waitForTransactionResilient(currentChainId, rawTxHash, {
6504
+ primary: provider
6505
+ });
6393
6506
  logger.log("\u{1F510} [Approve] Tx confirmed, refreshing allowance...");
6394
6507
  setIsApproving(false);
6395
6508
  setToastState((prev) => ({ ...prev, visible: false }));
@@ -6406,7 +6519,9 @@ var usePrivacyBridgeAllowance = ({
6406
6519
  title: "Approving...",
6407
6520
  message: "Waiting for allowance confirmation..."
6408
6521
  });
6409
- await tx.wait();
6522
+ await waitForTransactionResilient(currentChainId, tx.hash, {
6523
+ primary: provider
6524
+ });
6410
6525
  await checkAllowance();
6411
6526
  setIsApproving(false);
6412
6527
  setToastState((prev) => ({ ...prev, visible: false }));
@@ -6450,6 +6565,7 @@ init_config();
6450
6565
  init_executePodPortalTransaction();
6451
6566
  init_chains();
6452
6567
  init_logger();
6568
+ init_rpcProvider();
6453
6569
  init_utils();
6454
6570
  var usePrivacyBridgeExecutor = ({
6455
6571
  walletAddress,
@@ -6698,7 +6814,9 @@ var usePrivacyBridgeExecutor = ({
6698
6814
  logger.log("ERC20 deposit tx sent", { txHash: shortHash(rawDepositTxHash) });
6699
6815
  tx = {
6700
6816
  hash: rawDepositTxHash,
6701
- wait: async () => await provider.waitForTransaction(rawDepositTxHash)
6817
+ wait: async () => await waitForTransactionResilient(currentChainId, rawDepositTxHash, {
6818
+ primary: provider
6819
+ })
6702
6820
  };
6703
6821
  } else {
6704
6822
  logger.log("\u{1F504} Executing Native COTI Deposit...");
@@ -6855,7 +6973,9 @@ var usePrivacyBridgeExecutor = ({
6855
6973
  logger.log("Withdraw tx sent", { txHash: shortHash(rawWithdrawTxHash) });
6856
6974
  tx = {
6857
6975
  hash: rawWithdrawTxHash,
6858
- wait: async () => await provider.waitForTransaction(rawWithdrawTxHash)
6976
+ wait: async () => await waitForTransactionResilient(currentChainId, rawWithdrawTxHash, {
6977
+ primary: provider
6978
+ })
6859
6979
  };
6860
6980
  } catch (e) {
6861
6981
  setIsBridgingLoading(false);
@@ -6871,12 +6991,13 @@ var usePrivacyBridgeExecutor = ({
6871
6991
  title: "Processing Transaction",
6872
6992
  message: "Transaction sent to network. Waiting for confirmation..."
6873
6993
  });
6874
- const receipt = await tx.wait();
6994
+ const receipt = await waitForTransactionResilient(currentChainId, tx.hash, {
6995
+ primary: provider
6996
+ });
6875
6997
  logger.log("Transaction confirmed", {
6876
6998
  status: receipt.status,
6877
6999
  blockNumber: receipt.blockNumber,
6878
- gasUsed: receipt.gasUsed?.toString(),
6879
- gasLimit: receipt.gasLimit?.toString() ?? "n/a"
7000
+ gasUsed: receipt.gasUsed?.toString()
6880
7001
  });
6881
7002
  if (receipt.status !== 1) {
6882
7003
  const gasUsed = receipt.gasUsed ? Number(receipt.gasUsed) : 0;
@@ -6891,11 +7012,12 @@ var usePrivacyBridgeExecutor = ({
6891
7012
  const replayProvider = new import_ethers23.ethers.JsonRpcProvider(
6892
7013
  getRpcUrlForChain(Number(network2.chainId))
6893
7014
  );
7015
+ const broadcastTx = await replayProvider.getTransaction(txHashStr);
6894
7016
  await replayProvider.call({
6895
- to: receipt.to,
6896
- from: receipt.from,
6897
- data: receipt.data || void 0,
6898
- value: receipt.value || void 0
7017
+ to: broadcastTx?.to ?? receipt.to,
7018
+ from: broadcastTx?.from ?? receipt.from,
7019
+ data: broadcastTx?.data,
7020
+ value: broadcastTx?.value
6899
7021
  });
6900
7022
  } catch (replayErr) {
6901
7023
  const errorName = replayErr.errorName || replayErr.revert?.name;
@@ -7821,7 +7943,7 @@ async function fetchEncryptedBackupProbe(address, chainId) {
7821
7943
  return null;
7822
7944
  }
7823
7945
  }
7824
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7946
+ var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
7825
7947
  function resolveAesKeyChainId(currentChainId, overrideChainId) {
7826
7948
  const configuredChainId = getPluginConfig().aesKeyChainId;
7827
7949
  assertAesKeyChainId(configuredChainId);
@@ -7841,7 +7963,7 @@ async function probeSnapKeyWithRetry(hasAesKeyInSnap, address, retries, confirmS
7841
7963
  for (let attempt = 0; attempt <= retries; attempt += 1) {
7842
7964
  const result = await hasAesKeyInSnap(address);
7843
7965
  if (result !== null) return result;
7844
- if (attempt < retries) await sleep(250);
7966
+ if (attempt < retries) await sleep2(250);
7845
7967
  }
7846
7968
  if (confirmSnapInstalled && !await confirmSnapInstalled()) {
7847
7969
  return false;
@@ -8134,7 +8256,7 @@ function toBigInt(value, fallback2) {
8134
8256
  if (value === void 0) return fallback2;
8135
8257
  return (0, import_ethers26.getBigInt)(value);
8136
8258
  }
8137
- var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8259
+ var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8138
8260
  function useAesKeyProvider(walletTypeInfo) {
8139
8261
  const [isOnboarding, setIsOnboarding] = (0, import_react17.useState)(false);
8140
8262
  const [onboardingError, setOnboardingError] = (0, import_react17.useState)(null);
@@ -8379,7 +8501,7 @@ function useAesKeyProvider(walletTypeInfo) {
8379
8501
  const timeoutMs = config.onboardingGrantTimeoutMs ?? 6e4;
8380
8502
  const startedAt = Date.now();
8381
8503
  while (nativeBalance < requiredBalanceWei && Date.now() - startedAt < timeoutMs) {
8382
- await sleep2(pollIntervalMs);
8504
+ await sleep3(pollIntervalMs);
8383
8505
  nativeBalance = await provider.getBalance(address);
8384
8506
  logger.log("[AesKeyProvider] Native COTI balance while waiting for grant", {
8385
8507
  address,