@cofhe/react 0.0.0-beta-20260324063313 → 0.0.0-beta-20260324094644

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.cjs CHANGED
@@ -587,12 +587,12 @@ var require_assert = __commonJS({
587
587
  "use strict";
588
588
  Object.defineProperty(exports2, "__esModule", { value: true });
589
589
  exports2.assert = void 0;
590
- function assert24(condition, message = "no additional info provided") {
590
+ function assert26(condition, message = "no additional info provided") {
591
591
  if (!condition) {
592
592
  throw new Error("Assertion Error: " + message);
593
593
  }
594
594
  }
595
- exports2.assert = assert24;
595
+ exports2.assert = assert26;
596
596
  }
597
597
  });
598
598
 
@@ -757,6 +757,9 @@ __export(index_exports, {
757
757
  getBlockExplorerTokenUrl: () => getBlockExplorerTokenUrl,
758
758
  getBlockExplorerTxUrl: () => getBlockExplorerTxUrl,
759
759
  getBlockExplorerUrl: () => getBlockExplorerUrl,
760
+ getCofheTokenClaimUnshieldedCallArgs: () => getCofheTokenClaimUnshieldedCallArgs,
761
+ getCofheTokenShieldCallArgs: () => getCofheTokenShieldCallArgs,
762
+ getCofheTokenUnshieldCallArgs: () => getCofheTokenUnshieldCallArgs,
760
763
  isEthPair: () => isEthPair,
761
764
  isWrappedEthToken: () => isWrappedEthToken,
762
765
  statusToString: () => statusToString,
@@ -774,6 +777,7 @@ __export(index_exports, {
774
777
  useCofheReadContractAndDecrypt: () => useCofheReadContractAndDecrypt,
775
778
  useCofheRemovePermit: () => useCofheRemovePermit,
776
779
  useCofheSelectPermit: () => useCofheSelectPermit,
780
+ useCofheSimulateWriteContract: () => useCofheSimulateWriteContract,
777
781
  useCofheTokenClaimUnshielded: () => useCofheTokenClaimUnshielded,
778
782
  useCofheTokenClaimable: () => useCofheTokenClaimable,
779
783
  useCofheTokenDecryptedBalance: () => useCofheTokenDecryptedBalance,
@@ -789,6 +793,7 @@ __export(index_exports, {
789
793
  useCoingeckoContractMarketChartRange: () => useCoingeckoContractMarketChartRange,
790
794
  useCoingeckoUsdPrice: () => useCoingeckoUsdPrice,
791
795
  useInternalQueryClient: () => useInternalQueryClient,
796
+ useTokenAllowance: () => useTokenAllowance,
792
797
  useTokensWithPublicBalances: () => useTokensWithPublicBalances,
793
798
  useTransactionReceiptsByHash: () => useTransactionReceiptsByHash,
794
799
  useTransactionStore: () => useTransactionStore
@@ -1132,6 +1137,71 @@ async function maybeWaitUntilRpcAwareAndReadContract(publicClient, params, optio
1132
1137
  throw new Error("Unexpected exit from wait loop");
1133
1138
  }
1134
1139
 
1140
+ // src/utils/cofheErrors.ts
1141
+ var import_errors = __toESM(require("@fhenixprotocol/cofhe-errors/errors.json"), 1);
1142
+ var import_viem2 = require("viem");
1143
+ function is0xString(value) {
1144
+ return value.startsWith("0x");
1145
+ }
1146
+ function isStringArray(value) {
1147
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
1148
+ }
1149
+ function isCofheSolidityError(value) {
1150
+ if (typeof value !== "object" || value === null) return false;
1151
+ if (!("name" in value) || typeof value.name !== "string") return false;
1152
+ if (!("selector" in value) || typeof value.selector !== "string") return false;
1153
+ if (!is0xString(value.selector)) return false;
1154
+ if (!("signature" in value) || typeof value.signature !== "string") return false;
1155
+ if (!("source" in value) || typeof value.source !== "string") return false;
1156
+ if (!("inputs" in value) || !isStringArray(value.inputs)) return false;
1157
+ if (!("inputTypes" in value) || !isStringArray(value.inputTypes)) return false;
1158
+ return true;
1159
+ }
1160
+ function parseCofheSolidityErrors(value) {
1161
+ if (!Array.isArray(value)) return [];
1162
+ return value.filter(isCofheSolidityError);
1163
+ }
1164
+ var errorsJsonUnknown = import_errors.default;
1165
+ var errors = parseCofheSolidityErrors(errorsJsonUnknown);
1166
+ function normalizeSelector(selector) {
1167
+ if (typeof selector !== "string") return void 0;
1168
+ const s = selector.trim().toLowerCase();
1169
+ if (!s.startsWith("0x")) return void 0;
1170
+ if (s.length < 10) return void 0;
1171
+ const candidate = s.slice(0, 10);
1172
+ if (!is0xString(candidate)) return void 0;
1173
+ return candidate;
1174
+ }
1175
+ function decodeCofheErrorSelector(selectorOrRevertData) {
1176
+ const selector = normalizeSelector(selectorOrRevertData);
1177
+ if (!selector) return void 0;
1178
+ return errors.find((e) => e.selector.toLowerCase() === selector);
1179
+ }
1180
+ function cofheHumanizeRevertReason(reasonOfRevert) {
1181
+ const decoded = decodeCofheErrorSelector(reasonOfRevert);
1182
+ if (!decoded) return void 0;
1183
+ return `${decoded.signature} (${decoded.source})`;
1184
+ }
1185
+ function extractSelectorFromMessage(message) {
1186
+ const match = message.match(/0x[0-9a-fA-F]{8}/);
1187
+ return match?.[0];
1188
+ }
1189
+ function cofheHumanizeViemError(error) {
1190
+ if (error instanceof import_viem2.ContractFunctionExecutionError) {
1191
+ if (error.cause instanceof import_viem2.ContractFunctionRevertedError) {
1192
+ const raw = error.cause.raw;
1193
+ if (typeof raw === "string") {
1194
+ return cofheHumanizeRevertReason(raw);
1195
+ }
1196
+ }
1197
+ }
1198
+ if (error instanceof Error) {
1199
+ const selector = extractSelectorFromMessage(error.message);
1200
+ if (selector) return cofheHumanizeRevertReason(selector);
1201
+ }
1202
+ return void 0;
1203
+ }
1204
+
1135
1205
  // src/components/FnxFloatingButton/FloatingButtonComponent.tsx
1136
1206
  var import_Close = __toESM(require("@mui/icons-material/Close"), 1);
1137
1207
 
@@ -1171,13 +1241,15 @@ var TransactionActionType = /* @__PURE__ */ ((TransactionActionType2) => {
1171
1241
  TransactionActionType2["Shield"] = "shield";
1172
1242
  TransactionActionType2["Unshield"] = "unshield";
1173
1243
  TransactionActionType2["Claim"] = "claim";
1244
+ TransactionActionType2["Approve"] = "approve";
1174
1245
  return TransactionActionType2;
1175
1246
  })(TransactionActionType || {});
1176
1247
  var actionToStringMap = {
1177
1248
  ["shieldSend" /* ShieldSend */]: "Shielded Transfer",
1178
1249
  ["shield" /* Shield */]: "Shield",
1179
1250
  ["unshield" /* Unshield */]: "Unshield",
1180
- ["claim" /* Claim */]: "Claim"
1251
+ ["claim" /* Claim */]: "Claim",
1252
+ ["approve" /* Approve */]: "Approve"
1181
1253
  };
1182
1254
  var actionToString = (a) => actionToStringMap[a];
1183
1255
  var statusToStringMap = {
@@ -1326,11 +1398,11 @@ var useCofhePublicClient = () => useCofheConnection().publicClient;
1326
1398
  var useCofheWalletClient = () => useCofheConnection().walletClient;
1327
1399
 
1328
1400
  // src/hooks/useTrackPendingTransactions.ts
1329
- var import_ts_essentials5 = __toESM(require_dist());
1401
+ var import_ts_essentials6 = __toESM(require_dist());
1330
1402
 
1331
1403
  // src/hooks/useCofheReadContract.ts
1332
1404
  var import_react_query2 = require("@tanstack/react-query");
1333
- var import_viem2 = require("viem");
1405
+ var import_viem3 = require("viem");
1334
1406
 
1335
1407
  // src/hooks/useCofhePermits.ts
1336
1408
  var import_react4 = require("react");
@@ -1454,19 +1526,27 @@ function useIsCofheErrorActive() {
1454
1526
 
1455
1527
  // src/hooks/useCofheReadContract.ts
1456
1528
  var import_abi = require("@cofhe/abi");
1457
- var QUERY_CACHE_PREFIX = "cofheReadContract";
1529
+
1530
+ // src/utils/serializeBigint.ts
1531
+ function isRecord(value) {
1532
+ return typeof value === "object" && value !== null;
1533
+ }
1458
1534
  function serializeIfBigint(value) {
1459
- if (typeof value === "bigint") {
1460
- return value.toString();
1461
- }
1535
+ if (typeof value === "bigint") return value.toString();
1536
+ return value;
1462
1537
  }
1463
1538
  function serializeBigintRecursively(value) {
1464
- if (Array.isArray(value)) {
1465
- return value.map(serializeBigintRecursively);
1466
- } else {
1467
- return serializeIfBigint(value);
1539
+ if (Array.isArray(value)) return value.map(serializeBigintRecursively);
1540
+ if (isRecord(value)) {
1541
+ const out = {};
1542
+ for (const [key, nested] of Object.entries(value)) out[key] = serializeBigintRecursively(nested);
1543
+ return out;
1468
1544
  }
1545
+ return serializeIfBigint(value);
1469
1546
  }
1547
+
1548
+ // src/hooks/useCofheReadContract.ts
1549
+ var QUERY_CACHE_PREFIX = "cofheReadContract";
1470
1550
  function constructCofheReadContractQueryKey({
1471
1551
  cofheChainId,
1472
1552
  address,
@@ -1582,14 +1662,14 @@ function useCofheReadContract(params, queryOptions) {
1582
1662
  }
1583
1663
 
1584
1664
  // src/hooks/useTrackPendingTransactions.ts
1585
- var import_react_query7 = require("@tanstack/react-query");
1665
+ var import_react_query8 = require("@tanstack/react-query");
1586
1666
 
1587
1667
  // src/constants/confidentialTokenABIs.ts
1588
- var import_viem3 = require("viem");
1589
- var ERC20_ALLOWANCE_ABI = (0, import_viem3.parseAbi)([
1668
+ var import_viem4 = require("viem");
1669
+ var ERC20_ALLOWANCE_ABI = (0, import_viem4.parseAbi)([
1590
1670
  "function allowance(address owner, address spender) view returns (uint256)"
1591
1671
  ]);
1592
- var ERC20_APPROVE_ABI = (0, import_viem3.parseAbi)(["function approve(address spender, uint256 amount) returns (bool)"]);
1672
+ var ERC20_APPROVE_ABI = (0, import_viem4.parseAbi)(["function approve(address spender, uint256 amount) returns (bool)"]);
1593
1673
  var CONFIDENTIAL_TYPE_WRAPPED_ABI = [
1594
1674
  {
1595
1675
  inputs: [
@@ -1611,10 +1691,10 @@ var CONFIDENTIAL_TYPE_WRAPPED_ABI = [
1611
1691
  type: "function"
1612
1692
  }
1613
1693
  ];
1614
- var CONFIDENTIAL_TYPE_PURE_ABI = (0, import_viem3.parseAbi)([
1694
+ var CONFIDENTIAL_TYPE_PURE_ABI = (0, import_viem4.parseAbi)([
1615
1695
  "function confidentialBalanceOf(address account) view returns (uint256)"
1616
1696
  ]);
1617
- var CONFIDENTIAL_TYPE_DUAL_ABI = (0, import_viem3.parseAbi)([
1697
+ var CONFIDENTIAL_TYPE_DUAL_ABI = (0, import_viem4.parseAbi)([
1618
1698
  "function confidentialBalanceOf(address account) view returns (uint256)"
1619
1699
  ]);
1620
1700
  var CONFIDENTIAL_ABIS = {
@@ -1736,9 +1816,9 @@ var TRANSFER_ABIS = {
1736
1816
  functionName: "TBD_DUAL_FUNCTION_NAME"
1737
1817
  }
1738
1818
  };
1739
- var DUAL_SHIELD_ABI = (0, import_viem3.parseAbi)(["function shield(uint256 amount) public"]);
1740
- var DUAL_UNSHIELD_ABI = (0, import_viem3.parseAbi)(["function unshield(uint64 amount) public"]);
1741
- var DUAL_CLAIM_UNSHIELD_ABI = (0, import_viem3.parseAbi)(["function claimUnshielded() public"]);
1819
+ var DUAL_SHIELD_ABI = (0, import_viem4.parseAbi)(["function shield(uint256 amount) public"]);
1820
+ var DUAL_UNSHIELD_ABI = (0, import_viem4.parseAbi)(["function unshield(uint64 amount) public"]);
1821
+ var DUAL_CLAIM_UNSHIELD_ABI = (0, import_viem4.parseAbi)(["function claimUnshielded() public"]);
1742
1822
  var DUAL_GET_UNSHIELD_CLAIM_ABI = [
1743
1823
  {
1744
1824
  name: "getUserUnshieldClaim",
@@ -1760,10 +1840,10 @@ var DUAL_GET_UNSHIELD_CLAIM_ABI = [
1760
1840
  ]
1761
1841
  }
1762
1842
  ];
1763
- var WRAPPED_ENCRYPT_ABI = (0, import_viem3.parseAbi)(["function encrypt(address to, uint128 value) public"]);
1764
- var WRAPPED_DECRYPT_ABI = (0, import_viem3.parseAbi)(["function decrypt(address to, uint128 value) public"]);
1765
- var WRAPPED_CLAIM_DECRYPTED_ABI = (0, import_viem3.parseAbi)(["function claimDecrypted(uint256 ctHash) public"]);
1766
- var WRAPPED_CLAIM_ALL_DECRYPTED_ABI = (0, import_viem3.parseAbi)(["function claimAllDecrypted() public"]);
1843
+ var WRAPPED_ENCRYPT_ABI = (0, import_viem4.parseAbi)(["function encrypt(address to, uint128 value) public"]);
1844
+ var WRAPPED_DECRYPT_ABI = (0, import_viem4.parseAbi)(["function decrypt(address to, uint128 value) public"]);
1845
+ var WRAPPED_CLAIM_DECRYPTED_ABI = (0, import_viem4.parseAbi)(["function claimDecrypted(uint256 ctHash) public"]);
1846
+ var WRAPPED_CLAIM_ALL_DECRYPTED_ABI = (0, import_viem4.parseAbi)(["function claimAllDecrypted() public"]);
1767
1847
  var WRAPPED_GET_USER_CLAIMS_ABI = [
1768
1848
  {
1769
1849
  inputs: [{ name: "user", type: "address" }],
@@ -1786,8 +1866,8 @@ var WRAPPED_GET_USER_CLAIMS_ABI = [
1786
1866
  type: "function"
1787
1867
  }
1788
1868
  ];
1789
- var WRAPPED_ETH_ENCRYPT_ETH_ABI = (0, import_viem3.parseAbi)(["function encryptETH(address to) public payable"]);
1790
- var WRAPPED_ETH_ENCRYPT_WETH_ABI = (0, import_viem3.parseAbi)(["function encryptWETH(address to, uint128 value) public"]);
1869
+ var WRAPPED_ETH_ENCRYPT_ETH_ABI = (0, import_viem4.parseAbi)(["function encryptETH(address to) public payable"]);
1870
+ var WRAPPED_ETH_ENCRYPT_WETH_ABI = (0, import_viem4.parseAbi)(["function encryptWETH(address to, uint128 value) public"]);
1791
1871
  var SHIELD_ABIS = {
1792
1872
  dual: {
1793
1873
  abi: DUAL_SHIELD_ABI,
@@ -1887,14 +1967,14 @@ function useCofheToken({ chainId: _chainId, address }, metdataQueryOptions) {
1887
1967
 
1888
1968
  // src/hooks/useCofheTokenPublicBalance.ts
1889
1969
  var import_react_query4 = require("@tanstack/react-query");
1890
- var import_viem5 = require("viem");
1970
+ var import_viem6 = require("viem");
1891
1971
 
1892
1972
  // src/constants/erc20ABIs.ts
1893
- var import_viem4 = require("viem");
1894
- var ERC20_BALANCE_OF_ABI = (0, import_viem4.parseAbi)(["function balanceOf(address owner) view returns (uint256)"]);
1895
- var ERC20_DECIMALS_ABI = (0, import_viem4.parseAbi)(["function decimals() view returns (uint8)"]);
1896
- var ERC20_SYMBOL_ABI = (0, import_viem4.parseAbi)(["function symbol() view returns (string)"]);
1897
- var ERC20_NAME_ABI = (0, import_viem4.parseAbi)(["function name() view returns (string)"]);
1973
+ var import_viem5 = require("viem");
1974
+ var ERC20_BALANCE_OF_ABI = (0, import_viem5.parseAbi)(["function balanceOf(address owner) view returns (uint256)"]);
1975
+ var ERC20_DECIMALS_ABI = (0, import_viem5.parseAbi)(["function decimals() view returns (uint8)"]);
1976
+ var ERC20_SYMBOL_ABI = (0, import_viem5.parseAbi)(["function symbol() view returns (string)"]);
1977
+ var ERC20_NAME_ABI = (0, import_viem5.parseAbi)(["function name() view returns (string)"]);
1898
1978
 
1899
1979
  // src/hooks/useCofheTokenPublicBalance.ts
1900
1980
  var import_ts_essentials2 = __toESM(require_dist(), 1);
@@ -2021,11 +2101,11 @@ function useCofheTokenPublicBalance({ token, accountAddress, displayDecimals = 5
2021
2101
 
2022
2102
  // src/hooks/useCofheTokenClaimable.ts
2023
2103
  var import_react_query6 = require("@tanstack/react-query");
2024
- var import_viem7 = require("viem");
2104
+ var import_viem8 = require("viem");
2025
2105
 
2026
2106
  // src/utils/decryptionAwareReadContract.ts
2027
2107
  var import_react_query5 = require("@tanstack/react-query");
2028
- var import_viem6 = require("viem");
2108
+ var import_viem7 = require("viem");
2029
2109
 
2030
2110
  // src/stores/decryptionWatchingStore.ts
2031
2111
  var import_zustand2 = require("zustand");
@@ -2301,9 +2381,87 @@ function useCofheTokenClaimable({ accountAddress: account, token }, queryOptions
2301
2381
  };
2302
2382
  }
2303
2383
 
2384
+ // src/hooks/useTokenAllowance.ts
2385
+ var import_react_query7 = require("@tanstack/react-query");
2386
+ var import_viem9 = require("viem");
2387
+ var import_ts_essentials4 = __toESM(require_dist(), 1);
2388
+ function constructTokenAllowanceQueryKey({
2389
+ chainId,
2390
+ tokenAddress,
2391
+ ownerAddress,
2392
+ spenderAddress
2393
+ }) {
2394
+ return [
2395
+ "tokenAllowance",
2396
+ chainId,
2397
+ tokenAddress?.toLowerCase(),
2398
+ ownerAddress?.toLowerCase(),
2399
+ spenderAddress?.toLowerCase()
2400
+ ];
2401
+ }
2402
+ function constructTokenAllowanceQueryKeyForInvalidation({
2403
+ chainId,
2404
+ tokenAddress,
2405
+ ownerAddress,
2406
+ spenderAddress
2407
+ }) {
2408
+ return constructTokenAllowanceQueryKey({
2409
+ chainId,
2410
+ tokenAddress,
2411
+ ownerAddress,
2412
+ spenderAddress
2413
+ });
2414
+ }
2415
+ function createTokenAllowanceQueryOptions(params) {
2416
+ const { publicClient, tokenAddress, ownerAddress, spenderAddress, queryOptions } = params;
2417
+ const { enabled: userEnabled, ...restQueryOptions } = queryOptions ?? {};
2418
+ const baseEnabled = !!publicClient && !!tokenAddress && !!ownerAddress && !!spenderAddress;
2419
+ const enabled = baseEnabled && (userEnabled ?? true);
2420
+ const queryKey = constructTokenAllowanceQueryKey({
2421
+ chainId: publicClient?.chain?.id,
2422
+ tokenAddress,
2423
+ ownerAddress,
2424
+ spenderAddress
2425
+ });
2426
+ return {
2427
+ queryKey,
2428
+ queryFn: async () => {
2429
+ (0, import_ts_essentials4.assert)(publicClient, "PublicClient is required to fetch token allowance");
2430
+ (0, import_ts_essentials4.assert)(tokenAddress, "Token address is required to fetch token allowance");
2431
+ (0, import_ts_essentials4.assert)(ownerAddress, "Owner address is required to fetch token allowance");
2432
+ (0, import_ts_essentials4.assert)(spenderAddress, "Spender address is required to fetch token allowance");
2433
+ return publicClient.readContract({
2434
+ address: tokenAddress,
2435
+ abi: ERC20_ALLOWANCE_ABI,
2436
+ functionName: "allowance",
2437
+ args: [ownerAddress, spenderAddress]
2438
+ });
2439
+ },
2440
+ enabled,
2441
+ refetchOnMount: false,
2442
+ ...restQueryOptions
2443
+ };
2444
+ }
2445
+ function useTokenAllowance({ tokenAddress, ownerAddress, spenderAddress }, options) {
2446
+ const publicClient = useCofhePublicClient();
2447
+ const queryOptions = createTokenAllowanceQueryOptions({
2448
+ publicClient,
2449
+ tokenAddress,
2450
+ ownerAddress,
2451
+ spenderAddress,
2452
+ queryOptions: options
2453
+ });
2454
+ const { data, isFetching, refetch } = useInternalQuery(queryOptions);
2455
+ return {
2456
+ data,
2457
+ isFetching,
2458
+ refetch
2459
+ };
2460
+ }
2461
+
2304
2462
  // src/hooks/useStoredTransactions.ts
2305
2463
  var import_react8 = require("react");
2306
- var import_ts_essentials4 = __toESM(require_dist());
2464
+ var import_ts_essentials5 = __toESM(require_dist());
2307
2465
  function useStoredTransactions({
2308
2466
  chainId,
2309
2467
  account,
@@ -2335,7 +2493,7 @@ function useStoredTransactions({
2335
2493
  return reducer?.(filtered);
2336
2494
  }, [filtered, reducer]);
2337
2495
  const uniqueHashes = new Set(filtered.map((tx) => tx.hash));
2338
- (0, import_ts_essentials4.assert)(
2496
+ (0, import_ts_essentials5.assert)(
2339
2497
  uniqueHashes.size === filtered.length,
2340
2498
  "There should be no duplicate transactions with the same hash in the result"
2341
2499
  );
@@ -2753,6 +2911,7 @@ var usePortalToasts = (0, import_zustand7.create)()((set, get) => ({
2753
2911
  }));
2754
2912
 
2755
2913
  // src/hooks/useTransactionGlobalLifecycle.ts
2914
+ var import_viem10 = require("viem");
2756
2915
  var TOAST_DELAY_MS = 1e4;
2757
2916
  function useTransactionGlobalToastsLifecycle() {
2758
2917
  const { addToast } = usePortalToasts();
@@ -2774,12 +2933,21 @@ function useTransactionGlobalToastsLifecycle() {
2774
2933
  );
2775
2934
  },
2776
2935
  onTransactionSubmitError: (error, transactionType) => {
2936
+ let humanReadableError;
2937
+ if (error instanceof import_viem10.ContractFunctionExecutionError) {
2938
+ if (error.cause instanceof import_viem10.ContractFunctionRevertedError) {
2939
+ const reasonOfRevert = error.cause.raw;
2940
+ if (typeof reasonOfRevert === "string") {
2941
+ humanReadableError = cofheHumanizeRevertReason(reasonOfRevert);
2942
+ }
2943
+ }
2944
+ }
2777
2945
  console.error("____ Transaction submission failed for type:", transactionType, "with error:", error);
2778
2946
  addToast(
2779
2947
  {
2780
2948
  variant: "error",
2781
2949
  title: `${transactionType} Transaction Submission Failed`,
2782
- description: error instanceof Error ? error.message : "An unknown error occurred during transaction submission."
2950
+ description: humanReadableError ?? (error instanceof Error ? error.message : "An unknown error occurred during transaction submission.")
2783
2951
  },
2784
2952
  TOAST_DELAY_MS
2785
2953
  );
@@ -2901,7 +3069,7 @@ function invalidatePublicTokenBalanceQueries({
2901
3069
  function invalidatePublicAndConfidentialTokenBalanceQueries(token, accountAddress, queryClient) {
2902
3070
  invalidateConfidentialTokenBalanceQueries(token, queryClient);
2903
3071
  const publicPairTokenAddress = token.extensions.fhenix.erc20Pair?.address;
2904
- (0, import_ts_essentials5.assert)(publicPairTokenAddress, "Public pair token address is required for shield transaction invalidation");
3072
+ (0, import_ts_essentials6.assert)(publicPairTokenAddress, "Public pair token address is required for shield transaction invalidation");
2905
3073
  invalidatePublicTokenBalanceQueries(
2906
3074
  {
2907
3075
  tokenAddress: publicPairTokenAddress,
@@ -2911,6 +3079,20 @@ function invalidatePublicAndConfidentialTokenBalanceQueries(token, accountAddres
2911
3079
  queryClient
2912
3080
  );
2913
3081
  }
3082
+ function invalidateTokenAllowanceQueries({
3083
+ chainId,
3084
+ tokenAddress,
3085
+ ownerAddress,
3086
+ spenderAddress
3087
+ }, queryClient) {
3088
+ const queryKey = constructTokenAllowanceQueryKeyForInvalidation({
3089
+ chainId,
3090
+ tokenAddress,
3091
+ ownerAddress,
3092
+ spenderAddress
3093
+ });
3094
+ queryClient.invalidateQueries({ queryKey });
3095
+ }
2914
3096
  function useTrackPendingTransactionsBase({
2915
3097
  onReceiptSuccess,
2916
3098
  onReceiptFail,
@@ -2921,9 +3103,11 @@ function useTrackPendingTransactionsBase({
2921
3103
  const queries = accountsPendingTxs.map((tx) => ({
2922
3104
  queryKey: ["tx-receipt", tx.chainId, tx.hash],
2923
3105
  queryFn: async () => {
2924
- (0, import_ts_essentials5.assert)(publicClient, "Public client is guaranteed by enabled condition");
3106
+ (0, import_ts_essentials6.assert)(publicClient, "Public client is guaranteed by enabled condition");
2925
3107
  try {
2926
- const receipt = await publicClient.waitForTransactionReceipt({ hash: tx.hash });
3108
+ const receipt = await publicClient.waitForTransactionReceipt({
3109
+ hash: tx.hash
3110
+ });
2927
3111
  const status = receipt.status === "success" ? "confirmed" /* Confirmed */ : "failed" /* Failed */;
2928
3112
  if (status === "confirmed" /* Confirmed */) onReceiptSuccess(tx, receipt);
2929
3113
  else onReceiptFail?.(tx, receipt);
@@ -2979,7 +3163,7 @@ function useHandleInvalidations() {
2979
3163
  });
2980
3164
  } else if (tx.actionType === "claim" /* Claim */) {
2981
3165
  const publicTokenAddress = tx.token.extensions.fhenix.erc20Pair?.address;
2982
- (0, import_ts_essentials5.assert)(publicTokenAddress, "Public pair token address is required for claim transaction invalidation");
3166
+ (0, import_ts_essentials6.assert)(publicTokenAddress, "Public pair token address is required for claim transaction invalidation");
2983
3167
  invalidatePublicTokenBalanceQueries(
2984
3168
  {
2985
3169
  tokenAddress: publicTokenAddress,
@@ -2993,6 +3177,20 @@ function useHandleInvalidations() {
2993
3177
  accountAddress: tx.account,
2994
3178
  queryClient
2995
3179
  });
3180
+ } else if (tx.actionType === "approve" /* Approve */) {
3181
+ const underlying = tx.token.extensions.fhenix.erc20Pair?.address;
3182
+ (0, import_ts_essentials6.assert)(underlying, "erc20Pair is required for approve transaction invalidation");
3183
+ if (underlying.toLowerCase() !== ETH_ADDRESS_LOWERCASE) {
3184
+ invalidateTokenAllowanceQueries(
3185
+ {
3186
+ chainId: tx.token.chainId,
3187
+ tokenAddress: underlying,
3188
+ ownerAddress: tx.account,
3189
+ spenderAddress: tx.token.address
3190
+ },
3191
+ queryClient
3192
+ );
3193
+ }
2996
3194
  } else {
2997
3195
  console.warn("No invalidation logic for transaction action type:", tx.actionType);
2998
3196
  }
@@ -3038,7 +3236,7 @@ var import_react15 = require("react");
3038
3236
 
3039
3237
  // src/hooks/useTransactionReceiptsByHash.ts
3040
3238
  var import_react13 = require("react");
3041
- var import_ts_essentials6 = __toESM(require_dist(), 1);
3239
+ var import_ts_essentials7 = __toESM(require_dist(), 1);
3042
3240
  function useTransactionReceiptsByHash({ hashes, chainId: chainIdInput, enabled: enabledInput = true, queryOptions } = {
3043
3241
  enabled: true
3044
3242
  }) {
@@ -3054,7 +3252,7 @@ function useTransactionReceiptsByHash({ hashes, chainId: chainIdInput, enabled:
3054
3252
  queryKey,
3055
3253
  enabled,
3056
3254
  queryFn: async () => {
3057
- (0, import_ts_essentials6.assert)(publicClient, "Public client is guaranteed by enabled condition");
3255
+ (0, import_ts_essentials7.assert)(publicClient, "Public client is guaranteed by enabled condition");
3058
3256
  return await publicClient.getTransactionReceipt({ hash });
3059
3257
  },
3060
3258
  notifyOnChangeProps: ["data"],
@@ -3081,7 +3279,7 @@ function useTransactionReceiptsByHash({ hashes, chainId: chainIdInput, enabled:
3081
3279
  }
3082
3280
 
3083
3281
  // src/hooks/useTrackDecryptingTransactions.ts
3084
- var import_ts_essentials7 = __toESM(require_dist());
3282
+ var import_ts_essentials8 = __toESM(require_dist());
3085
3283
 
3086
3284
  // src/hooks/useCofheReadDecryptionResults.ts
3087
3285
  var import_react14 = require("react");
@@ -3220,7 +3418,7 @@ function useTrackDecryptingTransactionsBase({ onDecryptionResolve }) {
3220
3418
  const minedTxsWithCiphertextsToWatch = (0, import_react15.useMemo)(() => {
3221
3419
  return Object.entries(minedTxsPendingDecryptionByHash).filter(([, tx]) => receiptsByHash[tx.hash] !== void 0).map(([, tx]) => {
3222
3420
  const ciphertextToWatch = findDecryptRequestLog(tx.account, receiptsByHash[tx.hash].logs)?.ciphertext;
3223
- (0, import_ts_essentials7.assert)(
3421
+ (0, import_ts_essentials8.assert)(
3224
3422
  ciphertextToWatch,
3225
3423
  "Ciphertext to watch should be found in the transaction logs if a tx that isPendingDecryption and has a receipt"
3226
3424
  );
@@ -3240,7 +3438,7 @@ function useTrackDecryptingTransactionsBase({ onDecryptionResolve }) {
3240
3438
  (0, import_react15.useEffect)(() => {
3241
3439
  const txsWithDecryptionResults = minedTxsWithCiphertextsToWatch.filter(({ ciphertextToWatch }) => decryptionResults[ciphertextToWatch] !== void 0).map((item) => {
3242
3440
  const decryptionResult = decryptionResults[item.ciphertextToWatch];
3243
- (0, import_ts_essentials7.assert)(decryptionResult, "decryptionResult must be defined here");
3441
+ (0, import_ts_essentials8.assert)(decryptionResult, "decryptionResult must be defined here");
3244
3442
  return {
3245
3443
  ...item,
3246
3444
  decryptionResult
@@ -3332,19 +3530,19 @@ var useCofheClient = () => {
3332
3530
  };
3333
3531
 
3334
3532
  // src/hooks/useCofheTokenDecryptedBalance.ts
3335
- var import_react_query9 = require("@tanstack/react-query");
3336
- var import_viem9 = require("viem");
3533
+ var import_react_query10 = require("@tanstack/react-query");
3534
+ var import_viem12 = require("viem");
3337
3535
  var import_sdk6 = require("@cofhe/sdk");
3338
- var import_ts_essentials9 = __toESM(require_dist(), 1);
3536
+ var import_ts_essentials10 = __toESM(require_dist(), 1);
3339
3537
 
3340
3538
  // src/hooks/useCofheReadContractAndDecrypt.ts
3341
- var import_react_query8 = require("@tanstack/react-query");
3342
- var import_viem8 = require("viem");
3539
+ var import_react_query9 = require("@tanstack/react-query");
3540
+ var import_viem11 = require("viem");
3343
3541
  var import_sdk5 = require("@cofhe/sdk");
3344
3542
 
3345
3543
  // src/hooks/useCofheDecrypt.ts
3346
3544
  var import_sdk4 = require("@cofhe/sdk");
3347
- var import_ts_essentials8 = __toESM(require_dist(), 1);
3545
+ var import_ts_essentials9 = __toESM(require_dist(), 1);
3348
3546
  function useCofheDecrypt({
3349
3547
  input,
3350
3548
  onPoll: onPoll2
@@ -3357,7 +3555,7 @@ function useCofheDecrypt({
3357
3555
  enabled,
3358
3556
  queryKey: ["decryptCiphertext", input?.ctHash.toString(), input?.utype],
3359
3557
  queryFn: async () => {
3360
- (0, import_ts_essentials8.assert)(input, "input is guaranteed to be defined by enabled condition");
3558
+ (0, import_ts_essentials9.assert)(input, "input is guaranteed to be defined by enabled condition");
3361
3559
  const builder = client.decryptForView(input.ctHash, input.utype);
3362
3560
  if (onPoll2) builder.onPoll(onPoll2);
3363
3561
  return builder.execute();
@@ -3449,7 +3647,7 @@ function useCofheTokenDecryptedBalance({ token, accountAddress, displayDecimals
3449
3647
  decryptingQueryOptions: {
3450
3648
  refetchOnMount: false,
3451
3649
  select: (amountWei) => {
3452
- (0, import_ts_essentials9.assert)(token, "Token must be defined to format confidential balance");
3650
+ (0, import_ts_essentials10.assert)(token, "Token must be defined to format confidential balance");
3453
3651
  if (typeof amountWei !== "bigint") {
3454
3652
  throw new Error("Expected bigint from confidential decryption");
3455
3653
  }
@@ -3468,7 +3666,7 @@ function useCofheTokenDecryptedBalance({ token, accountAddress, displayDecimals
3468
3666
  }
3469
3667
 
3470
3668
  // src/hooks/useCofheTokenTransfer.ts
3471
- var import_viem10 = require("viem");
3669
+ var import_viem13 = require("viem");
3472
3670
  var import_sdk9 = require("@cofhe/sdk");
3473
3671
 
3474
3672
  // src/hooks/useCofheEncryptAndWriteContract.ts
@@ -3476,7 +3674,7 @@ var import_abi2 = require("@cofhe/abi");
3476
3674
 
3477
3675
  // src/hooks/useCofheEncrypt.ts
3478
3676
  var import_sdk8 = require("@cofhe/sdk");
3479
- var import_ts_essentials10 = __toESM(require_dist(), 1);
3677
+ var import_ts_essentials11 = __toESM(require_dist(), 1);
3480
3678
 
3481
3679
  // src/hooks/internal/useStepsState.ts
3482
3680
  var import_sdk7 = require("@cofhe/sdk");
@@ -3572,7 +3770,7 @@ function useCofheEncrypt(options) {
3572
3770
  ...options,
3573
3771
  mutationKey: options?.mutationKey ?? ["cofhe", "encryptInputs"],
3574
3772
  mutationFn: async (variables) => {
3575
- (0, import_ts_essentials10.assert)(client, "CoFHE client not initialized");
3773
+ (0, import_ts_essentials11.assert)(client, "CoFHE client not initialized");
3576
3774
  const key = crypto.randomUUID();
3577
3775
  handleStepSetKey(key);
3578
3776
  const items = hasEncryptInputsOptions(variables) ? variables.items : variables;
@@ -3607,19 +3805,28 @@ function useCofheEncrypt(options) {
3607
3805
  }
3608
3806
 
3609
3807
  // src/hooks/useCofheWriteContract.ts
3610
- var import_ts_essentials11 = __toESM(require_dist(), 1);
3808
+ var import_ts_essentials12 = __toESM(require_dist(), 1);
3611
3809
  function hasExtras(variables) {
3612
3810
  return typeof variables === "object" && variables !== null && "writeContractInput" in variables && "extras" in variables;
3613
3811
  }
3614
3812
  function useCofheWriteContract(options) {
3615
3813
  const walletClient = useCofheWalletClient();
3814
+ const publicClient = useCofhePublicClient();
3616
3815
  const mutation = useInternalMutation({
3617
3816
  ...options,
3618
3817
  mutationKey: options?.mutationKey ?? ["cofhe", "walletWriteContract"],
3619
3818
  mutationFn: async (variables) => {
3620
- (0, import_ts_essentials11.assert)(walletClient, "WalletClient is required to write to a contract");
3819
+ (0, import_ts_essentials12.assert)(walletClient, "WalletClient is required to write to a contract");
3820
+ (0, import_ts_essentials12.assert)(publicClient, "PublicClient is required to simulate contract before writing");
3621
3821
  const writeContractInput = hasExtras(variables) ? variables.writeContractInput : variables;
3622
- return walletClient.writeContract(writeContractInput);
3822
+ const accountForSimulation = writeContractInput.account ?? walletClient.account;
3823
+ (0, import_ts_essentials12.assert)(accountForSimulation, "Wallet account is required to simulate contract before writing");
3824
+ const { chain, ...rest } = writeContractInput;
3825
+ const { request } = await publicClient.simulateContract({
3826
+ ...chain ? { ...rest, chain } : rest,
3827
+ account: accountForSimulation
3828
+ });
3829
+ return walletClient.writeContract({ ...request, chain: void 0 });
3623
3830
  }
3624
3831
  });
3625
3832
  const writeContractAsync = async (params) => mutation.mutateAsync(params);
@@ -3739,10 +3946,97 @@ function useCofheTokenTransfer(writeMutationOptions) {
3739
3946
  };
3740
3947
  }
3741
3948
 
3949
+ // src/hooks/useCofheTokenApprove.ts
3950
+ function useCofheTokenApprove(writeMutationOptions) {
3951
+ const { onSuccess: passedOnSuccess, onError: passedOnError, ...restOfOptions } = writeMutationOptions || {};
3952
+ const { onTransactionSubmitError } = useTransactionGlobalLifecycle();
3953
+ return useCofheWriteContract({
3954
+ ...restOfOptions,
3955
+ onError: (error, variables, onMutateResult, context) => {
3956
+ if (passedOnError) passedOnError(error, variables, onMutateResult, context);
3957
+ onTransactionSubmitError(error, "approve" /* Approve */);
3958
+ },
3959
+ onSuccess: (hash, variables, onMutateResult, context) => {
3960
+ if (passedOnSuccess) passedOnSuccess(hash, variables, onMutateResult, context);
3961
+ if (!hasExtras(variables)) return;
3962
+ const { token, tokenAmount, account } = variables.extras;
3963
+ useTransactionStore.getState().addTransaction({
3964
+ hash,
3965
+ token,
3966
+ tokenAmount,
3967
+ chainId: token.chainId,
3968
+ actionType: "approve" /* Approve */,
3969
+ isPendingDecryption: false,
3970
+ account
3971
+ });
3972
+ }
3973
+ });
3974
+ }
3975
+
3742
3976
  // src/hooks/useCofheTokenShield.ts
3743
- var import_react_query10 = require("@tanstack/react-query");
3744
- var import_viem11 = require("viem");
3745
- var import_ts_essentials12 = __toESM(require_dist(), 1);
3977
+ var import_react_query11 = require("@tanstack/react-query");
3978
+ var import_viem14 = require("viem");
3979
+ var import_ts_essentials13 = __toESM(require_dist(), 1);
3980
+ function getCofheTokenShieldCallArgs(params) {
3981
+ const { token, amount, account } = params;
3982
+ const tokenAddress = token.address;
3983
+ const confidentialityType = token.extensions.fhenix.confidentialityType;
3984
+ if (!confidentialityType) {
3985
+ throw new Error("confidentialityType is required in token extensions");
3986
+ }
3987
+ if (confidentialityType !== "dual" && confidentialityType !== "wrapped") {
3988
+ throw new Error(`Shield not supported for confidentialityType: ${confidentialityType}`);
3989
+ }
3990
+ if (confidentialityType === "dual") {
3991
+ const contractConfig = SHIELD_ABIS.dual;
3992
+ return {
3993
+ main: {
3994
+ address: tokenAddress,
3995
+ abi: contractConfig.abi,
3996
+ functionName: contractConfig.functionName,
3997
+ args: [amount],
3998
+ account,
3999
+ chain: void 0
4000
+ }
4001
+ };
4002
+ }
4003
+ const erc20PairAddress = token.extensions.fhenix.erc20Pair?.address;
4004
+ const isEth = erc20PairAddress?.toLowerCase() === ETH_ADDRESS_LOWERCASE;
4005
+ if (isEth) {
4006
+ return {
4007
+ main: {
4008
+ address: tokenAddress,
4009
+ abi: WRAPPED_ETH_ENCRYPT_ETH_ABI,
4010
+ functionName: "encryptETH",
4011
+ args: [account],
4012
+ value: amount,
4013
+ account,
4014
+ chain: void 0
4015
+ }
4016
+ };
4017
+ }
4018
+ if (!erc20PairAddress) {
4019
+ throw new Error("erc20Pair address is required for wrapped ERC20 tokens");
4020
+ }
4021
+ return {
4022
+ approval: {
4023
+ address: erc20PairAddress,
4024
+ abi: ERC20_APPROVE_ABI,
4025
+ functionName: "approve",
4026
+ args: [tokenAddress, amount],
4027
+ account,
4028
+ chain: void 0
4029
+ },
4030
+ main: {
4031
+ address: tokenAddress,
4032
+ abi: WRAPPED_ENCRYPT_ABI,
4033
+ functionName: "encrypt",
4034
+ args: [account, amount],
4035
+ account,
4036
+ chain: void 0
4037
+ }
4038
+ };
4039
+ }
3746
4040
  function useCofheTokenShield(options) {
3747
4041
  const walletClient = useCofheWalletClient();
3748
4042
  const publicClient = useCofhePublicClient();
@@ -3755,6 +4049,9 @@ function useCofheTokenShield(options) {
3755
4049
  if (!walletClient) {
3756
4050
  throw new Error("WalletClient is required for token shield");
3757
4051
  }
4052
+ if (!publicClient) {
4053
+ throw new Error("PublicClient is required to simulate shield before writing");
4054
+ }
3758
4055
  const tokenAddress = input.token.address;
3759
4056
  const confidentialityType = input.token.extensions.fhenix.confidentialityType;
3760
4057
  if (!confidentialityType) {
@@ -3771,66 +4068,39 @@ function useCofheTokenShield(options) {
3771
4068
  const erc20PairAddress = input.token.extensions.fhenix.erc20Pair?.address;
3772
4069
  const isEth = erc20PairAddress?.toLowerCase() === ETH_ADDRESS_LOWERCASE;
3773
4070
  if (isEth) {
3774
- hash = await walletClient.writeContract({
4071
+ const { request } = await publicClient.simulateContract({
3775
4072
  address: tokenAddress,
3776
4073
  abi: WRAPPED_ETH_ENCRYPT_ETH_ABI,
3777
4074
  functionName: "encryptETH",
3778
4075
  args: [walletClient.account.address],
3779
4076
  value: input.amount,
3780
- account: walletClient.account,
3781
- chain: void 0
4077
+ account: walletClient.account
3782
4078
  });
4079
+ hash = await walletClient.writeContract({ ...request, chain: void 0 });
3783
4080
  } else {
3784
4081
  if (!erc20PairAddress) {
3785
4082
  throw new Error("erc20Pair address is required for wrapped ERC20 tokens");
3786
4083
  }
3787
- if (!publicClient) {
3788
- throw new Error("PublicClient is required for allowance check");
3789
- }
3790
- input.onStatusChange?.("Checking allowance...");
3791
- const currentAllowance = await publicClient.readContract({
3792
- address: erc20PairAddress,
3793
- abi: ERC20_ALLOWANCE_ABI,
3794
- functionName: "allowance",
3795
- args: [walletClient.account.address, tokenAddress]
3796
- });
3797
- if (currentAllowance < input.amount) {
3798
- input.onStatusChange?.("Approval required - please confirm in wallet...");
3799
- const approvalHash = await walletClient.writeContract({
3800
- address: erc20PairAddress,
3801
- abi: ERC20_APPROVE_ABI,
3802
- functionName: "approve",
3803
- args: [tokenAddress, input.amount],
3804
- account: walletClient.account,
3805
- chain: void 0
3806
- });
3807
- input.onStatusChange?.("Waiting for approval confirmation...");
3808
- await publicClient.waitForTransactionReceipt({ hash: approvalHash });
3809
- input.onStatusChange?.("Approved! Now shielding...");
3810
- }
3811
4084
  input.onStatusChange?.("Please confirm shield in wallet...");
3812
- hash = await walletClient.writeContract({
4085
+ const { request: encryptRequest } = await publicClient.simulateContract({
3813
4086
  address: tokenAddress,
3814
4087
  abi: WRAPPED_ENCRYPT_ABI,
3815
4088
  functionName: "encrypt",
3816
4089
  args: [walletClient.account.address, input.amount],
3817
- account: walletClient.account,
3818
- chain: void 0
4090
+ account: walletClient.account
3819
4091
  });
4092
+ hash = await walletClient.writeContract({ ...encryptRequest, chain: void 0 });
3820
4093
  }
3821
4094
  } else {
3822
- const contractConfig = SHIELD_ABIS[confidentialityType];
3823
- if (!contractConfig) {
3824
- throw new Error(`Unsupported confidentialityType for shield: ${confidentialityType}`);
3825
- }
3826
- hash = await walletClient.writeContract({
4095
+ const contractConfig = SHIELD_ABIS.dual;
4096
+ const { request } = await publicClient.simulateContract({
3827
4097
  address: tokenAddress,
3828
4098
  abi: contractConfig.abi,
3829
4099
  functionName: contractConfig.functionName,
3830
4100
  args: [input.amount],
3831
- account: walletClient.account,
3832
- chain: void 0
4101
+ account: walletClient.account
3833
4102
  });
4103
+ hash = await walletClient.writeContract({ ...request, chain: void 0 });
3834
4104
  }
3835
4105
  return hash;
3836
4106
  },
@@ -3839,8 +4109,8 @@ function useCofheTokenShield(options) {
3839
4109
  onTransactionSubmitError(error, "shield" /* Shield */);
3840
4110
  },
3841
4111
  onSuccess: async (hash, input, onMutateResult, context) => {
3842
- (0, import_ts_essentials12.assert)(chainId, "Chain ID is required for token shield");
3843
- (0, import_ts_essentials12.assert)(account, "Wallet account is required for token shield");
4112
+ (0, import_ts_essentials13.assert)(chainId, "Chain ID is required for token shield");
4113
+ (0, import_ts_essentials13.assert)(account, "Wallet account is required for token shield");
3844
4114
  if (onSuccess) await onSuccess(hash, input, onMutateResult, context);
3845
4115
  useTransactionStore.getState().addTransaction({
3846
4116
  hash,
@@ -3858,19 +4128,19 @@ function useCofheTokenShield(options) {
3858
4128
  }
3859
4129
 
3860
4130
  // src/hooks/useCofheTokenUnshield.ts
3861
- var import_react_query11 = require("@tanstack/react-query");
3862
- var import_viem12 = require("viem");
3863
- var import_ts_essentials14 = __toESM(require_dist(), 1);
4131
+ var import_react_query12 = require("@tanstack/react-query");
4132
+ var import_viem15 = require("viem");
4133
+ var import_ts_essentials15 = __toESM(require_dist(), 1);
3864
4134
 
3865
4135
  // src/hooks/useStoredTransaction.ts
3866
4136
  var import_react17 = require("react");
3867
- var import_ts_essentials13 = __toESM(require_dist(), 1);
4137
+ var import_ts_essentials14 = __toESM(require_dist(), 1);
3868
4138
  function useStoredTransaction(txHash) {
3869
4139
  const filter3 = (0, import_react17.useMemo)(() => {
3870
4140
  return (tx) => tx.hash === txHash;
3871
4141
  }, [txHash]);
3872
4142
  const { filteredTxs } = useStoredTransactions({ filter: filter3 });
3873
- (0, import_ts_essentials13.assert)(filteredTxs.length <= 1, "There should be at most one transaction with a given hash");
4143
+ (0, import_ts_essentials14.assert)(filteredTxs.length <= 1, "There should be at most one transaction with a given hash");
3874
4144
  return filteredTxs[0];
3875
4145
  }
3876
4146
 
@@ -3953,8 +4223,43 @@ function useStoredTransactionDecryptionEffect({
3953
4223
  }
3954
4224
 
3955
4225
  // src/hooks/useCofheTokenUnshield.ts
4226
+ function getCofheTokenUnshieldCallArgs(params) {
4227
+ const { token, amount: rawAmount, account } = params;
4228
+ const tokenAddress = token.address;
4229
+ const confidentialityType = token.extensions.fhenix.confidentialityType;
4230
+ if (!confidentialityType) {
4231
+ throw new Error("confidentialityType is required in token extensions");
4232
+ }
4233
+ if (confidentialityType !== "dual" && confidentialityType !== "wrapped") {
4234
+ throw new Error(`Unshield not supported for confidentialityType: ${confidentialityType}`);
4235
+ }
4236
+ const contractConfig = UNSHIELD_ABIS[confidentialityType];
4237
+ if (!contractConfig) {
4238
+ throw new Error(`Unsupported confidentialityType for unshield: ${confidentialityType}`);
4239
+ }
4240
+ if (confidentialityType === "wrapped") {
4241
+ return {
4242
+ address: tokenAddress,
4243
+ abi: contractConfig.abi,
4244
+ functionName: contractConfig.functionName,
4245
+ args: [account, rawAmount],
4246
+ account,
4247
+ chain: void 0
4248
+ };
4249
+ }
4250
+ const amount = BigInt.asUintN(64, rawAmount);
4251
+ return {
4252
+ address: tokenAddress,
4253
+ abi: contractConfig.abi,
4254
+ functionName: contractConfig.functionName,
4255
+ args: [amount],
4256
+ account,
4257
+ chain: void 0
4258
+ };
4259
+ }
3956
4260
  function useCofheTokenUnshieldMutation(options) {
3957
4261
  const walletClient = useCofheWalletClient();
4262
+ const publicClient = useCofhePublicClient();
3958
4263
  const chainId = useCofheChainId();
3959
4264
  const account = useCofheAccount();
3960
4265
  const { onTransactionSubmitError } = useTransactionGlobalLifecycle();
@@ -3964,6 +4269,9 @@ function useCofheTokenUnshieldMutation(options) {
3964
4269
  if (!walletClient) {
3965
4270
  throw new Error("WalletClient is required for token unshield");
3966
4271
  }
4272
+ if (!publicClient) {
4273
+ throw new Error("PublicClient is required to simulate unshield before writing");
4274
+ }
3967
4275
  const tokenAddress = input.token.address;
3968
4276
  const confidentialityType = input.token.extensions.fhenix.confidentialityType;
3969
4277
  if (!confidentialityType) {
@@ -3982,24 +4290,24 @@ function useCofheTokenUnshieldMutation(options) {
3982
4290
  let hash;
3983
4291
  input.onStatusChange?.("Please confirm in wallet...");
3984
4292
  if (confidentialityType === "wrapped") {
3985
- hash = await walletClient.writeContract({
4293
+ const { request } = await publicClient.simulateContract({
3986
4294
  address: tokenAddress,
3987
4295
  abi: contractConfig.abi,
3988
4296
  functionName: contractConfig.functionName,
3989
4297
  args: [walletClient.account.address, input.amount],
3990
- account: walletClient.account,
3991
- chain: void 0
4298
+ account: walletClient.account
3992
4299
  });
4300
+ hash = await walletClient.writeContract({ ...request, chain: void 0 });
3993
4301
  } else {
3994
4302
  const amount = BigInt.asUintN(64, input.amount);
3995
- hash = await walletClient.writeContract({
4303
+ const { request } = await publicClient.simulateContract({
3996
4304
  address: tokenAddress,
3997
4305
  abi: contractConfig.abi,
3998
4306
  functionName: contractConfig.functionName,
3999
4307
  args: [amount],
4000
- account: walletClient.account,
4001
- chain: void 0
4308
+ account: walletClient.account
4002
4309
  });
4310
+ hash = await walletClient.writeContract({ ...request, chain: void 0 });
4003
4311
  }
4004
4312
  return hash;
4005
4313
  },
@@ -4008,8 +4316,8 @@ function useCofheTokenUnshieldMutation(options) {
4008
4316
  onTransactionSubmitError(error, "unshield" /* Unshield */);
4009
4317
  },
4010
4318
  onSuccess: async (hash, input, onMutateResult, context) => {
4011
- (0, import_ts_essentials14.assert)(chainId, "Chain ID is required for token unshield");
4012
- (0, import_ts_essentials14.assert)(account, "Wallet account is required for token unshield");
4319
+ (0, import_ts_essentials15.assert)(chainId, "Chain ID is required for token unshield");
4320
+ (0, import_ts_essentials15.assert)(account, "Wallet account is required for token unshield");
4013
4321
  if (onSuccess) await onSuccess(hash, input, onMutateResult, context);
4014
4322
  useTransactionStore.getState().addTransaction({
4015
4323
  hash,
@@ -4047,11 +4355,35 @@ function useCofheTokenUnshield(input) {
4047
4355
  }
4048
4356
 
4049
4357
  // src/hooks/useCofheTokenClaimUnshielded.ts
4050
- var import_react_query12 = require("@tanstack/react-query");
4051
- var import_viem13 = require("viem");
4052
- var import_ts_essentials15 = __toESM(require_dist(), 1);
4358
+ var import_react_query13 = require("@tanstack/react-query");
4359
+ var import_viem16 = require("viem");
4360
+ var import_ts_essentials16 = __toESM(require_dist(), 1);
4361
+ function getCofheTokenClaimUnshieldedCallArgs(params) {
4362
+ const { token, account } = params;
4363
+ const tokenAddress = token.address;
4364
+ const confidentialityType = token.extensions.fhenix.confidentialityType;
4365
+ if (!confidentialityType) {
4366
+ throw new Error("confidentialityType is required in token extensions");
4367
+ }
4368
+ if (confidentialityType !== "dual" && confidentialityType !== "wrapped") {
4369
+ throw new Error(`Claim not supported for confidentialityType: ${confidentialityType}`);
4370
+ }
4371
+ const contractConfig = CLAIM_ABIS[confidentialityType];
4372
+ if (!contractConfig) {
4373
+ throw new Error(`Unsupported confidentialityType for claim: ${confidentialityType}`);
4374
+ }
4375
+ return {
4376
+ address: tokenAddress,
4377
+ abi: contractConfig.abi,
4378
+ functionName: contractConfig.functionName,
4379
+ args: [],
4380
+ account,
4381
+ chain: void 0
4382
+ };
4383
+ }
4053
4384
  function useCofheTokenClaimUnshielded(options) {
4054
4385
  const walletClient = useCofheWalletClient();
4386
+ const publicClient = useCofhePublicClient();
4055
4387
  const chainId = useCofheChainId();
4056
4388
  const account = useCofheAccount();
4057
4389
  const { onSuccess, onError, ...restOfOptions } = options || {};
@@ -4061,6 +4393,9 @@ function useCofheTokenClaimUnshielded(options) {
4061
4393
  if (!walletClient) {
4062
4394
  throw new Error("WalletClient is required for claim");
4063
4395
  }
4396
+ if (!publicClient) {
4397
+ throw new Error("PublicClient is required to simulate claim before writing");
4398
+ }
4064
4399
  const tokenAddress = input.token.address;
4065
4400
  const confidentialityType = input.token.extensions.fhenix.confidentialityType;
4066
4401
  if (!confidentialityType) {
@@ -4076,23 +4411,22 @@ function useCofheTokenClaimUnshielded(options) {
4076
4411
  if (!contractConfig) {
4077
4412
  throw new Error(`Unsupported confidentialityType for claim: ${confidentialityType}`);
4078
4413
  }
4079
- const hash = await walletClient.writeContract({
4414
+ const { request } = await publicClient.simulateContract({
4080
4415
  address: tokenAddress,
4081
4416
  abi: contractConfig.abi,
4082
4417
  functionName: contractConfig.functionName,
4083
4418
  args: [],
4084
- account: walletClient.account,
4085
- chain: void 0
4419
+ account: walletClient.account
4086
4420
  });
4087
- return hash;
4421
+ return await walletClient.writeContract({ ...request, chain: void 0 });
4088
4422
  },
4089
4423
  onError: (error, variables, onMutateResult, context) => {
4090
4424
  if (onError) onError(error, variables, onMutateResult, context);
4091
4425
  onTransactionSubmitError(error, "claim" /* Claim */);
4092
4426
  },
4093
4427
  onSuccess: async (hash, input, onMutateResult, context) => {
4094
- (0, import_ts_essentials15.assert)(chainId, "Chain ID is required for claim");
4095
- (0, import_ts_essentials15.assert)(account, "Wallet account is required for claim");
4428
+ (0, import_ts_essentials16.assert)(chainId, "Chain ID is required for claim");
4429
+ (0, import_ts_essentials16.assert)(account, "Wallet account is required for claim");
4096
4430
  if (onSuccess) await onSuccess(hash, input, onMutateResult, context);
4097
4431
  useTransactionStore.getState().addTransaction({
4098
4432
  hash,
@@ -4110,10 +4444,10 @@ function useCofheTokenClaimUnshielded(options) {
4110
4444
  }
4111
4445
 
4112
4446
  // src/hooks/useCofheTokensClaimable.ts
4113
- var import_react_query13 = require("@tanstack/react-query");
4447
+ var import_react_query14 = require("@tanstack/react-query");
4114
4448
  var import_react22 = require("react");
4115
- var import_viem15 = require("viem");
4116
- var import_ts_essentials17 = __toESM(require_dist(), 1);
4449
+ var import_viem18 = require("viem");
4450
+ var import_ts_essentials18 = __toESM(require_dist(), 1);
4117
4451
 
4118
4452
  // src/hooks/useNormalizedList.ts
4119
4453
  var import_react20 = require("react");
@@ -4143,14 +4477,14 @@ function useNormalizedList(items, options) {
4143
4477
 
4144
4478
  // src/hooks/useIsWaitingForDecryptionByAddress.ts
4145
4479
  var import_react21 = require("react");
4146
- var import_viem14 = require("viem");
4147
- var import_ts_essentials16 = __toESM(require_dist(), 1);
4480
+ var import_viem17 = require("viem");
4481
+ var import_ts_essentials17 = __toESM(require_dist(), 1);
4148
4482
  function useIsWaitingForDecryptionByAddress(entries) {
4149
4483
  const awaitingDecryptionKeySet = useAwaitingDecryptionQueryKeySet();
4150
4484
  return (0, import_react21.useMemo)(() => {
4151
4485
  const map = {};
4152
4486
  for (const entry of entries) {
4153
- (0, import_ts_essentials16.assert)((0, import_viem14.isAddress)(entry.address), "address is valid");
4487
+ (0, import_ts_essentials17.assert)((0, import_viem17.isAddress)(entry.address), "address is valid");
4154
4488
  map[entry.address] = awaitingDecryptionKeySet.has(JSON.stringify(entry.queryKey));
4155
4489
  }
4156
4490
  return map;
@@ -4227,12 +4561,12 @@ function useCofheTokensClaimable({ tokens, accountAddress: account }, queryOptio
4227
4561
  signal,
4228
4562
  queryKey: queryKey2
4229
4563
  }) => {
4230
- (0, import_ts_essentials17.assert)(
4564
+ (0, import_ts_essentials18.assert)(
4231
4565
  isTokenConfidentialityTypeClaimable(confidentialityType),
4232
4566
  "confidentialityType narrowed by token guard"
4233
4567
  );
4234
- (0, import_ts_essentials17.assert)(account, "account is required to fetch unshield claims");
4235
- (0, import_ts_essentials17.assert)(publicClient, "publicClient is required to fetch unshield claims");
4568
+ (0, import_ts_essentials18.assert)(account, "account is required to fetch unshield claims");
4569
+ (0, import_ts_essentials18.assert)(publicClient, "publicClient is required to fetch unshield claims");
4236
4570
  return fetchUnshieldClaimsSummary({
4237
4571
  publicClient,
4238
4572
  token,
@@ -4296,29 +4630,17 @@ function useCofheTokensClaimable({ tokens, accountAddress: account }, queryOptio
4296
4630
  var import_react23 = require("react");
4297
4631
 
4298
4632
  // src/hooks/useCofheReadContracts.ts
4299
- var import_react_query14 = require("@tanstack/react-query");
4300
- var import_ts_essentials18 = __toESM(require_dist(), 1);
4633
+ var import_react_query15 = require("@tanstack/react-query");
4634
+ var import_ts_essentials19 = __toESM(require_dist(), 1);
4301
4635
  var import_abi3 = require("@cofhe/abi");
4302
4636
  var QUERY_CACHE_PREFIX2 = "cofheReadContracts";
4303
- function serializeIfBigint2(value) {
4304
- if (typeof value === "bigint") {
4305
- return value.toString();
4306
- }
4307
- }
4308
- function serializeBigintRecursively2(value) {
4309
- if (Array.isArray(value)) {
4310
- return value.map(serializeBigintRecursively2);
4311
- } else {
4312
- return serializeIfBigint2(value);
4313
- }
4314
- }
4315
4637
  function constructCofheReadContractsQueryKey(params) {
4316
4638
  const { cofheChainId, contracts, allowFailure, enabled } = params;
4317
4639
  const serializedContracts = (contracts ?? []).map((contract) => {
4318
4640
  return {
4319
4641
  address: contract.address,
4320
4642
  functionName: contract.functionName,
4321
- args: Array.isArray(contract.args) ? serializeBigintRecursively2(contract.args) : contract.args,
4643
+ args: Array.isArray(contract.args) ? serializeBigintRecursively(contract.args) : contract.args,
4322
4644
  abi: contract.abi
4323
4645
  };
4324
4646
  });
@@ -4357,8 +4679,8 @@ function useCofheReadContracts(params, queryOptions) {
4357
4679
  enabled
4358
4680
  }),
4359
4681
  queryFn: async () => {
4360
- (0, import_ts_essentials18.assert)(publicClient, "PublicClient should be guaranteed by enabled check");
4361
- (0, import_ts_essentials18.assert)(contracts && contracts.length > 0, "Contracts should be guaranteed by enabled check");
4682
+ (0, import_ts_essentials19.assert)(publicClient, "PublicClient should be guaranteed by enabled check");
4683
+ (0, import_ts_essentials19.assert)(contracts && contracts.length > 0, "Contracts should be guaranteed by enabled check");
4362
4684
  const raw = await publicClient.multicall({
4363
4685
  contracts,
4364
4686
  ...multicallOptions || {}
@@ -4537,6 +4859,69 @@ function useTokensWithPublicBalances({ chainId: _chainId, accountAddress, displa
4537
4859
  };
4538
4860
  }
4539
4861
 
4862
+ // src/hooks/useCofheSimulateWriteContract.ts
4863
+ var import_ts_essentials20 = __toESM(require_dist(), 1);
4864
+ var QUERY_CACHE_PREFIX3 = "cofheSimulateWriteContract";
4865
+ function isRecord2(value) {
4866
+ return typeof value === "object" && value !== null;
4867
+ }
4868
+ function hasAddress(value) {
4869
+ return "address" in value && typeof value.address === "string";
4870
+ }
4871
+ function getAccountAddress(account) {
4872
+ if (!account) return void 0;
4873
+ if (typeof account === "string") return account;
4874
+ if (typeof account === "object" && hasAddress(account)) return account.address;
4875
+ return void 0;
4876
+ }
4877
+ function constructCofheSimulateWriteContractQueryKey(params) {
4878
+ const { cofheChainId, callArgs, resolvedAccountAddress, enabled } = params;
4879
+ const serialized = isRecord2(callArgs) ? {
4880
+ address: typeof callArgs.address === "string" ? callArgs.address : void 0,
4881
+ functionName: typeof callArgs.functionName === "string" ? callArgs.functionName : void 0,
4882
+ args: Array.isArray(callArgs.args) ? serializeBigintRecursively(callArgs.args) : void 0,
4883
+ value: serializeIfBigint(callArgs.value),
4884
+ dataSuffix: typeof callArgs.dataSuffix === "string" ? callArgs.dataSuffix : void 0,
4885
+ account: resolvedAccountAddress
4886
+ } : void 0;
4887
+ return [QUERY_CACHE_PREFIX3, cofheChainId, serialized, enabled];
4888
+ }
4889
+ function useCofheSimulateWriteContract(callArgs, queryOptions) {
4890
+ const walletClient = useCofheWalletClient();
4891
+ const publicClient = useCofhePublicClient();
4892
+ const cofheChainId = useCofheChainId();
4893
+ const isCofheErrorActive = useIsCofheErrorActive();
4894
+ const { enabled: userEnabled, ...restQueryOptions } = queryOptions || {};
4895
+ const resolvedAccountAddress = getAccountAddress(callArgs?.account ?? walletClient?.account);
4896
+ const enabled = !isCofheErrorActive && !!publicClient && !!walletClient && !!callArgs && !!resolvedAccountAddress && (userEnabled ?? true);
4897
+ return useInternalQuery({
4898
+ enabled,
4899
+ queryKey: constructCofheSimulateWriteContractQueryKey({
4900
+ cofheChainId,
4901
+ callArgs,
4902
+ resolvedAccountAddress,
4903
+ enabled
4904
+ }),
4905
+ retry: false,
4906
+ // no need to retry on error. The whole point of simulation is to check if there's an error
4907
+ queryFn: async () => {
4908
+ (0, import_ts_essentials20.assert)(publicClient, "PublicClient is required to simulate contract");
4909
+ (0, import_ts_essentials20.assert)(walletClient, "WalletClient is required to simulate contract");
4910
+ (0, import_ts_essentials20.assert)(callArgs, "callArgs is guaranteed to be defined by enabled condition");
4911
+ const accountForSimulation = callArgs.account ?? walletClient.account;
4912
+ (0, import_ts_essentials20.assert)(accountForSimulation, "Wallet account is required to simulate contract");
4913
+ const simulateArgs = {
4914
+ ...callArgs,
4915
+ account: accountForSimulation
4916
+ };
4917
+ return publicClient.simulateContract(
4918
+ simulateArgs
4919
+ );
4920
+ },
4921
+ ...restQueryOptions
4922
+ });
4923
+ }
4924
+
4540
4925
  // src/hooks/permits/usePermitDuration.ts
4541
4926
  var import_react25 = require("react");
4542
4927
  function usePermitDuration(options) {
@@ -4871,7 +5256,7 @@ function useReceivePermit({ onSuccess, onError } = {}) {
4871
5256
 
4872
5257
  // src/hooks/permits/usePermitDetailsAndActions.ts
4873
5258
  var import_react31 = require("react");
4874
- var import_viem16 = require("viem");
5259
+ var import_viem19 = require("viem");
4875
5260
  var import_permits4 = require("@cofhe/sdk/permits");
4876
5261
 
4877
5262
  // src/hooks/useCopyFeedback.ts
@@ -4969,7 +5354,7 @@ var usePermitDetailsAndActions = (permitHash) => {
4969
5354
  const permitLabel = (0, import_react31.useMemo)(() => {
4970
5355
  if (!permit) return void 0;
4971
5356
  if (permit.name?.trim()) return permit.name;
4972
- const fallbackAddress = permit.recipient && permit.recipient !== import_viem16.zeroAddress ? permit.recipient : permit.issuer || import_viem16.zeroAddress;
5357
+ const fallbackAddress = permit.recipient && permit.recipient !== import_viem19.zeroAddress ? permit.recipient : permit.issuer || import_viem19.zeroAddress;
4973
5358
  return truncateAddress(fallbackAddress, 4, 4) ?? fallbackAddress;
4974
5359
  }, [permit]);
4975
5360
  const expirationInfo = (0, import_react31.useMemo)(() => formatExpirationLabel(permit?.expiration), [permit?.expiration]);
@@ -5016,10 +5401,10 @@ var usePermitDetailsAndActions = (permitHash) => {
5016
5401
  var import_react33 = require("react");
5017
5402
 
5018
5403
  // src/providers/internalQueryHooks.ts
5019
- var import_react_query16 = require("@tanstack/react-query");
5404
+ var import_react_query17 = require("@tanstack/react-query");
5020
5405
 
5021
5406
  // src/providers/queryUtils.ts
5022
- var import_react_query15 = require("@tanstack/react-query");
5407
+ var import_react_query16 = require("@tanstack/react-query");
5023
5408
  var import_react_query_persist_client = require("@tanstack/react-query-persist-client");
5024
5409
  var import_query_async_storage_persister = require("@tanstack/query-async-storage-persister");
5025
5410
  var import_react32 = require("react");
@@ -5071,17 +5456,17 @@ function applyPersistedDefaults(options) {
5071
5456
  }
5072
5457
  function useInternalQuery(options) {
5073
5458
  const qc = useInternalQueryClient();
5074
- return (0, import_react_query16.useQuery)(applyPersistedDefaults(options), qc);
5459
+ return (0, import_react_query17.useQuery)(applyPersistedDefaults(options), qc);
5075
5460
  }
5076
5461
  function useInternalMutation(options) {
5077
5462
  const qc = useInternalQueryClient();
5078
- return (0, import_react_query16.useMutation)(options, qc);
5463
+ return (0, import_react_query17.useMutation)(options, qc);
5079
5464
  }
5080
5465
  var useInternalQueries2 = (options, _ignoredQueryClient) => {
5081
5466
  const qc = useInternalQueryClient();
5082
5467
  if (options && typeof options === "object" && "queries" in options) {
5083
5468
  const typedOptions = options;
5084
- return (0, import_react_query16.useQueries)(
5469
+ return (0, import_react_query17.useQueries)(
5085
5470
  {
5086
5471
  ...options,
5087
5472
  queries: typedOptions.queries.map((q) => applyPersistedDefaults(q))
@@ -5089,7 +5474,7 @@ var useInternalQueries2 = (options, _ignoredQueryClient) => {
5089
5474
  qc
5090
5475
  );
5091
5476
  }
5092
- return (0, import_react_query16.useQueries)(options, qc);
5477
+ return (0, import_react_query17.useQueries)(options, qc);
5093
5478
  };
5094
5479
 
5095
5480
  // src/hooks/useCofheConnect.ts
@@ -6150,7 +6535,7 @@ var import_io2 = require("react-icons/io");
6150
6535
  var import_ai = require("react-icons/ai");
6151
6536
  var import_tb3 = require("react-icons/tb");
6152
6537
  var import_fa = require("react-icons/fa");
6153
- var import_ts_essentials19 = __toESM(require_dist(), 1);
6538
+ var import_ts_essentials21 = __toESM(require_dist(), 1);
6154
6539
 
6155
6540
  // src/components/FnxFloatingButton/components/ActionButton.tsx
6156
6541
  var import_jsx_runtime22 = require("react/jsx-runtime");
@@ -6416,7 +6801,7 @@ var PageContainer = ({ isModal, header, content, footer }) => {
6416
6801
  };
6417
6802
 
6418
6803
  // src/components/FnxFloatingButton/components/TransactionItem.tsx
6419
- var import_viem17 = require("viem");
6804
+ var import_viem20 = require("viem");
6420
6805
  var import_go = require("react-icons/go");
6421
6806
  var import_tb2 = require("react-icons/tb");
6422
6807
  var import_jsx_runtime27 = require("react/jsx-runtime");
@@ -6431,6 +6816,8 @@ var ActionIcon = ({ actionType }) => {
6431
6816
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_tb2.TbShieldMinus, { className: iconClassName2 });
6432
6817
  case "claim" /* Claim */:
6433
6818
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_tb2.TbShieldCheck, { className: iconClassName2 });
6819
+ case "approve" /* Approve */:
6820
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_go.GoArrowUpRight, { className: iconClassName2 });
6434
6821
  default:
6435
6822
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_go.GoArrowUpRight, { className: iconClassName2 });
6436
6823
  }
@@ -6443,7 +6830,7 @@ var TransactionItem = ({ transaction }) => {
6443
6830
  /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
6444
6831
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "text-sm font-semibold fnx-text-primary", children: actionToString(transaction.actionType) }),
6445
6832
  /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "text-sm font-semibold fnx-text-primary", children: [
6446
- (0, import_viem17.formatUnits)(transaction.tokenAmount, transaction.token.decimals),
6833
+ (0, import_viem20.formatUnits)(transaction.tokenAmount, transaction.token.decimals),
6447
6834
  " ",
6448
6835
  transaction.token.symbol
6449
6836
  ] })
@@ -6527,12 +6914,12 @@ var BottomNavigation = () => {
6527
6914
  return;
6528
6915
  }
6529
6916
  if (page === "send" /* Send */) {
6530
- (0, import_ts_essentials19.assert)(defaultToken, "No pinned token available for Send page");
6917
+ (0, import_ts_essentials21.assert)(defaultToken, "No pinned token available for Send page");
6531
6918
  navigateTo("send" /* Send */, { pageProps: { token: defaultToken } });
6532
6919
  return;
6533
6920
  }
6534
6921
  if (page === "shield" /* Shield */) {
6535
- (0, import_ts_essentials19.assert)(defaultToken, "No pinned token available for Shield page");
6922
+ (0, import_ts_essentials21.assert)(defaultToken, "No pinned token available for Shield page");
6536
6923
  navigateTo("shield" /* Shield */, { pageProps: { token: defaultToken } });
6537
6924
  return;
6538
6925
  }
@@ -6747,7 +7134,7 @@ var InfoModalButton = ({ onClick }) => {
6747
7134
 
6748
7135
  // src/components/FnxFloatingButton/components/PermitCard.tsx
6749
7136
  var import_fa63 = require("react-icons/fa6");
6750
- var import_viem18 = require("viem");
7137
+ var import_viem21 = require("viem");
6751
7138
  var import_permits7 = require("@cofhe/sdk/permits");
6752
7139
 
6753
7140
  // src/components/StripedBackground.tsx
@@ -6867,14 +7254,14 @@ var BasePermitCard = ({
6867
7254
  ] })
6868
7255
  ] }),
6869
7256
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("br", {}),
6870
- permit.recipient != null && permit.recipient !== import_viem18.zeroAddress && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
7257
+ permit.recipient != null && permit.recipient !== import_viem21.zeroAddress && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
6871
7258
  "Recipient: ",
6872
7259
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("b", { children: truncateAddress(permit.recipient, 6, 6) }),
6873
7260
  " ",
6874
7261
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CopyButton2, { hash: permit.recipient }),
6875
7262
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("br", {})
6876
7263
  ] }),
6877
- permit.validatorContract != null && permit.validatorContract !== import_viem18.zeroAddress && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
7264
+ permit.validatorContract != null && permit.validatorContract !== import_viem21.zeroAddress && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
6878
7265
  "Validator:",
6879
7266
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("br", {}),
6880
7267
  " ",
@@ -7541,7 +7928,7 @@ var TokenInfoBalanceChart = ({ token, chartPoints }) => {
7541
7928
 
7542
7929
  // src/components/FnxFloatingButton/pages/TokenInfoTransactionHistory.tsx
7543
7930
  var import_react52 = require("react");
7544
- var import_viem19 = require("viem");
7931
+ var import_viem22 = require("viem");
7545
7932
  var import_jsx_runtime46 = require("react/jsx-runtime");
7546
7933
  var TokenInfoTransactionHistoryHeader = ({ token }) => {
7547
7934
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center justify-between", children: [
@@ -7565,7 +7952,7 @@ var TokenInfoTransactionHistory = ({ token, priceUsd }) => {
7565
7952
  });
7566
7953
  const activity = (0, import_react52.useMemo)(() => {
7567
7954
  return tokenTxs.slice().sort((a, b) => b.timestamp - a.timestamp).map((tx) => {
7568
- const amountToken = parseFloat((0, import_viem19.formatUnits)(tx.tokenAmount, tx.token.decimals));
7955
+ const amountToken = parseFloat((0, import_viem22.formatUnits)(tx.tokenAmount, tx.token.decimals));
7569
7956
  const amountUsd = typeof priceUsd === "number" && Number.isFinite(amountToken) ? amountToken * priceUsd : void 0;
7570
7957
  return {
7571
7958
  kind: actionToString(tx.actionType),
@@ -7672,8 +8059,8 @@ var TokenInfoPage = ({ token }) => {
7672
8059
  var import_react54 = require("react");
7673
8060
  var import_ArrowBack7 = __toESM(require("@mui/icons-material/ArrowBack"), 1);
7674
8061
  var import_KeyboardArrowRight2 = __toESM(require("@mui/icons-material/KeyboardArrowRight"), 1);
7675
- var import_viem20 = require("viem");
7676
- var import_ts_essentials20 = __toESM(require_dist(), 1);
8062
+ var import_viem23 = require("viem");
8063
+ var import_ts_essentials22 = __toESM(require_dist(), 1);
7677
8064
  var import_jsx_runtime48 = require("react/jsx-runtime");
7678
8065
  var SendPage = ({ token: _token }) => {
7679
8066
  const [overriddenToken, setOverriddenToken] = (0, import_react54.useState)(null);
@@ -7692,7 +8079,7 @@ var SendPage = ({ token: _token }) => {
7692
8079
  }
7693
8080
  } = useCofheTokenTransfer({
7694
8081
  onError: (error2) => {
7695
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to send tokens";
8082
+ const errorMessage = cofheHumanizeViemError(error2) ?? (error2 instanceof Error ? error2.message : "Failed to send tokens");
7696
8083
  setError(errorMessage);
7697
8084
  console.error("Send tx submit error:", error2);
7698
8085
  },
@@ -7725,13 +8112,13 @@ var SendPage = ({ token: _token }) => {
7725
8112
  const [recipientAddress, setRecipientAddress] = (0, import_react54.useState)("");
7726
8113
  const [error, setError] = (0, import_react54.useState)(null);
7727
8114
  const [success, setSuccess] = (0, import_react54.useState)(null);
7728
- const isValidAddress2 = (0, import_viem20.isAddress)(recipientAddress);
8115
+ const isValidAddress2 = (0, import_viem23.isAddress)(recipientAddress);
7729
8116
  const isValidAmount = (amount.length > 0 && confidentialUnitBalance && confidentialUnitBalance.gte(amount)) ?? false;
7730
8117
  const handleSend = async () => {
7731
- (0, import_ts_essentials20.assert)((0, import_viem20.isAddress)(recipientAddress), "Recipient address is not valid");
8118
+ (0, import_ts_essentials22.assert)((0, import_viem23.isAddress)(recipientAddress), "Recipient address is not valid");
7732
8119
  const amountWei = unitToWei(amount, token.decimals);
7733
- (0, import_ts_essentials20.assert)(amountWei <= import_viem20.maxUint128, "Amount exceeds maximum supported value (uint128 max)");
7734
- (0, import_ts_essentials20.assert)(account, "Sender account is required");
8120
+ (0, import_ts_essentials22.assert)(amountWei <= import_viem23.maxUint128, "Amount exceeds maximum supported value (uint128 max)");
8121
+ (0, import_ts_essentials22.assert)(account, "Sender account is required");
7735
8122
  await encryptAmountAndSendToken({
7736
8123
  input: {
7737
8124
  token,
@@ -7745,6 +8132,7 @@ var SendPage = ({ token: _token }) => {
7745
8132
  if (confidentialUnitBalance) setAmount(confidentialUnitBalance.toFixed());
7746
8133
  };
7747
8134
  const { tokensWithExistingEncryptedBalance } = useCofheTokensWithExistingEncryptedBalances();
8135
+ const disabled = !isValidAddress2 || !isValidAmount || isSending || isEncryptingInput;
7748
8136
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
7749
8137
  PageContainer,
7750
8138
  {
@@ -7851,11 +8239,11 @@ var SendPage = ({ token: _token }) => {
7851
8239
  "button",
7852
8240
  {
7853
8241
  onClick: handleSend,
7854
- disabled: !isValidAddress2 || !isValidAmount || isSending || isEncryptingInput,
8242
+ disabled,
7855
8243
  className: cn(
7856
8244
  "fnx-send-button w-full py-3 px-4 font-small",
7857
8245
  "flex items-center justify-center gap-2",
7858
- isValidAddress2 && isValidAmount && !isSending && !isEncryptingInput ? "fnx-send-button-enabled" : "fnx-send-button-disabled"
8246
+ disabled ? "fnx-send-button-disabled" : "fnx-send-button-enabled"
7859
8247
  ),
7860
8248
  children: [
7861
8249
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { children: "Send" }),
@@ -7873,8 +8261,8 @@ var import_KeyboardArrowDown = __toESM(require("@mui/icons-material/KeyboardArro
7873
8261
  var import_tb4 = require("react-icons/tb");
7874
8262
  var import_lu2 = require("react-icons/lu");
7875
8263
  var import_react56 = require("react");
7876
- var import_viem21 = require("viem");
7877
- var import_ts_essentials21 = __toESM(require_dist(), 1);
8264
+ var import_viem24 = require("viem");
8265
+ var import_ts_essentials23 = __toESM(require_dist(), 1);
7878
8266
 
7879
8267
  // src/hooks/useIsUnshieldingMining.ts
7880
8268
  var import_react55 = require("react");
@@ -7908,6 +8296,13 @@ function useLifecycleStore() {
7908
8296
  setStatus
7909
8297
  };
7910
8298
  }
8299
+ function humanizeSimulationError(error, fallbackMessage) {
8300
+ if (!error) return null;
8301
+ const cofheErrorMessage = cofheHumanizeViemError(error);
8302
+ if (cofheErrorMessage) return cofheErrorMessage;
8303
+ if (error instanceof import_viem24.ContractFunctionExecutionError) return error.shortMessage;
8304
+ return fallbackMessage;
8305
+ }
7911
8306
  function TxHashWithActions({ hash, chainId }) {
7912
8307
  const href = chainId ? getBlockExplorerTxUrl(chainId, hash) : void 0;
7913
8308
  const ellipsed = truncateHash(hash);
@@ -7956,7 +8351,7 @@ function useClaimUnshieldedWithLifecycle() {
7956
8351
  });
7957
8352
  },
7958
8353
  onError: (error2) => {
7959
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to claim unshielded tokens";
8354
+ const errorMessage = cofheHumanizeViemError(error2) ?? (error2 instanceof Error ? error2.message : "Failed to claim unshielded tokens");
7960
8355
  setError(errorMessage);
7961
8356
  setStatus(null);
7962
8357
  console.error("Claim tx submit error:", error2);
@@ -8015,12 +8410,18 @@ function useShieldWithLifecycle(token) {
8015
8410
  setShieldAmount("");
8016
8411
  },
8017
8412
  onError: (error2) => {
8018
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to shield tokens";
8413
+ const errorMessage = cofheHumanizeViemError(error2) ?? (error2 instanceof Error ? error2.message : "Failed to shield tokens");
8019
8414
  setError(errorMessage);
8020
8415
  setStatus(null);
8021
8416
  console.error("Shield tx submit error:", error2);
8022
8417
  }
8023
8418
  });
8419
+ const { tokenApprove, isApprovingMining } = useApproveWithLifecycle({
8420
+ chainId,
8421
+ setError,
8422
+ setStatus,
8423
+ scheduleStatusClear
8424
+ });
8024
8425
  const { isMining: isTokenShieldMining } = useOnceTransactionMined({
8025
8426
  txHash: tokenShield.data,
8026
8427
  onceMined: (transaction) => {
@@ -8052,6 +8453,15 @@ function useShieldWithLifecycle(token) {
8052
8453
  onStatusChange: (message) => setStatus({ message, type: "info" })
8053
8454
  });
8054
8455
  };
8456
+ const handleApprove = async () => {
8457
+ (0, import_ts_essentials23.assert)(approvalCallArgs, "Approval call args are required to approve");
8458
+ (0, import_ts_essentials23.assert)(shieldAmountWei, "Shield amount is required to approve");
8459
+ (0, import_ts_essentials23.assert)(account, "Wallet account is required to approve");
8460
+ await tokenApprove.writeContractAsync({
8461
+ writeContractInput: { ...approvalCallArgs, account: account ?? null, chain: void 0 },
8462
+ extras: { token, tokenAmount: shieldAmountWei, account }
8463
+ });
8464
+ };
8055
8465
  const { data: { unit: publicBalanceUnit } = {}, isFetching: isFetchingPublic } = useCofheTokenPublicBalance({
8056
8466
  token,
8057
8467
  accountAddress: account
@@ -8061,20 +8471,66 @@ function useShieldWithLifecycle(token) {
8061
8471
  if (publicBalanceUnit) setShieldAmount(publicBalanceUnit.toFixed());
8062
8472
  };
8063
8473
  const isValidShieldAmount = (shieldAmount.length > 0 && publicBalanceUnit?.gte(shieldAmount)) ?? false;
8474
+ const shieldAmountWei = (0, import_react56.useMemo)(() => {
8475
+ if (!isValidShieldAmount) return void 0;
8476
+ try {
8477
+ return unitToWei(shieldAmount, token.decimals);
8478
+ } catch {
8479
+ return void 0;
8480
+ }
8481
+ }, [isValidShieldAmount, shieldAmount, token.decimals]);
8482
+ const shieldTxCallArgs = (0, import_react56.useMemo)(() => {
8483
+ if (!account || !shieldAmountWei) return void 0;
8484
+ return getCofheTokenShieldCallArgs({ token, amount: shieldAmountWei, account });
8485
+ }, [account, shieldAmountWei, token]);
8486
+ const shieldCallArgs = shieldTxCallArgs?.main;
8487
+ const approvalCallArgs = shieldTxCallArgs?.approval;
8488
+ const approvalSpender = approvalCallArgs?.args?.[0] ?? void 0;
8489
+ const { data: allowanceWei, isFetching: isFetchingAllowance } = useTokenAllowance(
8490
+ {
8491
+ tokenAddress: approvalCallArgs?.address,
8492
+ ownerAddress: account,
8493
+ spenderAddress: approvalSpender
8494
+ },
8495
+ {
8496
+ enabled: !!approvalCallArgs && !!account && !!approvalSpender && !!shieldAmountWei
8497
+ }
8498
+ );
8499
+ const requiresApproval = !!approvalCallArgs;
8500
+ const isAllowanceKnown = !requiresApproval || typeof allowanceWei === "bigint";
8501
+ const hasSufficientAllowance = !requiresApproval || typeof allowanceWei === "bigint" && typeof shieldAmountWei === "bigint" && allowanceWei >= shieldAmountWei;
8502
+ const shouldApprove = requiresApproval && typeof allowanceWei === "bigint" && typeof shieldAmountWei === "bigint" && allowanceWei < shieldAmountWei;
8503
+ const approvalSimulation = useCofheSimulateWriteContract(approvalCallArgs, {
8504
+ enabled: shouldApprove
8505
+ });
8506
+ const shieldSimulation = useCofheSimulateWriteContract(shieldCallArgs, {
8507
+ enabled: !!shieldCallArgs && !shouldApprove
8508
+ });
8509
+ const simulationError = (0, import_react56.useMemo)(() => {
8510
+ const relevantError = shouldApprove ? approvalSimulation.error : shieldSimulation.error;
8511
+ if (!relevantError) return null;
8512
+ const cofheErrorMessage = cofheHumanizeViemError(relevantError);
8513
+ if (cofheErrorMessage) return cofheErrorMessage;
8514
+ if (relevantError instanceof import_viem24.ContractFunctionExecutionError) return relevantError.shortMessage;
8515
+ return shouldApprove ? "Failed to simulate approval transaction" : "Failed to simulate shield transaction";
8516
+ }, [approvalSimulation.error, shieldSimulation.error, shouldApprove]);
8517
+ const canShield = isValidShieldAmount && !!shieldCallArgs && isAllowanceKnown && hasSufficientAllowance && !shieldSimulation.isFetching && !shieldSimulation.error;
8518
+ const canApprove = isValidShieldAmount && shouldApprove && !isFetchingAllowance && !approvalSimulation.isFetching && !approvalSimulation.error;
8519
+ const canWrite = shouldApprove ? canApprove : canShield;
8064
8520
  return {
8065
8521
  status,
8066
- error,
8067
- isProcessing: tokenShield.isPending || isTokenShieldMining,
8522
+ error: error ?? simulationError,
8523
+ isProcessing: tokenShield.isPending || isTokenShieldMining || tokenApprove.isPending || isApprovingMining,
8068
8524
  inputAmount: shieldAmount,
8069
8525
  setInputAmount: setShieldAmount,
8070
8526
  onMaxClick: handleShieldMax,
8071
- isValidAmount: isValidShieldAmount,
8527
+ canWriteContract: canWrite,
8072
8528
  sourceSymbol: token.extensions.fhenix.erc20Pair?.symbol,
8073
8529
  destSymbol: token.symbol,
8074
8530
  sourceLogoURI: token.extensions.fhenix.erc20Pair?.logoURI,
8075
8531
  destLogoURI: token.logoURI,
8076
- handlePrimaryAction: handleShield,
8077
- primaryLabel: "Shield",
8532
+ handlePrimaryAction: shouldApprove ? handleApprove : handleShield,
8533
+ primaryLabel: shouldApprove ? "Approve" : "Shield",
8078
8534
  primaryIcon: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_tb4.TbShieldPlus, { className: "w-3 h-3" })
8079
8535
  };
8080
8536
  }
@@ -8103,7 +8559,7 @@ function useUnshieldWithLifecycle(token) {
8103
8559
  setUnshieldAmount("");
8104
8560
  },
8105
8561
  onTransactionSubmitError: (error2) => {
8106
- const errorMessage = error2 instanceof Error ? error2.message : "Failed to unshield tokens";
8562
+ const errorMessage = cofheHumanizeViemError(error2) ?? (error2 instanceof Error ? error2.message : "Failed to unshield tokens");
8107
8563
  setError(errorMessage);
8108
8564
  setStatus(null);
8109
8565
  console.error("Unshield tx submit error:", error2);
@@ -8134,10 +8590,10 @@ function useUnshieldWithLifecycle(token) {
8134
8590
  }
8135
8591
  });
8136
8592
  const handleUnshield = async () => {
8137
- const amountInSmallestUnit = (0, import_viem21.parseUnits)(unshieldAmount, token.decimals);
8593
+ const amountWei = (0, import_viem24.parseUnits)(unshieldAmount, token.decimals);
8138
8594
  tokenUnshield.mutateAsync({
8139
8595
  token,
8140
- amount: amountInSmallestUnit,
8596
+ amount: amountWei,
8141
8597
  onStatusChange: (message) => setStatus({ message, type: "info" })
8142
8598
  });
8143
8599
  };
@@ -8146,14 +8602,27 @@ function useUnshieldWithLifecycle(token) {
8146
8602
  if (confidentialBalanceUnit) setUnshieldAmount(confidentialBalanceUnit.toFixed());
8147
8603
  };
8148
8604
  const isValidUnshieldAmount = (unshieldAmount.length > 0 && confidentialBalanceUnit?.gte(unshieldAmount)) ?? false;
8605
+ const unshieldCallArgs = (0, import_react56.useMemo)(() => {
8606
+ if (!account || !isValidUnshieldAmount) return void 0;
8607
+ const amountWei = (0, import_viem24.parseUnits)(unshieldAmount, token.decimals);
8608
+ return getCofheTokenUnshieldCallArgs({ token, amount: amountWei, account });
8609
+ }, [account, isValidUnshieldAmount, token, unshieldAmount]);
8610
+ const unshieldSimulation = useCofheSimulateWriteContract(unshieldCallArgs, {
8611
+ enabled: !!unshieldCallArgs
8612
+ });
8613
+ const simulationError = (0, import_react56.useMemo)(
8614
+ () => humanizeSimulationError(unshieldSimulation.error, "Failed to simulate unshield transaction"),
8615
+ [unshieldSimulation.error]
8616
+ );
8617
+ const canUnshield = isValidUnshieldAmount && !!unshieldCallArgs && !unshieldSimulation.isFetching && !unshieldSimulation.error;
8149
8618
  return {
8150
8619
  status,
8151
- error,
8620
+ error: error ?? simulationError,
8152
8621
  isProcessing: tokenUnshield.isPending || tokenUnshield.isTokenUnshieldMining || tokenUnshield.isPendingDecryption,
8153
8622
  inputAmount: unshieldAmount,
8154
8623
  setInputAmount: setUnshieldAmount,
8155
8624
  onMaxClick: handleUnshieldMax,
8156
- isValidAmount: isValidUnshieldAmount ?? false,
8625
+ canWriteContract: canUnshield,
8157
8626
  sourceSymbol: token.symbol,
8158
8627
  destSymbol: token.extensions.fhenix.erc20Pair?.symbol,
8159
8628
  sourceLogoURI: token.logoURI,
@@ -8206,19 +8675,30 @@ function ClaimingSection({ token }) {
8206
8675
  } = useClaimUnshieldedWithLifecycle();
8207
8676
  const pairedSymbol = token.extensions.fhenix.erc20Pair?.symbol;
8208
8677
  const handleClaim = async () => {
8209
- (0, import_ts_essentials21.assert)(unshieldedClaims, "Unshield claims data is required to claim unshielded tokens");
8678
+ (0, import_ts_essentials23.assert)(unshieldedClaims, "Unshield claims data is required to claim unshielded tokens");
8210
8679
  claimUnshield.mutateAsync({
8211
8680
  token,
8212
8681
  amount: unshieldedClaims.claimableAmount
8213
8682
  });
8214
8683
  };
8215
8684
  const isUnshieldingMining = useIsUnshieldingMining(token);
8685
+ const claimCallArgs = (0, import_react56.useMemo)(() => {
8686
+ if (!account || !unshieldedClaims?.hasClaimable) return void 0;
8687
+ return getCofheTokenClaimUnshieldedCallArgs({ token, account });
8688
+ }, [account, token, unshieldedClaims?.hasClaimable]);
8689
+ const claimSimulation = useCofheSimulateWriteContract(claimCallArgs, {
8690
+ enabled: !!claimCallArgs
8691
+ });
8692
+ const claimSimulationError = (0, import_react56.useMemo)(
8693
+ () => humanizeSimulationError(claimSimulation.error, "Failed to simulate claim transaction"),
8694
+ [claimSimulation.error]
8695
+ );
8216
8696
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
8217
8697
  unshieldedClaims?.hasClaimable && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
8218
8698
  ActionButton,
8219
8699
  {
8220
8700
  onClick: handleClaim,
8221
- disabled: claimUnshield.isPending || isClaimingMining || isFetchingClaims || isWaitingForNewClaimsDecryption || isUnshieldingMining,
8701
+ disabled: claimUnshield.isPending || isClaimingMining || isFetchingClaims || isWaitingForNewClaimsDecryption || isUnshieldingMining || !claimCallArgs || claimSimulation.isFetching || !!claimSimulation.error,
8222
8702
  label: claimUnshield.isPending ? "Claiming..." : `Claim ${isFetchingClaims || isWaitingForNewClaimsDecryption ? "..." : formatTokenAmount(unshieldedClaims.claimableAmount, token.decimals, 5).formatted} ${pairedSymbol}`,
8223
8703
  className: "mt-1"
8224
8704
  }
@@ -8229,7 +8709,7 @@ function ClaimingSection({ token }) {
8229
8709
  " ",
8230
8710
  pairedSymbol
8231
8711
  ] }),
8232
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StatusAndError, { status: claimingStatus, error: claimingError })
8712
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StatusAndError, { status: claimingStatus, error: claimingError ?? claimSimulationError })
8233
8713
  ] });
8234
8714
  }
8235
8715
  function StatusAndError({ status, error }) {
@@ -8267,7 +8747,7 @@ var ShieldAndUnshieldPageView = ({
8267
8747
  inputAmount,
8268
8748
  setInputAmount,
8269
8749
  onMaxClick,
8270
- isValidAmount,
8750
+ canWriteContract,
8271
8751
  sourceSymbol,
8272
8752
  destSymbol,
8273
8753
  sourceLogoURI,
@@ -8384,7 +8864,7 @@ var ShieldAndUnshieldPageView = ({
8384
8864
  ActionButton,
8385
8865
  {
8386
8866
  onClick: handlePrimaryAction,
8387
- disabled: !isValidAmount || isProcessing || !isShieldableToken,
8867
+ disabled: !canWriteContract || isProcessing || !isShieldableToken,
8388
8868
  icon: primaryIcon,
8389
8869
  label: primaryLabel,
8390
8870
  className: "py-2"
@@ -8397,6 +8877,62 @@ var ShieldAndUnshieldPageView = ({
8397
8877
  }
8398
8878
  );
8399
8879
  };
8880
+ function useApproveWithLifecycle({
8881
+ chainId,
8882
+ setError,
8883
+ setStatus,
8884
+ scheduleStatusClear
8885
+ }) {
8886
+ const tokenApprove = useCofheTokenApprove({
8887
+ onMutate: () => {
8888
+ setError(null);
8889
+ setStatus({ message: "Preparing approval transaction...", type: "info" });
8890
+ },
8891
+ onSuccess: (hash, variables) => {
8892
+ setStatus({
8893
+ message: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
8894
+ "Approval transaction sent! Hash: ",
8895
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(TxHashWithActions, { hash, chainId }),
8896
+ " Waiting for confirmation..."
8897
+ ] }),
8898
+ type: "info"
8899
+ });
8900
+ },
8901
+ onError: (error) => {
8902
+ const errorMessage = cofheHumanizeViemError(error) ?? (error instanceof Error ? error.message : "Failed to approve tokens");
8903
+ setError(errorMessage);
8904
+ setStatus(null);
8905
+ console.error("Approve tx submit error:", error);
8906
+ }
8907
+ });
8908
+ const { isMining: isApprovingMining } = useOnceTransactionMined({
8909
+ txHash: tokenApprove.data,
8910
+ onceMined: async (transaction) => {
8911
+ if (transaction.status === "confirmed") {
8912
+ setStatus({
8913
+ message: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
8914
+ "Approval transaction confirmed! Hash: ",
8915
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(TxHashWithActions, { hash: transaction.hash, chainId })
8916
+ ] }),
8917
+ type: "success"
8918
+ });
8919
+ } else if (transaction.status === "failed") {
8920
+ setError(
8921
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
8922
+ "Approval transaction failed! Hash: ",
8923
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(TxHashWithActions, { hash: transaction.hash, chainId })
8924
+ ] })
8925
+ );
8926
+ setStatus(null);
8927
+ }
8928
+ scheduleStatusClear();
8929
+ }
8930
+ });
8931
+ return {
8932
+ tokenApprove,
8933
+ isApprovingMining
8934
+ };
8935
+ }
8400
8936
 
8401
8937
  // src/components/FnxFloatingButton/pages/ActivityPage.tsx
8402
8938
  var import_ArrowBack9 = __toESM(require("@mui/icons-material/ArrowBack"), 1);
@@ -8433,7 +8969,7 @@ var ActivityPage = () => {
8433
8969
  var import_ArrowBack10 = __toESM(require("@mui/icons-material/ArrowBack"), 1);
8434
8970
  var import_react57 = require("react");
8435
8971
  var import_accounts = require("viem/accounts");
8436
- var import_viem22 = require("viem");
8972
+ var import_viem25 = require("viem");
8437
8973
  var import_jsx_runtime51 = require("react/jsx-runtime");
8438
8974
  var DebugPage = () => {
8439
8975
  const { navigateBack } = usePortalNavigation();
@@ -8814,9 +9350,9 @@ var CreateAndUseReceivingPermitButton = () => {
8814
9350
  if (!account) throw new Error("No connected account found");
8815
9351
  if (!publicClient) throw new Error("No public client found");
8816
9352
  const sharingAccount = (0, import_accounts.privateKeyToAccount)("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80");
8817
- const sharingWalletClient = (0, import_viem22.createWalletClient)({
9353
+ const sharingWalletClient = (0, import_viem25.createWalletClient)({
8818
9354
  chain: publicClient.chain,
8819
- transport: (0, import_viem22.http)(),
9355
+ transport: (0, import_viem25.http)(),
8820
9356
  account: sharingAccount
8821
9357
  });
8822
9358
  const sharedPermit = await cofheClient.permits.createSharing(
@@ -9022,7 +9558,7 @@ var PortfolioPage = () => {
9022
9558
  // src/components/FnxFloatingButton/pages/ClaimableTokens.tsx
9023
9559
  var import_ArrowBack13 = __toESM(require("@mui/icons-material/ArrowBack"), 1);
9024
9560
  var import_react58 = require("react");
9025
- var import_ts_essentials22 = __toESM(require_dist(), 1);
9561
+ var import_ts_essentials24 = __toESM(require_dist(), 1);
9026
9562
  var import_jsx_runtime55 = require("react/jsx-runtime");
9027
9563
  function ClaimableTokens() {
9028
9564
  const chainsClaimableTokens = useCofheClaimableTokens();
@@ -9043,7 +9579,7 @@ function ClaimableTokens() {
9043
9579
  }, [allTokens, claimableByTokenAddress]);
9044
9580
  const handleClaim = async (tokenAddress) => {
9045
9581
  const row = rows.find((r) => r.token.address.toLowerCase() === tokenAddress.toLowerCase());
9046
- (0, import_ts_essentials22.assert)(row, "token not found for claim");
9582
+ (0, import_ts_essentials24.assert)(row, "token not found for claim");
9047
9583
  await claimMutation.mutateAsync({ token: row.token, amount: row.claimableAmount });
9048
9584
  };
9049
9585
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
@@ -9603,12 +10139,12 @@ var FnxFloatingButtonWithProvider = (props) => {
9603
10139
  var import_zod = require("zod");
9604
10140
  var import_sdk10 = require("@cofhe/sdk");
9605
10141
  var import_web2 = require("@cofhe/sdk/web");
9606
- var import_viem23 = require("viem");
9607
- var addressSchema = import_zod.z.string().refine((val) => (0, import_viem23.isAddress)(val), {
10142
+ var import_viem26 = require("viem");
10143
+ var addressSchema = import_zod.z.string().refine((val) => (0, import_viem26.isAddress)(val), {
9608
10144
  error: "Invalid address"
9609
- }).refine((val) => val !== import_viem23.zeroAddress, {
10145
+ }).refine((val) => val !== import_viem26.zeroAddress, {
9610
10146
  error: "Must not be zeroAddress"
9611
- }).transform((val) => (0, import_viem23.getAddress)(val));
10147
+ }).transform((val) => (0, import_viem26.getAddress)(val));
9612
10148
  var CofheReactConfigSchema = import_zod.z.object({
9613
10149
  shareablePermits: import_zod.z.boolean().optional().default(false),
9614
10150
  enableShieldUnshield: import_zod.z.boolean().optional().default(true),
@@ -9650,7 +10186,7 @@ function createCofheConfig(config) {
9650
10186
 
9651
10187
  // src/providers/CofheProvider.tsx
9652
10188
  var import_chains5 = require("@cofhe/sdk/chains");
9653
- var import_ts_essentials23 = __toESM(require_dist(), 1);
10189
+ var import_ts_essentials25 = __toESM(require_dist(), 1);
9654
10190
  var import_jsx_runtime66 = require("react/jsx-runtime");
9655
10191
  var CofheContext = (0, import_react64.createContext)(void 0);
9656
10192
  function CofheProvider(props) {
@@ -9658,7 +10194,7 @@ function CofheProvider(props) {
9658
10194
  const config = (0, import_react64.useMemo)(() => {
9659
10195
  if (props.config) return props.config;
9660
10196
  if (props.cofheClient) {
9661
- (0, import_ts_essentials23.assert)(config.environment === "react", "Provided cofheClient must have react config");
10197
+ (0, import_ts_essentials25.assert)(config.environment === "react", "Provided cofheClient must have react config");
9662
10198
  return props.cofheClient.config;
9663
10199
  }
9664
10200
  return createCofheConfig({ supportedChains: Object.values(import_chains5.chains) });
@@ -10035,6 +10571,9 @@ var import_web4 = require("@cofhe/sdk/web");
10035
10571
  getBlockExplorerTokenUrl,
10036
10572
  getBlockExplorerTxUrl,
10037
10573
  getBlockExplorerUrl,
10574
+ getCofheTokenClaimUnshieldedCallArgs,
10575
+ getCofheTokenShieldCallArgs,
10576
+ getCofheTokenUnshieldCallArgs,
10038
10577
  isEthPair,
10039
10578
  isWrappedEthToken,
10040
10579
  statusToString,
@@ -10052,6 +10591,7 @@ var import_web4 = require("@cofhe/sdk/web");
10052
10591
  useCofheReadContractAndDecrypt,
10053
10592
  useCofheRemovePermit,
10054
10593
  useCofheSelectPermit,
10594
+ useCofheSimulateWriteContract,
10055
10595
  useCofheTokenClaimUnshielded,
10056
10596
  useCofheTokenClaimable,
10057
10597
  useCofheTokenDecryptedBalance,
@@ -10067,6 +10607,7 @@ var import_web4 = require("@cofhe/sdk/web");
10067
10607
  useCoingeckoContractMarketChartRange,
10068
10608
  useCoingeckoUsdPrice,
10069
10609
  useInternalQueryClient,
10610
+ useTokenAllowance,
10070
10611
  useTokensWithPublicBalances,
10071
10612
  useTransactionReceiptsByHash,
10072
10613
  useTransactionStore