@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/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as Trustware } from './core-Ck7dinYj.cjs';
2
- export { a as TrustwareCore, v as validateAddressForChain, b as validateBtcAddress, c as validateCosmosAddress, d as validateEvmAddress, e as validateRouteAddresses, f as validateSeiAddress, g as validateSolanaAddress } from './core-Ck7dinYj.cjs';
1
+ import { T as Trustware } from './core-CHXz6NrH.cjs';
2
+ export { a as TrustwareCore, v as validateAddressForChain, b as validateBtcAddress, c as validateCosmosAddress, d as validateEvmAddress, e as validateRouteAddresses, f as validateSeiAddress, g as validateSolanaAddress } from './core-CHXz6NrH.cjs';
3
3
  import { R as RateLimitInfo, T as TrustwareConfigOptions, i as WalletInterFaceAPI, j as TrustwareError, k as Transaction, l as TrustwareEvent } from './manager-BZ8gyHGT.cjs';
4
4
  export { B as BuildRouteBody, m as BuildRouteResponse, n as BuildRouteResult, o as DEFAULT_AUTO_DETECT_PROVIDER, p as DEFAULT_FEATURE_FLAGS, q as DEFAULT_MESSAGES, r as DEFAULT_RETRY_CONFIG, s as DEFAULT_SLIPPAGE, D as DetectedWallet, E as EIP1193, t as EIP6963ProviderDetail, v as EvmWalletInterface, F as FeatureFlags, I as IdentityStore, P as PostHookRequest, x as RATE_LIMIT_WAIT_BUDGET_MS, y as ResolvedFeatureFlags, z as ResolvedRetryConfig, A as ResolvedTrustwareConfig, C as ResolvedWalletConnectConfig, G as RetryConfig, H as RouteApproval, J as RouteEstimate, K as RouteIntent, L as RouteParams, M as RoutePlan, N as RouteSponsorship, O as RoutesConfig, Q as SimpleWalletInterface, S as SolanaProviderLike, b as SolanaWalletInterface, U as SponsorshipApproval, V as SwapTokenRef, X as TrustwareTheme, Y as TrustwareWidgetMessages, c as WagmiBridge, d as WagmiConnector, Z as WalletAddressResolution, _ as WalletAddressSource, $ as WalletCategory, a0 as WalletConnectConfig, a1 as WalletEcosystem, a as WalletId, a2 as WalletIdentity, a3 as WalletIdentityAddress, a4 as WalletIdentityChainLike, W as WalletMeta, a5 as assertValidPostHook, a6 as buildWalletIdentityAddress, a7 as createWalletIdentity, a8 as resolveWalletAddressForChain, a9 as upsertWalletIdentityAddress, f as useWalletExternalDisconnect, g as useWalletInfo, h as useWireDetectionIntoManager, w as walletManager } from './manager-BZ8gyHGT.cjs';
5
5
  import * as React from 'react';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as Trustware } from './core-B4YDGtJT.js';
2
- export { a as TrustwareCore, v as validateAddressForChain, b as validateBtcAddress, c as validateCosmosAddress, d as validateEvmAddress, e as validateRouteAddresses, f as validateSeiAddress, g as validateSolanaAddress } from './core-B4YDGtJT.js';
1
+ import { T as Trustware } from './core-_eoyfy2s.js';
2
+ export { a as TrustwareCore, v as validateAddressForChain, b as validateBtcAddress, c as validateCosmosAddress, d as validateEvmAddress, e as validateRouteAddresses, f as validateSeiAddress, g as validateSolanaAddress } from './core-_eoyfy2s.js';
3
3
  import { R as RateLimitInfo, T as TrustwareConfigOptions, i as WalletInterFaceAPI, j as TrustwareError, k as Transaction, l as TrustwareEvent } from './manager-D8nQsod2.js';
4
4
  export { B as BuildRouteBody, m as BuildRouteResponse, n as BuildRouteResult, o as DEFAULT_AUTO_DETECT_PROVIDER, p as DEFAULT_FEATURE_FLAGS, q as DEFAULT_MESSAGES, r as DEFAULT_RETRY_CONFIG, s as DEFAULT_SLIPPAGE, D as DetectedWallet, E as EIP1193, t as EIP6963ProviderDetail, v as EvmWalletInterface, F as FeatureFlags, I as IdentityStore, P as PostHookRequest, x as RATE_LIMIT_WAIT_BUDGET_MS, y as ResolvedFeatureFlags, z as ResolvedRetryConfig, A as ResolvedTrustwareConfig, C as ResolvedWalletConnectConfig, G as RetryConfig, H as RouteApproval, J as RouteEstimate, K as RouteIntent, L as RouteParams, M as RoutePlan, N as RouteSponsorship, O as RoutesConfig, Q as SimpleWalletInterface, S as SolanaProviderLike, b as SolanaWalletInterface, U as SponsorshipApproval, V as SwapTokenRef, X as TrustwareTheme, Y as TrustwareWidgetMessages, c as WagmiBridge, d as WagmiConnector, Z as WalletAddressResolution, _ as WalletAddressSource, $ as WalletCategory, a0 as WalletConnectConfig, a1 as WalletEcosystem, a as WalletId, a2 as WalletIdentity, a3 as WalletIdentityAddress, a4 as WalletIdentityChainLike, W as WalletMeta, a5 as assertValidPostHook, a6 as buildWalletIdentityAddress, a7 as createWalletIdentity, a8 as resolveWalletAddressForChain, a9 as upsertWalletIdentityAddress, f as useWalletExternalDisconnect, g as useWalletInfo, h as useWireDetectionIntoManager, w as walletManager } from './manager-D8nQsod2.js';
5
5
  import * as React from 'react';
package/dist/index.mjs CHANGED
@@ -312,9 +312,107 @@ var init_bridges = __esm({
312
312
  }
313
313
  });
314
314
 
315
+ // src/wallets/chainParams.ts
316
+ function chainParamsFor(chainId) {
317
+ return registered.get(chainId) ?? BUILTIN_CHAIN_PARAMS[chainId];
318
+ }
319
+ function registerChainParams(chainId, params) {
320
+ registered.set(chainId, params);
321
+ }
322
+ function usableRpcUrls(chain) {
323
+ const candidates = [
324
+ ...Array.isArray(chain.rpcList) ? chain.rpcList : [],
325
+ ...chain.rpc ? [chain.rpc] : []
326
+ ];
327
+ const seen = /* @__PURE__ */ new Set();
328
+ const out = [];
329
+ for (const raw of candidates) {
330
+ const url = String(raw ?? "").trim();
331
+ if (!/^https?:\/\//i.test(url) || seen.has(url)) continue;
332
+ seen.add(url);
333
+ out.push(url);
334
+ }
335
+ return out;
336
+ }
337
+ function chainParamsFromChainDef(chain) {
338
+ const chainType = String(
339
+ chain.chainType ?? chain.type ?? "evm"
340
+ ).toLowerCase();
341
+ if (chainType !== "evm") return void 0;
342
+ const numericId = Number(chain.chainId ?? chain.id);
343
+ if (!Number.isSafeInteger(numericId) || numericId <= 0) return void 0;
344
+ const rpcUrls = usableRpcUrls(chain);
345
+ if (rpcUrls.length === 0) return void 0;
346
+ const native = chain.nativeCurrency;
347
+ const symbol = String(native?.symbol ?? "").trim();
348
+ if (!symbol) return void 0;
349
+ const decimals = Number(native?.decimals);
350
+ const chainName = String(
351
+ chain.networkName ?? chain.axelarChainName ?? chain.networkIdentifier ?? ""
352
+ ).trim();
353
+ const explorers = (chain.blockExplorerUrls ?? []).filter(
354
+ (u) => /^https?:\/\//i.test(String(u ?? ""))
355
+ );
356
+ return {
357
+ chainIdHex: `0x${numericId.toString(16)}`,
358
+ chainName: chainName || `Chain ${numericId}`,
359
+ rpcUrls,
360
+ nativeCurrency: {
361
+ name: String(native?.name ?? symbol),
362
+ symbol,
363
+ decimals: Number.isFinite(decimals) ? decimals : 18
364
+ },
365
+ ...explorers.length > 0 ? { blockExplorerUrls: explorers } : {}
366
+ };
367
+ }
368
+ var BUILTIN_CHAIN_PARAMS, registered;
369
+ var init_chainParams = __esm({
370
+ "src/wallets/chainParams.ts"() {
371
+ "use strict";
372
+ BUILTIN_CHAIN_PARAMS = {
373
+ 1: {
374
+ chainIdHex: "0x1",
375
+ chainName: "Ethereum",
376
+ rpcUrls: ["https://eth.llamarpc.com"],
377
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
378
+ blockExplorerUrls: ["https://etherscan.io"]
379
+ },
380
+ 137: {
381
+ chainIdHex: "0x89",
382
+ chainName: "Polygon",
383
+ rpcUrls: ["https://polygon-rpc.com"],
384
+ nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
385
+ blockExplorerUrls: ["https://polygonscan.com"]
386
+ },
387
+ 8453: {
388
+ chainIdHex: "0x2105",
389
+ chainName: "Base",
390
+ rpcUrls: ["https://mainnet.base.org"],
391
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
392
+ blockExplorerUrls: ["https://basescan.org"]
393
+ },
394
+ 42161: {
395
+ chainIdHex: "0xa4b1",
396
+ chainName: "Arbitrum One",
397
+ rpcUrls: ["https://arb1.arbitrum.io/rpc"],
398
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
399
+ blockExplorerUrls: ["https://arbiscan.io"]
400
+ },
401
+ 43114: {
402
+ chainIdHex: "0xa86a",
403
+ chainName: "Avalanche C-Chain",
404
+ rpcUrls: ["https://api.avax.network/ext/bc/C/rpc"],
405
+ nativeCurrency: { name: "Avalanche", symbol: "AVAX", decimals: 18 },
406
+ blockExplorerUrls: ["https://snowtrace.io"]
407
+ }
408
+ };
409
+ registered = /* @__PURE__ */ new Map();
410
+ }
411
+ });
412
+
315
413
  // src/wallets/eipWallets.ts
316
414
  async function addThenSwitch(eth, chainId) {
317
- const p = CHAIN_PARAMS[chainId];
415
+ const p = chainParamsFor(chainId);
318
416
  if (!p) throw new Error(`Unknown chain ${chainId} (no params to add)`);
319
417
  await eth.request({ method: "wallet_addEthereumChain", params: [p] });
320
418
  await eth.request({
@@ -342,7 +440,7 @@ function useEIP1193(eth) {
342
440
  async switchChain(chainId) {
343
441
  if (switching) return;
344
442
  switching = true;
345
- const hex = CHAIN_PARAMS[chainId]?.chainIdHex ?? `0x${chainId.toString(16)}`;
443
+ const hex = chainParamsFor(chainId)?.chainIdHex ?? `0x${chainId.toString(16)}`;
346
444
  try {
347
445
  await eth.request({
348
446
  method: "wallet_switchEthereumChain",
@@ -351,7 +449,6 @@ function useEIP1193(eth) {
351
449
  } catch (e2) {
352
450
  if (e2?.code === 4902) {
353
451
  await addThenSwitch(eth, chainId);
354
- } else if (e2?.code === 4001) {
355
452
  } else {
356
453
  throw e2;
357
454
  }
@@ -393,15 +490,13 @@ function useWagmi(client) {
393
490
  const eth = globalThis.ethereum;
394
491
  if (!eth?.request) throw new Error("switchChain not available");
395
492
  try {
396
- const hex = CHAIN_PARAMS[target]?.chainIdHex ?? `0x${target.toString(16)}`;
493
+ const hex = chainParamsFor(target)?.chainIdHex ?? `0x${target.toString(16)}`;
397
494
  await eth.request({
398
495
  method: "wallet_switchEthereumChain",
399
496
  params: [{ chainId: hex }]
400
497
  });
401
498
  } catch (e2) {
402
499
  if (e2?.code === 4902) await addThenSwitch(eth, target);
403
- else if (e2?.code === 4001)
404
- ;
405
500
  else throw e2;
406
501
  }
407
502
  } finally {
@@ -442,47 +537,10 @@ function useWagmi(client) {
442
537
  sendTransaction: sendTransaction2
443
538
  };
444
539
  }
445
- var CHAIN_PARAMS;
446
540
  var init_eipWallets = __esm({
447
541
  "src/wallets/eipWallets.ts"() {
448
542
  "use strict";
449
- CHAIN_PARAMS = {
450
- 1: {
451
- chainIdHex: "0x1",
452
- chainName: "Ethereum",
453
- rpcUrls: ["https://eth.llamarpc.com"],
454
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
455
- blockExplorerUrls: ["https://etherscan.io"]
456
- },
457
- 137: {
458
- chainIdHex: "0x89",
459
- chainName: "Polygon",
460
- rpcUrls: ["https://polygon-rpc.com"],
461
- nativeCurrency: { name: "POL", symbol: "POL", decimals: 18 },
462
- blockExplorerUrls: ["https://polygonscan.com"]
463
- },
464
- 8453: {
465
- chainIdHex: "0x2105",
466
- chainName: "Base",
467
- rpcUrls: ["https://mainnet.base.org"],
468
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
469
- blockExplorerUrls: ["https://basescan.org"]
470
- },
471
- 42161: {
472
- chainIdHex: "0xa4b1",
473
- chainName: "Arbitrum One",
474
- rpcUrls: ["https://arb1.arbitrum.io/rpc"],
475
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
476
- blockExplorerUrls: ["https://arbiscan.io"]
477
- },
478
- 43114: {
479
- chainIdHex: "0xa86a",
480
- chainName: "Avalanche C-Chain",
481
- rpcUrls: ["https://api.avax.network/ext/bc/C/rpc"],
482
- nativeCurrency: { name: "Avalanche", symbol: "AVAX", decimals: 18 },
483
- blockExplorerUrls: ["https://snowtrace.io"]
484
- }
485
- };
543
+ init_chainParams();
486
544
  }
487
545
  });
488
546
 
@@ -492,7 +550,7 @@ var init_constants = __esm({
492
550
  "src/constants.ts"() {
493
551
  "use strict";
494
552
  SDK_NAME = "@trustware/sdk";
495
- SDK_VERSION = "1.1.12-staging.6";
553
+ SDK_VERSION = "1.1.12-staging.8";
496
554
  API_ROOT = "https://bv-staging-api.trustware.io";
497
555
  GTM_ID = "GTM-TZDGNCXB";
498
556
  API_PREFIX = "/api";
@@ -4826,6 +4884,7 @@ var init_registry = __esm({
4826
4884
  "use strict";
4827
4885
  init_store();
4828
4886
  init_chains();
4887
+ init_chainParams();
4829
4888
  NATIVE = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
4830
4889
  PAGE_CACHE_TTL_MS = 5 * 60 * 1e3;
4831
4890
  PAGE_CACHE_LIMIT = 100;
@@ -4904,6 +4963,10 @@ var init_registry = __esm({
4904
4963
  for (const alias of getChainAliases(normalized)) {
4905
4964
  this._chainAliases.set(alias, canonicalKey);
4906
4965
  }
4966
+ const addParams = chainParamsFromChainDef(normalized);
4967
+ if (addParams) {
4968
+ registerChainParams(Number(normalized.chainId ?? canonical), addParams);
4969
+ }
4907
4970
  }
4908
4971
  storeTokenForAlias(chainKey, token) {
4909
4972
  const key = tokenKey(token);
@@ -5488,6 +5551,25 @@ function backendChainId(chain, fallback) {
5488
5551
  function sleep2(ms) {
5489
5552
  return new Promise((resolve) => setTimeout(resolve, ms));
5490
5553
  }
5554
+ async function ensureWalletOnChain(wallet, target) {
5555
+ if (!Number.isSafeInteger(target) || target <= 0) {
5556
+ throw new Error(`Invalid chain id: ${target}`);
5557
+ }
5558
+ if (await wallet.getChainId() === target) return;
5559
+ let switchError;
5560
+ try {
5561
+ await wallet.switchChain(target);
5562
+ } catch (e2) {
5563
+ switchError = e2;
5564
+ }
5565
+ if (await wallet.getChainId() === target) return;
5566
+ if (switchError && isUserRejected(switchError)) {
5567
+ throw switchError;
5568
+ }
5569
+ throw new Error(
5570
+ `Wallet is on the wrong network. Switch to chain ${target} and try again.`
5571
+ );
5572
+ }
5491
5573
  async function sendEvmTx(w, tx) {
5492
5574
  if (w.type === "eip1193") {
5493
5575
  const from = await w.getAddress();
@@ -5514,19 +5596,37 @@ async function sendEvmTx(w, tx) {
5514
5596
  });
5515
5597
  return response.hash;
5516
5598
  }
5517
- async function waitForTxConfirmation(chainId, txHash) {
5518
- const timeoutMs = 12e4;
5519
- const intervalMs = 2e3;
5599
+ async function waitForEvmTxConfirmation(chainId, txHash, label, options = {}) {
5600
+ const {
5601
+ readStatus = getEVMTxStatus,
5602
+ timeoutMs = 12e4,
5603
+ intervalMs = 2e3,
5604
+ notFoundGraceMs = NOT_FOUND_GRACE_MS
5605
+ } = options;
5520
5606
  const started = Date.now();
5607
+ let notFoundSince = null;
5521
5608
  while (Date.now() - started < timeoutMs) {
5522
- const status = await getEVMTxStatus({ chainId, txHash });
5609
+ const status = await readStatus({ chainId, txHash });
5523
5610
  if (status.status === "success") return;
5524
5611
  if (status.status === "reverted") {
5525
- throw new Error("Approval transaction reverted");
5612
+ throw new Error(`${label} transaction reverted`);
5613
+ }
5614
+ if (status.status === "not_found") {
5615
+ notFoundSince ?? (notFoundSince = Date.now());
5616
+ if (Date.now() - notFoundSince >= notFoundGraceMs) {
5617
+ throw new Error(
5618
+ `${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.`
5619
+ );
5620
+ }
5621
+ } else {
5622
+ notFoundSince = null;
5526
5623
  }
5527
5624
  await sleep2(intervalMs);
5528
5625
  }
5529
- throw new Error("Timed out waiting for approval confirmation");
5626
+ throw new Error(`Timed out waiting for ${label.toLowerCase()} confirmation`);
5627
+ }
5628
+ function waitForApprovalConfirmation(chainId, txHash, options) {
5629
+ return waitForEvmTxConfirmation(chainId, txHash, "Approval", options);
5530
5630
  }
5531
5631
  function requiredApprovalAmount(planAmountWei, routeAmountWei) {
5532
5632
  if (planAmountWei >= UNLIMITED_APPROVAL_FLOOR && routeAmountWei !== void 0 && routeAmountWei > 0n) {
@@ -5566,17 +5666,22 @@ async function ensureApprovals(w, approvals, fallbackChainId, intentId, routeAmo
5566
5666
  functionName: "approve",
5567
5667
  args: [spender, amountWei]
5568
5668
  });
5669
+ const approvalChainId = Number(chainId);
5670
+ if (!Number.isSafeInteger(approvalChainId) || approvalChainId <= 0) {
5671
+ throw new Error(`Approval has an invalid chain id: ${chainId}`);
5672
+ }
5673
+ await ensureWalletOnChain(w, approvalChainId);
5569
5674
  const hash = await sendEvmTx(w, {
5570
5675
  to: tokenAddress,
5571
5676
  data,
5572
5677
  value: 0n,
5573
- chainId: Number.isFinite(Number(chainId)) ? Number(chainId) : void 0
5678
+ chainId: approvalChainId
5574
5679
  });
5575
5680
  if (intentId) {
5576
5681
  void submitStepReceipt(intentId, stepIndex, hash).catch(() => {
5577
5682
  });
5578
5683
  }
5579
- await waitForTxConfirmation(chainId, hash);
5684
+ await waitForApprovalConfirmation(chainId, hash);
5580
5685
  }
5581
5686
  }
5582
5687
  function isUserRejected(e2) {
@@ -5603,15 +5708,14 @@ async function sendRouteTransaction(b, fallbackChainId, options) {
5603
5708
  validatedSponsorship = b.sponsorship;
5604
5709
  }
5605
5710
  }
5606
- if (Number.isFinite(target)) {
5607
- const current = await w.getChainId();
5608
- if (current !== target) {
5609
- try {
5610
- await w.switchChain(target);
5611
- } catch {
5612
- }
5613
- }
5711
+ if (!Number.isSafeInteger(target) || target <= 0) {
5712
+ throw new Error(
5713
+ `Route is missing a usable chain id (got ${String(
5714
+ txReq.chainId ?? fallbackChainId
5715
+ )}).`
5716
+ );
5614
5717
  }
5718
+ await ensureWalletOnChain(w, target);
5615
5719
  if (!validatedSponsorship && !options?.approvalsEnsured) {
5616
5720
  await ensureApprovals(
5617
5721
  w,
@@ -5717,7 +5821,7 @@ async function runTopUp(params) {
5717
5821
  }
5718
5822
  }
5719
5823
  }
5720
- var erc20ApproveAbi, UNLIMITED_APPROVAL_FLOOR;
5824
+ var erc20ApproveAbi, NOT_FOUND_GRACE_MS, UNLIMITED_APPROVAL_FLOOR;
5721
5825
  var init_tx = __esm({
5722
5826
  "src/core/tx.ts"() {
5723
5827
  "use strict";
@@ -5727,6 +5831,7 @@ var init_tx = __esm({
5727
5831
  erc20ApproveAbi = parseAbi([
5728
5832
  "function approve(address spender, uint256 amount) returns (bool)"
5729
5833
  ]);
5834
+ NOT_FOUND_GRACE_MS = 2e4;
5730
5835
  UNLIMITED_APPROVAL_FLOOR = 1n << 255n;
5731
5836
  }
5732
5837
  });
@@ -9799,12 +9904,32 @@ function mapWalletTokens(balances, chains, tokens) {
9799
9904
  if (!foundToken && balanceRow.category === "native") {
9800
9905
  const nativeAddress = canonicalTokenAddressForChain(
9801
9906
  chain,
9802
- getNativeTokenAddress2(chain.type),
9907
+ getNativeTokenAddress2(normalizeChainType2(chain)),
9803
9908
  tokens
9804
9909
  );
9805
9910
  const nativeKey = `${normalizeChainKey2(balanceRow.chain_id)}:${nativeAddress}`;
9806
9911
  foundToken = tokensByCanonicalKey.get(nativeKey);
9807
9912
  }
9913
+ if (!foundToken && balanceRow.category === "native") {
9914
+ const native = chain.nativeCurrency;
9915
+ const symbol = native?.symbol?.trim();
9916
+ if (symbol) {
9917
+ return [
9918
+ {
9919
+ ...balanceRow,
9920
+ symbol,
9921
+ decimals: native?.decimals ?? balanceRow.decimals ?? 18,
9922
+ name: native?.name?.trim() || symbol,
9923
+ iconUrl: chain.chainIconURI || "",
9924
+ chainId: balanceRow.chain_id,
9925
+ usdPrice: void 0,
9926
+ address: getNativeTokenAddress2(normalizeChainType2(chain)),
9927
+ chainIconURI: chain.chainIconURI || "",
9928
+ chainData: chain
9929
+ }
9930
+ ];
9931
+ }
9932
+ }
9808
9933
  if (!foundToken?.name || !foundToken.symbol || !foundToken.address) {
9809
9934
  return [];
9810
9935
  }
@@ -33715,6 +33840,35 @@ var init_useSwapRoute = __esm({
33715
33840
  }
33716
33841
  });
33717
33842
 
33843
+ // src/modes/swap/routeValue.ts
33844
+ function parseUsd(value) {
33845
+ if (value == null) return null;
33846
+ const n = parseFloat(value);
33847
+ return Number.isFinite(n) ? n : null;
33848
+ }
33849
+ function routeNetUsd(estimate) {
33850
+ if (!estimate) return null;
33851
+ const toUsd = parseUsd(estimate.toAmountUsd);
33852
+ const feesUsd = parseUsd(estimate.totalFeesUsd);
33853
+ if (toUsd == null || feesUsd == null) return null;
33854
+ return toUsd - feesUsd;
33855
+ }
33856
+ function isValueDestroying(estimate) {
33857
+ const net = routeNetUsd(estimate);
33858
+ if (net == null) return false;
33859
+ const toUsd = parseUsd(estimate?.toAmountUsd) ?? 0;
33860
+ const feesUsd = parseUsd(estimate?.totalFeesUsd) ?? 0;
33861
+ if (toUsd < USD_NOISE_FLOOR && feesUsd < USD_NOISE_FLOOR) return false;
33862
+ return net < 0;
33863
+ }
33864
+ var USD_NOISE_FLOOR;
33865
+ var init_routeValue = __esm({
33866
+ "src/modes/swap/routeValue.ts"() {
33867
+ "use strict";
33868
+ USD_NOISE_FLOOR = 5e-3;
33869
+ }
33870
+ });
33871
+
33718
33872
  // src/modes/swap/hooks/useSwapExecution.ts
33719
33873
  import { useCallback as useCallback21, useRef as useRef16, useState as useState29 } from "react";
33720
33874
  import { encodeFunctionData as encodeFunctionData10, erc20Abi as erc20Abi2 } from "viem";
@@ -33728,9 +33882,6 @@ function normalizeTx(raw) {
33728
33882
  toChainTxUrl: r.toChainTxUrl || r.to_chain_tx_url || void 0
33729
33883
  };
33730
33884
  }
33731
- function sleep4(ms) {
33732
- return new Promise((resolve) => setTimeout(resolve, ms));
33733
- }
33734
33885
  function isUserRejection(err) {
33735
33886
  if (!err) return false;
33736
33887
  const e2 = err;
@@ -33739,17 +33890,6 @@ function isUserRejection(err) {
33739
33890
  const msg = (err instanceof Error ? err.message : String(err)).toLowerCase();
33740
33891
  return msg.includes("user rejected") || msg.includes("user denied") || msg.includes("cancelled");
33741
33892
  }
33742
- async function waitForApprovalConfirmation(chainId, txHash) {
33743
- const started = Date.now();
33744
- while (Date.now() - started < 12e4) {
33745
- const status = await getEVMTxStatus({ chainId, txHash });
33746
- if (status.status === "success") return;
33747
- if (status.status === "reverted")
33748
- throw new Error("Approval transaction reverted");
33749
- await sleep4(2e3);
33750
- }
33751
- throw new Error("Timed out waiting for approval confirmation");
33752
- }
33753
33893
  async function requireActiveAddress(wallet, plannedFor) {
33754
33894
  if (!wallet) throw new Error("Wallet not connected. Please reconnect.");
33755
33895
  const active = await wallet.getAddress();
@@ -33913,6 +34053,12 @@ function useSwapExecution(fromChain) {
33913
34053
  onError(msg);
33914
34054
  return;
33915
34055
  }
34056
+ if (isValueDestroying(routeResult.route?.estimate)) {
34057
+ const msg = "This route now costs more in fees than it delivers. Get a new quote.";
34058
+ setState((p) => ({ ...p, txStatus: "error", errorMessage: msg }));
34059
+ onError(msg);
34060
+ return;
34061
+ }
33916
34062
  setState((p) => ({
33917
34063
  ...p,
33918
34064
  isSubmitting: true,
@@ -34054,15 +34200,10 @@ function useSwapExecution(fromChain) {
34054
34200
  throw new Error("Invalid chain ID for token approval");
34055
34201
  }
34056
34202
  const targetChain = Number(required.chainId ?? txReq.chainId);
34057
- if (Number.isFinite(targetChain)) {
34058
- const current = await wallet.getChainId();
34059
- if (current !== targetChain) {
34060
- try {
34061
- await wallet.switchChain(targetChain);
34062
- } catch {
34063
- }
34064
- }
34203
+ if (!Number.isFinite(targetChain)) {
34204
+ throw new Error("Invalid chain ID for token approval");
34065
34205
  }
34206
+ await ensureWalletOnChain(wallet, targetChain);
34066
34207
  const approveAmount = maxApproval ? BigInt(
34067
34208
  "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
34068
34209
  ) : required.amountWei;
@@ -34188,6 +34329,7 @@ var init_useSwapExecution = __esm({
34188
34329
  init_failure();
34189
34330
  init_tx();
34190
34331
  init_sdkRpc();
34332
+ init_routeValue();
34191
34333
  init_chainHelpers();
34192
34334
  FAST_POLL_MS = 1500;
34193
34335
  SLOW_POLL_MS = 2500;
@@ -34196,6 +34338,33 @@ var init_useSwapExecution = __esm({
34196
34338
  }
34197
34339
  });
34198
34340
 
34341
+ // src/modes/swap/walletBalance.ts
34342
+ function compareKey(address, chainType) {
34343
+ if (chainType) return normalizeAddress2(address, chainType);
34344
+ return HEX_ADDRESS.test(address.trim()) ? address.toLowerCase() : address.trim();
34345
+ }
34346
+ function findWalletBalanceRow(rows, token, chainId, chainType) {
34347
+ const rawAddress = token?.address;
34348
+ if (!rawAddress || !rows?.length) return void 0;
34349
+ const wantAddress = compareKey(rawAddress, chainType);
34350
+ if (!wantAddress) return void 0;
34351
+ const wantChain = chainId == null ? "" : String(chainId).trim().toLowerCase();
34352
+ return rows.find((row) => {
34353
+ if (!row?.address) return false;
34354
+ if (compareKey(row.address, chainType) !== wantAddress) return false;
34355
+ if (!wantChain) return true;
34356
+ return String(row.chainId ?? "").trim().toLowerCase() === wantChain;
34357
+ });
34358
+ }
34359
+ var HEX_ADDRESS;
34360
+ var init_walletBalance = __esm({
34361
+ "src/modes/swap/walletBalance.ts"() {
34362
+ "use strict";
34363
+ init_chainHelpers();
34364
+ HEX_ADDRESS = /^0x[0-9a-f]+$/i;
34365
+ }
34366
+ });
34367
+
34199
34368
  // src/core/forex.ts
34200
34369
  async function fetchForexRates(base2 = "USD") {
34201
34370
  let r;
@@ -36357,14 +36526,19 @@ function SwapMode({
36357
36526
  route
36358
36527
  ]);
36359
36528
  const fromBalance = useMemo29(() => {
36360
- const walletToken = fromToken;
36361
- if (!walletToken || !("balance" in walletToken)) return null;
36362
- const raw = walletToken.balance;
36529
+ if (!fromToken) return null;
36530
+ const row = findWalletBalanceRow(
36531
+ yourWalletTokens,
36532
+ fromToken,
36533
+ fromChain?.chainId,
36534
+ normalizeChainType2(fromChain)
36535
+ );
36536
+ const raw = row?.balance ?? fromToken.balance;
36363
36537
  if (!raw) return null;
36364
- const decimals = fromToken?.decimals ?? 18;
36538
+ const decimals = row?.decimals ?? fromToken?.decimals ?? 18;
36365
36539
  const n = Number(rawToDecimal(raw, decimals));
36366
36540
  return Number.isFinite(n) ? n : null;
36367
- }, [fromToken]);
36541
+ }, [fromToken, fromChain, yourWalletTokens]);
36368
36542
  const balanceUsd = fromBalance !== null && hasFromUsdPrice ? fromBalance * fromTokenPriceUSD : null;
36369
36543
  const estimatedToAmount = useMemo29(() => {
36370
36544
  if (tokenSellNum <= 0 || !hasFromUsdPrice || !hasToUsdPrice) return null;
@@ -38133,8 +38307,9 @@ function SwapMode({
38133
38307
  }
38134
38308
  ) });
38135
38309
  }
38136
- 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";
38137
- const ctaDisabled = !hasTokens || !hasAmount || insufficient || isConnected && needsDestAddress && !isValidDestAddress || route.loading;
38310
+ const feesExceedOutput = isValueDestroying(route.data?.route?.estimate);
38311
+ 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";
38312
+ const ctaDisabled = !hasTokens || !hasAmount || insufficient || isConnected && needsDestAddress && !isValidDestAddress || route.loading || feesExceedOutput;
38138
38313
  const ctaAction = !isConnected ? handleConnectAndReview : () => void handleReview();
38139
38314
  const showRateRow = hasTokens && exchangeRate !== null;
38140
38315
  return /* @__PURE__ */ jsx59(WidgetContainer, { theme: resolvedTheme, style, children: /* @__PURE__ */ jsxs47(
@@ -39236,35 +39411,51 @@ function SwapMode({
39236
39411
  ]
39237
39412
  }
39238
39413
  ),
39239
- /* @__PURE__ */ jsx59("div", { style: { padding: `${spacing[2]} ${spacing[3]} 0` }, children: /* @__PURE__ */ jsx59(
39240
- "button",
39241
- {
39242
- disabled: ctaDisabled,
39243
- onClick: ctaAction,
39244
- style: mergeStyles(
39245
- {
39246
- width: "100%",
39247
- height: "3.5rem",
39248
- borderRadius: "1.5rem",
39249
- fontSize: fontSize.base,
39250
- fontWeight: fontWeight.semibold,
39251
- border: 0,
39252
- transition: "all 0.2s",
39253
- marginBottom: spacing[3]
39414
+ /* @__PURE__ */ jsxs47("div", { style: { padding: `${spacing[2]} ${spacing[3]} 0` }, children: [
39415
+ /* @__PURE__ */ jsx59(
39416
+ "button",
39417
+ {
39418
+ disabled: ctaDisabled,
39419
+ onClick: ctaAction,
39420
+ style: mergeStyles(
39421
+ {
39422
+ width: "100%",
39423
+ height: "3.5rem",
39424
+ borderRadius: "1.5rem",
39425
+ fontSize: fontSize.base,
39426
+ fontWeight: fontWeight.semibold,
39427
+ border: 0,
39428
+ transition: "all 0.2s",
39429
+ marginBottom: spacing[3]
39430
+ },
39431
+ !ctaDisabled ? {
39432
+ backgroundColor: colors.primary,
39433
+ color: colors.primaryForeground,
39434
+ cursor: "pointer"
39435
+ } : {
39436
+ backgroundColor: "hsl(var(--tw-muted))",
39437
+ color: colors.mutedForeground,
39438
+ cursor: "default"
39439
+ }
39440
+ ),
39441
+ children: ctaLabel
39442
+ }
39443
+ ),
39444
+ feesExceedOutput && /* @__PURE__ */ jsx59(
39445
+ "div",
39446
+ {
39447
+ style: {
39448
+ marginTop: `-${spacing[2]}`,
39449
+ marginBottom: spacing[3],
39450
+ fontSize: fontSize.xs,
39451
+ lineHeight: 1.4,
39452
+ color: "#f87171",
39453
+ textAlign: "center"
39254
39454
  },
39255
- !ctaDisabled ? {
39256
- backgroundColor: colors.primary,
39257
- color: colors.primaryForeground,
39258
- cursor: "pointer"
39259
- } : {
39260
- backgroundColor: "hsl(var(--tw-muted))",
39261
- color: colors.mutedForeground,
39262
- cursor: "default"
39263
- }
39264
- ),
39265
- children: ctaLabel
39266
- }
39267
- ) }),
39455
+ children: "This route costs more in fees than it delivers. Try a larger amount, or a different token or network."
39456
+ }
39457
+ )
39458
+ ] }),
39268
39459
  showRateRow && /* @__PURE__ */ jsxs47(
39269
39460
  "div",
39270
39461
  {
@@ -40109,6 +40300,8 @@ var init_SwapMode = __esm({
40109
40300
  init_provider();
40110
40301
  init_useSwapRoute();
40111
40302
  init_useSwapExecution();
40303
+ init_routeValue();
40304
+ init_walletBalance();
40112
40305
  init_useForex();
40113
40306
  init_SwapTokenSelect();
40114
40307
  init_currency();