@trustware/sdk-staging 1.1.12-staging.6 → 1.1.12-staging.8

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/widget.cjs CHANGED
@@ -593,7 +593,7 @@ var init_constants = __esm({
593
593
  "src/constants.ts"() {
594
594
  "use strict";
595
595
  SDK_NAME = "@trustware/sdk";
596
- SDK_VERSION = "1.1.12-staging.6";
596
+ SDK_VERSION = "1.1.12-staging.8";
597
597
  API_ROOT = "https://bv-staging-api.trustware.io";
598
598
  GTM_ID = "GTM-TZDGNCXB";
599
599
  API_PREFIX = "/api";
@@ -822,6 +822,104 @@ var init_chains = __esm({
822
822
  }
823
823
  });
824
824
 
825
+ // src/wallets/chainParams.ts
826
+ function chainParamsFor(chainId) {
827
+ return registered.get(chainId) ?? BUILTIN_CHAIN_PARAMS[chainId];
828
+ }
829
+ function registerChainParams(chainId, params) {
830
+ registered.set(chainId, params);
831
+ }
832
+ function usableRpcUrls(chain) {
833
+ const candidates = [
834
+ ...Array.isArray(chain.rpcList) ? chain.rpcList : [],
835
+ ...chain.rpc ? [chain.rpc] : []
836
+ ];
837
+ const seen = /* @__PURE__ */ new Set();
838
+ const out = [];
839
+ for (const raw of candidates) {
840
+ const url = String(raw ?? "").trim();
841
+ if (!/^https?:\/\//i.test(url) || seen.has(url)) continue;
842
+ seen.add(url);
843
+ out.push(url);
844
+ }
845
+ return out;
846
+ }
847
+ function chainParamsFromChainDef(chain) {
848
+ const chainType = String(
849
+ chain.chainType ?? chain.type ?? "evm"
850
+ ).toLowerCase();
851
+ if (chainType !== "evm") return void 0;
852
+ const numericId = Number(chain.chainId ?? chain.id);
853
+ if (!Number.isSafeInteger(numericId) || numericId <= 0) return void 0;
854
+ const rpcUrls = usableRpcUrls(chain);
855
+ if (rpcUrls.length === 0) return void 0;
856
+ const native = chain.nativeCurrency;
857
+ const symbol = String(native?.symbol ?? "").trim();
858
+ if (!symbol) return void 0;
859
+ const decimals = Number(native?.decimals);
860
+ const chainName = String(
861
+ chain.networkName ?? chain.axelarChainName ?? chain.networkIdentifier ?? ""
862
+ ).trim();
863
+ const explorers = (chain.blockExplorerUrls ?? []).filter(
864
+ (u) => /^https?:\/\//i.test(String(u ?? ""))
865
+ );
866
+ return {
867
+ chainIdHex: `0x${numericId.toString(16)}`,
868
+ chainName: chainName || `Chain ${numericId}`,
869
+ rpcUrls,
870
+ nativeCurrency: {
871
+ name: String(native?.name ?? symbol),
872
+ symbol,
873
+ decimals: Number.isFinite(decimals) ? decimals : 18
874
+ },
875
+ ...explorers.length > 0 ? { blockExplorerUrls: explorers } : {}
876
+ };
877
+ }
878
+ var BUILTIN_CHAIN_PARAMS, registered;
879
+ var init_chainParams = __esm({
880
+ "src/wallets/chainParams.ts"() {
881
+ "use strict";
882
+ BUILTIN_CHAIN_PARAMS = {
883
+ 1: {
884
+ chainIdHex: "0x1",
885
+ chainName: "Ethereum",
886
+ rpcUrls: ["https://eth.llamarpc.com"],
887
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
888
+ blockExplorerUrls: ["https://etherscan.io"]
889
+ },
890
+ 137: {
891
+ chainIdHex: "0x89",
892
+ chainName: "Polygon",
893
+ rpcUrls: ["https://polygon-rpc.com"],
894
+ nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
895
+ blockExplorerUrls: ["https://polygonscan.com"]
896
+ },
897
+ 8453: {
898
+ chainIdHex: "0x2105",
899
+ chainName: "Base",
900
+ rpcUrls: ["https://mainnet.base.org"],
901
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
902
+ blockExplorerUrls: ["https://basescan.org"]
903
+ },
904
+ 42161: {
905
+ chainIdHex: "0xa4b1",
906
+ chainName: "Arbitrum One",
907
+ rpcUrls: ["https://arb1.arbitrum.io/rpc"],
908
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
909
+ blockExplorerUrls: ["https://arbiscan.io"]
910
+ },
911
+ 43114: {
912
+ chainIdHex: "0xa86a",
913
+ chainName: "Avalanche C-Chain",
914
+ rpcUrls: ["https://api.avax.network/ext/bc/C/rpc"],
915
+ nativeCurrency: { name: "Avalanche", symbol: "AVAX", decimals: 18 },
916
+ blockExplorerUrls: ["https://snowtrace.io"]
917
+ }
918
+ };
919
+ registered = /* @__PURE__ */ new Map();
920
+ }
921
+ });
922
+
825
923
  // src/registry.ts
826
924
  function getChainAliases(chain) {
827
925
  const values = [
@@ -866,6 +964,7 @@ var init_registry = __esm({
866
964
  "use strict";
867
965
  init_store();
868
966
  init_chains();
967
+ init_chainParams();
869
968
  NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
870
969
  PAGE_CACHE_TTL_MS = 5 * 60 * 1e3;
871
970
  PAGE_CACHE_LIMIT = 100;
@@ -944,6 +1043,10 @@ var init_registry = __esm({
944
1043
  for (const alias of getChainAliases(normalized)) {
945
1044
  this._chainAliases.set(alias, canonicalKey);
946
1045
  }
1046
+ const addParams = chainParamsFromChainDef(normalized);
1047
+ if (addParams) {
1048
+ registerChainParams(Number(normalized.chainId ?? canonical), addParams);
1049
+ }
947
1050
  }
948
1051
  storeTokenForAlias(chainKey, token) {
949
1052
  const key = tokenKey(token);
@@ -5920,7 +6023,7 @@ var init_bridges = __esm({
5920
6023
 
5921
6024
  // src/wallets/eipWallets.ts
5922
6025
  async function addThenSwitch(eth, chainId) {
5923
- const p = CHAIN_PARAMS[chainId];
6026
+ const p = chainParamsFor(chainId);
5924
6027
  if (!p) throw new Error(`Unknown chain ${chainId} (no params to add)`);
5925
6028
  await eth.request({ method: "wallet_addEthereumChain", params: [p] });
5926
6029
  await eth.request({
@@ -5948,7 +6051,7 @@ function useEIP1193(eth) {
5948
6051
  async switchChain(chainId) {
5949
6052
  if (switching) return;
5950
6053
  switching = true;
5951
- const hex = CHAIN_PARAMS[chainId]?.chainIdHex ?? `0x${chainId.toString(16)}`;
6054
+ const hex = chainParamsFor(chainId)?.chainIdHex ?? `0x${chainId.toString(16)}`;
5952
6055
  try {
5953
6056
  await eth.request({
5954
6057
  method: "wallet_switchEthereumChain",
@@ -5957,7 +6060,6 @@ function useEIP1193(eth) {
5957
6060
  } catch (e2) {
5958
6061
  if (e2?.code === 4902) {
5959
6062
  await addThenSwitch(eth, chainId);
5960
- } else if (e2?.code === 4001) {
5961
6063
  } else {
5962
6064
  throw e2;
5963
6065
  }
@@ -5968,47 +6070,10 @@ function useEIP1193(eth) {
5968
6070
  request: (args) => eth.request(args)
5969
6071
  };
5970
6072
  }
5971
- var CHAIN_PARAMS;
5972
6073
  var init_eipWallets = __esm({
5973
6074
  "src/wallets/eipWallets.ts"() {
5974
6075
  "use strict";
5975
- CHAIN_PARAMS = {
5976
- 1: {
5977
- chainIdHex: "0x1",
5978
- chainName: "Ethereum",
5979
- rpcUrls: ["https://eth.llamarpc.com"],
5980
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
5981
- blockExplorerUrls: ["https://etherscan.io"]
5982
- },
5983
- 137: {
5984
- chainIdHex: "0x89",
5985
- chainName: "Polygon",
5986
- rpcUrls: ["https://polygon-rpc.com"],
5987
- nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
5988
- blockExplorerUrls: ["https://polygonscan.com"]
5989
- },
5990
- 8453: {
5991
- chainIdHex: "0x2105",
5992
- chainName: "Base",
5993
- rpcUrls: ["https://mainnet.base.org"],
5994
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
5995
- blockExplorerUrls: ["https://basescan.org"]
5996
- },
5997
- 42161: {
5998
- chainIdHex: "0xa4b1",
5999
- chainName: "Arbitrum One",
6000
- rpcUrls: ["https://arb1.arbitrum.io/rpc"],
6001
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
6002
- blockExplorerUrls: ["https://arbiscan.io"]
6003
- },
6004
- 43114: {
6005
- chainIdHex: "0xa86a",
6006
- chainName: "Avalanche C-Chain",
6007
- rpcUrls: ["https://api.avax.network/ext/bc/C/rpc"],
6008
- nativeCurrency: { name: "Avalanche", symbol: "AVAX", decimals: 18 },
6009
- blockExplorerUrls: ["https://snowtrace.io"]
6010
- }
6011
- };
6076
+ init_chainParams();
6012
6077
  }
6013
6078
  });
6014
6079
 
@@ -8871,12 +8936,32 @@ function mapWalletTokens(balances, chains, tokens) {
8871
8936
  if (!foundToken && balanceRow.category === "native") {
8872
8937
  const nativeAddress = canonicalTokenAddressForChain(
8873
8938
  chain,
8874
- getNativeTokenAddress2(chain.type),
8939
+ getNativeTokenAddress2(normalizeChainType2(chain)),
8875
8940
  tokens
8876
8941
  );
8877
8942
  const nativeKey = `${normalizeChainKey2(balanceRow.chain_id)}:${nativeAddress}`;
8878
8943
  foundToken = tokensByCanonicalKey.get(nativeKey);
8879
8944
  }
8945
+ if (!foundToken && balanceRow.category === "native") {
8946
+ const native = chain.nativeCurrency;
8947
+ const symbol = native?.symbol?.trim();
8948
+ if (symbol) {
8949
+ return [
8950
+ {
8951
+ ...balanceRow,
8952
+ symbol,
8953
+ decimals: native?.decimals ?? balanceRow.decimals ?? 18,
8954
+ name: native?.name?.trim() || symbol,
8955
+ iconUrl: chain.chainIconURI || "",
8956
+ chainId: balanceRow.chain_id,
8957
+ usdPrice: void 0,
8958
+ address: getNativeTokenAddress2(normalizeChainType2(chain)),
8959
+ chainIconURI: chain.chainIconURI || "",
8960
+ chainData: chain
8961
+ }
8962
+ ];
8963
+ }
8964
+ }
8880
8965
  if (!foundToken?.name || !foundToken.symbol || !foundToken.address) {
8881
8966
  return [];
8882
8967
  }
@@ -9224,6 +9309,25 @@ function backendChainId(chain, fallback) {
9224
9309
  function sleep2(ms) {
9225
9310
  return new Promise((resolve) => setTimeout(resolve, ms));
9226
9311
  }
9312
+ async function ensureWalletOnChain(wallet, target) {
9313
+ if (!Number.isSafeInteger(target) || target <= 0) {
9314
+ throw new Error(`Invalid chain id: ${target}`);
9315
+ }
9316
+ if (await wallet.getChainId() === target) return;
9317
+ let switchError;
9318
+ try {
9319
+ await wallet.switchChain(target);
9320
+ } catch (e2) {
9321
+ switchError = e2;
9322
+ }
9323
+ if (await wallet.getChainId() === target) return;
9324
+ if (switchError && isUserRejected(switchError)) {
9325
+ throw switchError;
9326
+ }
9327
+ throw new Error(
9328
+ `Wallet is on the wrong network. Switch to chain ${target} and try again.`
9329
+ );
9330
+ }
9227
9331
  async function sendEvmTx(w, tx) {
9228
9332
  if (w.type === "eip1193") {
9229
9333
  const from = await w.getAddress();
@@ -9250,19 +9354,37 @@ async function sendEvmTx(w, tx) {
9250
9354
  });
9251
9355
  return response.hash;
9252
9356
  }
9253
- async function waitForTxConfirmation(chainId, txHash) {
9254
- const timeoutMs = 12e4;
9255
- const intervalMs = 2e3;
9357
+ async function waitForEvmTxConfirmation(chainId, txHash, label, options = {}) {
9358
+ const {
9359
+ readStatus = getEVMTxStatus,
9360
+ timeoutMs = 12e4,
9361
+ intervalMs = 2e3,
9362
+ notFoundGraceMs = NOT_FOUND_GRACE_MS
9363
+ } = options;
9256
9364
  const started = Date.now();
9365
+ let notFoundSince = null;
9257
9366
  while (Date.now() - started < timeoutMs) {
9258
- const status = await getEVMTxStatus({ chainId, txHash });
9367
+ const status = await readStatus({ chainId, txHash });
9259
9368
  if (status.status === "success") return;
9260
9369
  if (status.status === "reverted") {
9261
- throw new Error("Approval transaction reverted");
9370
+ throw new Error(`${label} transaction reverted`);
9371
+ }
9372
+ if (status.status === "not_found") {
9373
+ notFoundSince ?? (notFoundSince = Date.now());
9374
+ if (Date.now() - notFoundSince >= notFoundGraceMs) {
9375
+ throw new Error(
9376
+ `${label} transaction ${txHash} was not found on chain ${chainId}. It may have been sent on a different network \u2014 check your wallet's selected network and try again.`
9377
+ );
9378
+ }
9379
+ } else {
9380
+ notFoundSince = null;
9262
9381
  }
9263
9382
  await sleep2(intervalMs);
9264
9383
  }
9265
- throw new Error("Timed out waiting for approval confirmation");
9384
+ throw new Error(`Timed out waiting for ${label.toLowerCase()} confirmation`);
9385
+ }
9386
+ function waitForApprovalConfirmation(chainId, txHash, options) {
9387
+ return waitForEvmTxConfirmation(chainId, txHash, "Approval", options);
9266
9388
  }
9267
9389
  function requiredApprovalAmount(planAmountWei, routeAmountWei) {
9268
9390
  if (planAmountWei >= UNLIMITED_APPROVAL_FLOOR && routeAmountWei !== void 0 && routeAmountWei > 0n) {
@@ -9302,17 +9424,22 @@ async function ensureApprovals(w, approvals, fallbackChainId, intentId, routeAmo
9302
9424
  functionName: "approve",
9303
9425
  args: [spender, amountWei]
9304
9426
  });
9427
+ const approvalChainId = Number(chainId);
9428
+ if (!Number.isSafeInteger(approvalChainId) || approvalChainId <= 0) {
9429
+ throw new Error(`Approval has an invalid chain id: ${chainId}`);
9430
+ }
9431
+ await ensureWalletOnChain(w, approvalChainId);
9305
9432
  const hash = await sendEvmTx(w, {
9306
9433
  to: tokenAddress,
9307
9434
  data,
9308
9435
  value: 0n,
9309
- chainId: Number.isFinite(Number(chainId)) ? Number(chainId) : void 0
9436
+ chainId: approvalChainId
9310
9437
  });
9311
9438
  if (intentId) {
9312
9439
  void submitStepReceipt(intentId, stepIndex, hash).catch(() => {
9313
9440
  });
9314
9441
  }
9315
- await waitForTxConfirmation(chainId, hash);
9442
+ await waitForApprovalConfirmation(chainId, hash);
9316
9443
  }
9317
9444
  }
9318
9445
  function isUserRejected(e2) {
@@ -9339,15 +9466,14 @@ async function sendRouteTransaction(b, fallbackChainId, options) {
9339
9466
  validatedSponsorship = b.sponsorship;
9340
9467
  }
9341
9468
  }
9342
- if (Number.isFinite(target)) {
9343
- const current = await w.getChainId();
9344
- if (current !== target) {
9345
- try {
9346
- await w.switchChain(target);
9347
- } catch {
9348
- }
9349
- }
9469
+ if (!Number.isSafeInteger(target) || target <= 0) {
9470
+ throw new Error(
9471
+ `Route is missing a usable chain id (got ${String(
9472
+ txReq.chainId ?? fallbackChainId
9473
+ )}).`
9474
+ );
9350
9475
  }
9476
+ await ensureWalletOnChain(w, target);
9351
9477
  if (!validatedSponsorship && !options?.approvalsEnsured) {
9352
9478
  await ensureApprovals(
9353
9479
  w,
@@ -9453,7 +9579,7 @@ async function runTopUp(params) {
9453
9579
  }
9454
9580
  }
9455
9581
  }
9456
- var import_viem2, erc20ApproveAbi, UNLIMITED_APPROVAL_FLOOR;
9582
+ var import_viem2, erc20ApproveAbi, NOT_FOUND_GRACE_MS, UNLIMITED_APPROVAL_FLOOR;
9457
9583
  var init_tx = __esm({
9458
9584
  "src/core/tx.ts"() {
9459
9585
  "use strict";
@@ -9464,6 +9590,7 @@ var init_tx = __esm({
9464
9590
  erc20ApproveAbi = (0, import_viem2.parseAbi)([
9465
9591
  "function approve(address spender, uint256 amount) returns (bool)"
9466
9592
  ]);
9593
+ NOT_FOUND_GRACE_MS = 2e4;
9467
9594
  UNLIMITED_APPROVAL_FLOOR = 1n << 255n;
9468
9595
  }
9469
9596
  });
@@ -33611,6 +33738,35 @@ var init_useSwapRoute = __esm({
33611
33738
  }
33612
33739
  });
33613
33740
 
33741
+ // src/modes/swap/routeValue.ts
33742
+ function parseUsd(value) {
33743
+ if (value == null) return null;
33744
+ const n = parseFloat(value);
33745
+ return Number.isFinite(n) ? n : null;
33746
+ }
33747
+ function routeNetUsd(estimate) {
33748
+ if (!estimate) return null;
33749
+ const toUsd = parseUsd(estimate.toAmountUsd);
33750
+ const feesUsd = parseUsd(estimate.totalFeesUsd);
33751
+ if (toUsd == null || feesUsd == null) return null;
33752
+ return toUsd - feesUsd;
33753
+ }
33754
+ function isValueDestroying(estimate) {
33755
+ const net = routeNetUsd(estimate);
33756
+ if (net == null) return false;
33757
+ const toUsd = parseUsd(estimate?.toAmountUsd) ?? 0;
33758
+ const feesUsd = parseUsd(estimate?.totalFeesUsd) ?? 0;
33759
+ if (toUsd < USD_NOISE_FLOOR && feesUsd < USD_NOISE_FLOOR) return false;
33760
+ return net < 0;
33761
+ }
33762
+ var USD_NOISE_FLOOR;
33763
+ var init_routeValue = __esm({
33764
+ "src/modes/swap/routeValue.ts"() {
33765
+ "use strict";
33766
+ USD_NOISE_FLOOR = 5e-3;
33767
+ }
33768
+ });
33769
+
33614
33770
  // src/modes/swap/hooks/useSwapExecution.ts
33615
33771
  function normalizeTx(raw) {
33616
33772
  const r = raw;
@@ -33622,9 +33778,6 @@ function normalizeTx(raw) {
33622
33778
  toChainTxUrl: r.toChainTxUrl || r.to_chain_tx_url || void 0
33623
33779
  };
33624
33780
  }
33625
- function sleep4(ms) {
33626
- return new Promise((resolve) => setTimeout(resolve, ms));
33627
- }
33628
33781
  function isUserRejection(err) {
33629
33782
  if (!err) return false;
33630
33783
  const e2 = err;
@@ -33633,17 +33786,6 @@ function isUserRejection(err) {
33633
33786
  const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
33634
33787
  return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("cancelled");
33635
33788
  }
33636
- async function waitForApprovalConfirmation(chainId, txHash) {
33637
- const started = Date.now();
33638
- while (Date.now() - started < 12e4) {
33639
- const status = await getEVMTxStatus({ chainId, txHash });
33640
- if (status.status === "success") return;
33641
- if (status.status === "reverted")
33642
- throw new Error("Approval transaction reverted");
33643
- await sleep4(2e3);
33644
- }
33645
- throw new Error("Timed out waiting for approval confirmation");
33646
- }
33647
33789
  async function requireActiveAddress(wallet, plannedFor) {
33648
33790
  if (!wallet) throw new Error("Wallet not connected. Please reconnect.");
33649
33791
  const active = await wallet.getAddress();
@@ -33807,6 +33949,12 @@ function useSwapExecution(fromChain) {
33807
33949
  onError(msg);
33808
33950
  return;
33809
33951
  }
33952
+ if (isValueDestroying(routeResult.route?.estimate)) {
33953
+ const msg = "This route now costs more in fees than it delivers. Get a new quote.";
33954
+ setState((p) => ({ ...p, txStatus: "error", errorMessage: msg }));
33955
+ onError(msg);
33956
+ return;
33957
+ }
33810
33958
  setState((p) => ({
33811
33959
  ...p,
33812
33960
  isSubmitting: true,
@@ -33948,15 +34096,10 @@ function useSwapExecution(fromChain) {
33948
34096
  throw new Error("Invalid chain ID for token approval");
33949
34097
  }
33950
34098
  const targetChain = Number(required.chainId ?? txReq.chainId);
33951
- if (Number.isFinite(targetChain)) {
33952
- const current = await wallet.getChainId();
33953
- if (current !== targetChain) {
33954
- try {
33955
- await wallet.switchChain(targetChain);
33956
- } catch {
33957
- }
33958
- }
34099
+ if (!Number.isFinite(targetChain)) {
34100
+ throw new Error("Invalid chain ID for token approval");
33959
34101
  }
34102
+ await ensureWalletOnChain(wallet, targetChain);
33960
34103
  const approveAmount = maxApproval ? BigInt(
33961
34104
  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
33962
34105
  ) : required.amountWei;
@@ -34084,6 +34227,7 @@ var init_useSwapExecution = __esm({
34084
34227
  init_failure();
34085
34228
  init_tx();
34086
34229
  init_sdkRpc();
34230
+ init_routeValue();
34087
34231
  init_chainHelpers();
34088
34232
  FAST_POLL_MS = 1500;
34089
34233
  SLOW_POLL_MS = 2500;
@@ -34092,6 +34236,33 @@ var init_useSwapExecution = __esm({
34092
34236
  }
34093
34237
  });
34094
34238
 
34239
+ // src/modes/swap/walletBalance.ts
34240
+ function compareKey(address, chainType) {
34241
+ if (chainType) return normalizeAddress2(address, chainType);
34242
+ return HEX_ADDRESS.test(address.trim()) ? address.toLowerCase() : address.trim();
34243
+ }
34244
+ function findWalletBalanceRow(rows, token, chainId, chainType) {
34245
+ const rawAddress = token?.address;
34246
+ if (!rawAddress || !rows?.length) return void 0;
34247
+ const wantAddress = compareKey(rawAddress, chainType);
34248
+ if (!wantAddress) return void 0;
34249
+ const wantChain = chainId == null ? "" : String(chainId).trim().toLowerCase();
34250
+ return rows.find((row) => {
34251
+ if (!row?.address) return false;
34252
+ if (compareKey(row.address, chainType) !== wantAddress) return false;
34253
+ if (!wantChain) return true;
34254
+ return String(row.chainId ?? "").trim().toLowerCase() === wantChain;
34255
+ });
34256
+ }
34257
+ var HEX_ADDRESS;
34258
+ var init_walletBalance = __esm({
34259
+ "src/modes/swap/walletBalance.ts"() {
34260
+ "use strict";
34261
+ init_chainHelpers();
34262
+ HEX_ADDRESS = /^0x[0-9a-f]+$/i;
34263
+ }
34264
+ });
34265
+
34095
34266
  // src/core/forex.ts
34096
34267
  async function fetchForexRates(base2 = "USD") {
34097
34268
  let r;
@@ -36244,14 +36415,19 @@ function SwapMode({
36244
36415
  route
36245
36416
  ]);
36246
36417
  const fromBalance = (0, import_react50.useMemo)(() => {
36247
- const walletToken = fromToken;
36248
- if (!walletToken || !("balance" in walletToken)) return null;
36249
- const raw = walletToken.balance;
36418
+ if (!fromToken) return null;
36419
+ const row = findWalletBalanceRow(
36420
+ yourWalletTokens,
36421
+ fromToken,
36422
+ fromChain?.chainId,
36423
+ normalizeChainType2(fromChain)
36424
+ );
36425
+ const raw = row?.balance ?? fromToken.balance;
36250
36426
  if (!raw) return null;
36251
- const decimals = fromToken?.decimals ?? 18;
36427
+ const decimals = row?.decimals ?? fromToken?.decimals ?? 18;
36252
36428
  const n = Number(rawToDecimal(raw, decimals));
36253
36429
  return Number.isFinite(n) ? n : null;
36254
- }, [fromToken]);
36430
+ }, [fromToken, fromChain, yourWalletTokens]);
36255
36431
  const balanceUsd = fromBalance !== null && hasFromUsdPrice ? fromBalance * fromTokenPriceUSD : null;
36256
36432
  const estimatedToAmount = (0, import_react50.useMemo)(() => {
36257
36433
  if (tokenSellNum <= 0 || !hasFromUsdPrice || !hasToUsdPrice) return null;
@@ -38020,8 +38196,9 @@ function SwapMode({
38020
38196
  }
38021
38197
  ) });
38022
38198
  }
38023
- const ctaLabel = !hasTokens ? "Get started" : !hasAmount ? "Enter an amount" : insufficient ? `Insufficient ${fromToken?.symbol} balance` : !isConnected ? "Connect Wallet" : needsDestAddress && !isValidDestAddress ? `Enter ${toChain?.networkName ?? "destination"} address` : route.loading ? "Getting quote..." : "Review";
38024
- const ctaDisabled = !hasTokens || !hasAmount || insufficient || isConnected && needsDestAddress && !isValidDestAddress || route.loading;
38199
+ const feesExceedOutput = isValueDestroying(route.data?.route?.estimate);
38200
+ const ctaLabel = !hasTokens ? "Get started" : !hasAmount ? "Enter an amount" : insufficient ? `Insufficient ${fromToken?.symbol} balance` : !isConnected ? "Connect Wallet" : needsDestAddress && !isValidDestAddress ? `Enter ${toChain?.networkName ?? "destination"} address` : route.loading ? "Getting quote..." : feesExceedOutput ? "Fees exceed amount received" : "Review";
38201
+ const ctaDisabled = !hasTokens || !hasAmount || insufficient || isConnected && needsDestAddress && !isValidDestAddress || route.loading || feesExceedOutput;
38025
38202
  const ctaAction = !isConnected ? handleConnectAndReview : () => void handleReview();
38026
38203
  const showRateRow = hasTokens && exchangeRate !== null;
38027
38204
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(WidgetContainer, { theme: resolvedTheme, style, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
@@ -39123,35 +39300,51 @@ function SwapMode({
39123
39300
  ]
39124
39301
  }
39125
39302
  ),
39126
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("div", { style: { padding: `${spacing[2]} ${spacing[3]} 0` }, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
39127
- "button",
39128
- {
39129
- disabled: ctaDisabled,
39130
- onClick: ctaAction,
39131
- style: mergeStyles(
39132
- {
39133
- width: "100%",
39134
- height: "3.5rem",
39135
- borderRadius: "1.5rem",
39136
- fontSize: fontSize.base,
39137
- fontWeight: fontWeight.semibold,
39138
- border: 0,
39139
- transition: "all 0.2s",
39140
- marginBottom: spacing[3]
39303
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)("div", { style: { padding: `${spacing[2]} ${spacing[3]} 0` }, children: [
39304
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
39305
+ "button",
39306
+ {
39307
+ disabled: ctaDisabled,
39308
+ onClick: ctaAction,
39309
+ style: mergeStyles(
39310
+ {
39311
+ width: "100%",
39312
+ height: "3.5rem",
39313
+ borderRadius: "1.5rem",
39314
+ fontSize: fontSize.base,
39315
+ fontWeight: fontWeight.semibold,
39316
+ border: 0,
39317
+ transition: "all 0.2s",
39318
+ marginBottom: spacing[3]
39319
+ },
39320
+ !ctaDisabled ? {
39321
+ backgroundColor: colors.primary,
39322
+ color: colors.primaryForeground,
39323
+ cursor: "pointer"
39324
+ } : {
39325
+ backgroundColor: "hsl(var(--tw-muted))",
39326
+ color: colors.mutedForeground,
39327
+ cursor: "default"
39328
+ }
39329
+ ),
39330
+ children: ctaLabel
39331
+ }
39332
+ ),
39333
+ feesExceedOutput && /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
39334
+ "div",
39335
+ {
39336
+ style: {
39337
+ marginTop: `-${spacing[2]}`,
39338
+ marginBottom: spacing[3],
39339
+ fontSize: fontSize.xs,
39340
+ lineHeight: 1.4,
39341
+ color: "#f87171",
39342
+ textAlign: "center"
39141
39343
  },
39142
- !ctaDisabled ? {
39143
- backgroundColor: colors.primary,
39144
- color: colors.primaryForeground,
39145
- cursor: "pointer"
39146
- } : {
39147
- backgroundColor: "hsl(var(--tw-muted))",
39148
- color: colors.mutedForeground,
39149
- cursor: "default"
39150
- }
39151
- ),
39152
- children: ctaLabel
39153
- }
39154
- ) }),
39344
+ children: "This route costs more in fees than it delivers. Try a larger amount, or a different token or network."
39345
+ }
39346
+ )
39347
+ ] }),
39155
39348
  showRateRow && /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
39156
39349
  "div",
39157
39350
  {
@@ -39998,6 +40191,8 @@ var init_SwapMode = __esm({
39998
40191
  init_provider();
39999
40192
  init_useSwapRoute();
40000
40193
  init_useSwapExecution();
40194
+ init_routeValue();
40195
+ init_walletBalance();
40001
40196
  init_useForex();
40002
40197
  init_SwapTokenSelect();
40003
40198
  init_currency();