@trustware/sdk-staging 1.1.17-staging.6 → 1.1.18-staging.1

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.mjs CHANGED
@@ -611,7 +611,7 @@ var init_constants = __esm({
611
611
  "src/constants.ts"() {
612
612
  "use strict";
613
613
  SDK_NAME = "@trustware/sdk";
614
- SDK_VERSION = "1.1.17-staging.6";
614
+ SDK_VERSION = "1.1.18-staging.1";
615
615
  API_ROOT = "https://bv-staging-api.trustware.io";
616
616
  GTM_ID = "GTM-TZDGNCXB";
617
617
  API_PREFIX = "/api";
@@ -4938,6 +4938,32 @@ var init_routeError = __esm({
4938
4938
  }
4939
4939
  });
4940
4940
 
4941
+ // src/core/routeApprovals.ts
4942
+ function approvalAmountWei(approval, stepIndex) {
4943
+ const { amount } = approval;
4944
+ if (amount === void 0 || amount === "") {
4945
+ throw new Error(
4946
+ `Route approval ${stepIndex} (spender ${String(approval.spender)}) has no amount.`
4947
+ );
4948
+ }
4949
+ if (!DECIMAL_INTEGER.test(amount)) {
4950
+ throw new Error(
4951
+ `Route approval ${stepIndex} (spender ${String(approval.spender)}) has an invalid amount "${amount}".`
4952
+ );
4953
+ }
4954
+ return BigInt(amount);
4955
+ }
4956
+ function assertValidRouteApprovals(approvals) {
4957
+ (approvals ?? []).forEach(approvalAmountWei);
4958
+ }
4959
+ var DECIMAL_INTEGER;
4960
+ var init_routeApprovals = __esm({
4961
+ "src/core/routeApprovals.ts"() {
4962
+ "use strict";
4963
+ DECIMAL_INTEGER = /^\d+$/;
4964
+ }
4965
+ });
4966
+
4941
4967
  // src/core/routeValue.ts
4942
4968
  function parseUsd(value) {
4943
4969
  if (value == null) return null;
@@ -5098,6 +5124,7 @@ async function buildRoute(body, signal) {
5098
5124
  if (!txReq?.data) {
5099
5125
  throw new Error("Invalid route: missing transaction data");
5100
5126
  }
5127
+ assertValidRouteApprovals(route?.execution?.approvals);
5101
5128
  assertRouteDeliversValue(route);
5102
5129
  return { intentId, txReq, actions, finalExchangeRate, route, sponsorship };
5103
5130
  }
@@ -5218,6 +5245,7 @@ var init_routes = __esm({
5218
5245
  "use strict";
5219
5246
  init_http();
5220
5247
  init_routeError();
5248
+ init_routeApprovals();
5221
5249
  init_routeValue();
5222
5250
  init_store();
5223
5251
  init_address();
@@ -6361,11 +6389,13 @@ async function ensureApprovals(w, approvals, fallbackChainId, intentId, routeAmo
6361
6389
  for (const [stepIndex, approval] of approvals.entries()) {
6362
6390
  const spender = approval.spender;
6363
6391
  const tokenAddress = approval.tokenAddress;
6364
- const amount = approval.amount;
6365
- if (!spender || !tokenAddress || !amount) continue;
6392
+ if (!spender || !tokenAddress) continue;
6366
6393
  const chainId = approval.chainId || String(fallbackChainId ?? "");
6367
6394
  if (!chainId) continue;
6368
- const amountWei = requiredApprovalAmount(BigInt(amount), routeAmountWei);
6395
+ const amountWei = requiredApprovalAmount(
6396
+ approvalAmountWei(approval, stepIndex),
6397
+ routeAmountWei
6398
+ );
6369
6399
  let currentAllowance = 0n;
6370
6400
  try {
6371
6401
  const { allowance } = await getEVMAllowance({
@@ -6451,7 +6481,6 @@ async function sendRouteTransactionReporting(b, fallbackChainId, options, onAcce
6451
6481
  )}).`
6452
6482
  );
6453
6483
  }
6454
- await ensureWalletOnChain(w, target);
6455
6484
  if (!validatedSponsorship && !options?.approvalsEnsured) {
6456
6485
  await ensureApprovals(
6457
6486
  w,
@@ -6461,6 +6490,7 @@ async function sendRouteTransactionReporting(b, fallbackChainId, options, onAcce
6461
6490
  BigInt(b.route?.estimate?.fromAmount ?? "0")
6462
6491
  );
6463
6492
  }
6493
+ await ensureWalletOnChain(w, target);
6464
6494
  if (w.type === "eip1193") {
6465
6495
  const from = await w.getAddress();
6466
6496
  const hexValue = value ? `0x${value.toString(16)}` : "0x0";
@@ -6627,6 +6657,7 @@ var erc20ApproveAbi, NOT_FOUND_GRACE_MS, UNLIMITED_APPROVAL_FLOOR;
6627
6657
  var init_tx = __esm({
6628
6658
  "src/core/tx.ts"() {
6629
6659
  "use strict";
6660
+ init_routeApprovals();
6630
6661
  init_routeValue();
6631
6662
  init_wallets();
6632
6663
  init_routes();
@@ -9930,11 +9961,7 @@ function mapRouteFacts(facts) {
9930
9961
  return INVALID_ADDRESS;
9931
9962
  }
9932
9963
  if (!facts.allDeclined) {
9933
- return {
9934
- category: "route_error",
9935
- title: "Route Unavailable",
9936
- message: "A routing provider did not respond. Please try again in a moment."
9937
- };
9964
+ return ROUTING_BUSY;
9938
9965
  }
9939
9966
  const has = (code) => facts.codes.includes(code);
9940
9967
  if (has(RouteDeclineCode.FeesExceedOutput)) {
@@ -9954,12 +9981,8 @@ function mapRouteFacts(facts) {
9954
9981
  message: minimum ? `This route needs at least ${minimum}. Try a larger amount.` : "This amount is below the minimum for this route. Try a larger amount."
9955
9982
  };
9956
9983
  }
9957
- if (lowLiquidity && !tooLow) {
9958
- return {
9959
- category: "route_error",
9960
- title: "Insufficient Liquidity",
9961
- message: "Not enough liquidity for this swap. Try a smaller amount or different token."
9962
- };
9984
+ if (facts.codes.every((code) => code === RouteDeclineCode.InsufficientLiquidity)) {
9985
+ return INSUFFICIENT_LIQUIDITY;
9963
9986
  }
9964
9987
  if (facts.codes.every((code) => code === RouteDeclineCode.DestinationCallFailed)) {
9965
9988
  return DESTINATION_CALL_FAILED;
@@ -10011,11 +10034,7 @@ function classifyError(raw, msg) {
10011
10034
  };
10012
10035
  }
10013
10036
  if (bm.includes("liquidity") || bm.includes("slippage") || bm.includes("price impact")) {
10014
- return {
10015
- category: "route_error",
10016
- title: "Insufficient Liquidity",
10017
- message: "Not enough liquidity for this swap. Try a smaller amount or different token."
10018
- };
10037
+ return INSUFFICIENT_LIQUIDITY;
10019
10038
  }
10020
10039
  if (bm.includes("insufficient funds") || bm.includes("insufficient balance")) {
10021
10040
  return {
@@ -10051,12 +10070,8 @@ function classifyError(raw, msg) {
10051
10070
  message: "Could not get a quote right now. Please try again."
10052
10071
  };
10053
10072
  }
10054
- if (lower.includes("not enough liquidity") || lower.includes("insufficient liquidity") || lower.includes("price impact too high")) {
10055
- return {
10056
- category: "route_error",
10057
- title: "Insufficient Liquidity",
10058
- message: "Not enough liquidity for this swap. Try a smaller amount or different token."
10059
- };
10073
+ if (lower.includes("enough liquidity") || lower.includes("insufficient liquidity") || lower.includes("price impact too high")) {
10074
+ return INSUFFICIENT_LIQUIDITY;
10060
10075
  }
10061
10076
  if (lower.includes("destination contract call")) {
10062
10077
  return DESTINATION_CALL_FAILED;
@@ -10064,6 +10079,9 @@ function classifyError(raw, msg) {
10064
10079
  if (lower.includes("invalid address") || lower.includes("address is not valid")) {
10065
10080
  return INVALID_ADDRESS;
10066
10081
  }
10082
+ if (lower.includes("routing is busy")) {
10083
+ return ROUTING_BUSY;
10084
+ }
10067
10085
  if (lower.includes("did not respond") || lower.includes("routing provider")) {
10068
10086
  return {
10069
10087
  category: "route_error",
@@ -10142,7 +10160,7 @@ function classifyError(raw, msg) {
10142
10160
  message: "An unexpected error occurred. Please try again."
10143
10161
  };
10144
10162
  }
10145
- var SELF_MAPPED, INVALID_ADDRESS, DESTINATION_CALL_FAILED;
10163
+ var SELF_MAPPED, INVALID_ADDRESS, DESTINATION_CALL_FAILED, INSUFFICIENT_LIQUIDITY, ROUTING_BUSY;
10146
10164
  var init_mapError = __esm({
10147
10165
  "src/widget/lib/mapError.ts"() {
10148
10166
  "use strict";
@@ -10163,6 +10181,16 @@ var init_mapError = __esm({
10163
10181
  title: "Destination Call Failed",
10164
10182
  message: "The destination contract call could not be simulated. Check the call data and target."
10165
10183
  };
10184
+ INSUFFICIENT_LIQUIDITY = {
10185
+ category: "route_error",
10186
+ title: "Insufficient Liquidity",
10187
+ message: "There isn't enough liquidity to route this swap right now. Try a different token or network, or a smaller amount."
10188
+ };
10189
+ ROUTING_BUSY = {
10190
+ category: "route_error",
10191
+ title: "Route Unavailable",
10192
+ message: "Routing is busy right now. Please wait a moment and try again."
10193
+ };
10166
10194
  }
10167
10195
  });
10168
10196
 
@@ -20040,6 +20068,37 @@ var init_SuccessSummaryCard = __esm({
20040
20068
  }
20041
20069
  });
20042
20070
 
20071
+ // src/widget/helpers/plannedApprovals.ts
20072
+ function plannedErc20Approvals(approvals, route) {
20073
+ return (approvals ?? []).flatMap((approval, stepIndex) => {
20074
+ const token = approval.tokenAddress ?? route.fromTokenAddress;
20075
+ const spender = approval.spender;
20076
+ if (!isEvmAddress2(token) || !isEvmAddress2(spender)) return [];
20077
+ return [
20078
+ {
20079
+ stepIndex,
20080
+ chainId: approval.chainId || route.chainId,
20081
+ token,
20082
+ spender,
20083
+ // A planned 0 is the reset-to-zero step USDT-style tokens need
20084
+ // before an existing allowance can change; it is kept as 0.
20085
+ amountWei: requiredApprovalAmount(
20086
+ approvalAmountWei(approval, stepIndex),
20087
+ route.amountWei
20088
+ )
20089
+ }
20090
+ ];
20091
+ });
20092
+ }
20093
+ var init_plannedApprovals = __esm({
20094
+ "src/widget/helpers/plannedApprovals.ts"() {
20095
+ "use strict";
20096
+ init_routeApprovals();
20097
+ init_tx();
20098
+ init_chainHelpers();
20099
+ }
20100
+ });
20101
+
20043
20102
  // src/widget/lib/routeChange.ts
20044
20103
  function describeRouteChange(change) {
20045
20104
  switch (change.kind) {
@@ -33339,9 +33398,6 @@ var init_smart_account2 = __esm({
33339
33398
  // src/widget/features/transaction/hooks/useTransactionActionModel.ts
33340
33399
  import { useCallback as useCallback20, useEffect as useEffect26, useMemo as useMemo22, useRef as useRef12, useState as useState26 } from "react";
33341
33400
  import { encodeFunctionData as encodeFunctionData9, erc20Abi } from "viem";
33342
- function sleep3(ms) {
33343
- return new Promise((resolve) => setTimeout(resolve, ms));
33344
- }
33345
33401
  function useTransactionActionModel({
33346
33402
  actionErrorMessage,
33347
33403
  amountWei,
@@ -33379,92 +33435,72 @@ function useTransactionActionModel({
33379
33435
  selectedChain?.id,
33380
33436
  selectedChain?.networkIdentifier
33381
33437
  ]);
33382
- const selectedTokenOnBackendChain = useMemo22(() => {
33383
- if (!backendChainId2) return false;
33384
- const target = normalizeChainKey(backendChainId2);
33385
- return [
33386
- selectedChain?.chainId,
33387
- selectedChain?.id,
33388
- selectedChain?.networkIdentifier
33389
- ].some((c) => {
33390
- const k = normalizeChainKey(c ?? null);
33391
- return k !== "" && k === target;
33392
- });
33393
- }, [
33394
- backendChainId2,
33395
- selectedChain?.chainId,
33396
- selectedChain?.id,
33397
- selectedChain?.networkIdentifier
33398
- ]);
33399
33438
  const isNativeSelected = useMemo22(() => {
33400
33439
  const address = selectedToken?.address;
33401
33440
  return !address || isNativeTokenAddress(address, chainType);
33402
33441
  }, [chainType, selectedToken?.address]);
33403
- const spender = useMemo22(() => {
33404
- const txReq = routeResult?.txReq;
33405
- const addr = txReq?.to ?? txReq?.target;
33406
- return addr ?? null;
33407
- }, [routeResult?.txReq]);
33408
- const [allowanceWei, setAllowanceWei] = useState26(0n);
33442
+ const requiredApprovals = useMemo22(
33443
+ () => isEvm && !isNativeSelected && !routeResult?.sponsorship ? plannedErc20Approvals(routeResult?.route?.execution?.approvals, {
33444
+ fromTokenAddress: selectedToken?.address,
33445
+ chainId: backendChainId2 ?? "",
33446
+ amountWei
33447
+ }) : [],
33448
+ [
33449
+ amountWei,
33450
+ backendChainId2,
33451
+ isEvm,
33452
+ isNativeSelected,
33453
+ routeResult?.route?.execution?.approvals,
33454
+ routeResult?.sponsorship,
33455
+ selectedToken?.address
33456
+ ]
33457
+ );
33458
+ const [unmetApprovals, setUnmetApprovals] = useState26(
33459
+ []
33460
+ );
33409
33461
  const [isReadingAllowance, setIsReadingAllowance] = useState26(false);
33410
33462
  const [isApproving, setIsApproving] = useState26(false);
33411
33463
  const [gasReservationWei, setGasReservationWei] = useState26(0n);
33464
+ const findUnmetApprovals = useCallback20(async () => {
33465
+ if (!walletAddress) return [];
33466
+ const met = await Promise.all(
33467
+ requiredApprovals.map(async (approval) => {
33468
+ try {
33469
+ const { allowance } = await getEVMAllowance({
33470
+ chainId: approval.chainId,
33471
+ tokenAddress: approval.token,
33472
+ ownerAddress: walletAddress,
33473
+ spenderAddress: approval.spender
33474
+ });
33475
+ return approvalSatisfied(
33476
+ BigInt(allowance || "0"),
33477
+ approval.amountWei
33478
+ );
33479
+ } catch {
33480
+ return false;
33481
+ }
33482
+ })
33483
+ );
33484
+ return requiredApprovals.filter((_, index) => !met[index]);
33485
+ }, [requiredApprovals, walletAddress]);
33412
33486
  const readAllowance = useCallback20(async () => {
33413
- if (!isEvm || isNativeSelected || !!routeResult?.sponsorship || // sponsored routes: SA approves bridge inside the UO batch
33414
- !backendChainId2 || !selectedTokenOnBackendChain || !walletAddress || !spender || !selectedToken?.address) {
33415
- setAllowanceWei(0n);
33487
+ if (requiredApprovals.length === 0) {
33488
+ setUnmetApprovals([]);
33416
33489
  return;
33417
33490
  }
33491
+ setIsReadingAllowance(true);
33418
33492
  try {
33419
- setIsReadingAllowance(true);
33420
- const result = await getEVMAllowance({
33421
- chainId: backendChainId2,
33422
- tokenAddress: selectedToken.address,
33423
- ownerAddress: walletAddress,
33424
- spenderAddress: spender
33425
- });
33426
- setAllowanceWei(BigInt(result.allowance || "0"));
33427
- } catch {
33428
- setAllowanceWei(0n);
33493
+ setUnmetApprovals(await findUnmetApprovals());
33429
33494
  } finally {
33430
33495
  setIsReadingAllowance(false);
33431
33496
  }
33432
- }, [
33433
- backendChainId2,
33434
- isEvm,
33435
- isNativeSelected,
33436
- routeResult?.sponsorship,
33437
- selectedTokenOnBackendChain,
33438
- selectedToken?.address,
33439
- spender,
33440
- walletAddress
33441
- ]);
33497
+ }, [findUnmetApprovals, requiredApprovals.length]);
33442
33498
  useEffect26(() => {
33443
33499
  void readAllowance();
33444
33500
  }, [readAllowance]);
33445
- const needsApproval = isEvm && !isNativeSelected && !routeResult?.sponsorship && // sponsored routes use Permit2 + SA internal batch — no EOA bridge approval
33446
- !!walletAddress && !!spender && amountWei > 0n && allowanceWei < amountWei;
33447
- const waitForApprovalConfirmation2 = useCallback20(
33448
- async (chainId, txHash) => {
33449
- const timeoutMs = 12e4;
33450
- const intervalMs = 2e3;
33451
- const started = Date.now();
33452
- while (Date.now() - started < timeoutMs) {
33453
- const status = await getEVMTxStatus({ chainId, txHash });
33454
- if (status.status === "success") {
33455
- return;
33456
- }
33457
- if (status.status === "reverted") {
33458
- throw new Error("Approval transaction reverted");
33459
- }
33460
- await sleep3(intervalMs);
33461
- }
33462
- throw new Error("Timed out waiting for approval confirmation");
33463
- },
33464
- []
33465
- );
33501
+ const needsApproval = !!walletAddress && amountWei > 0n && unmetApprovals.length > 0;
33466
33502
  const handleApproveExact = useCallback20(async () => {
33467
- if (isApproving || amountWei <= 0n || !walletAddress || !spender || !selectedToken?.address) {
33503
+ if (isApproving || amountWei <= 0n || !walletAddress || unmetApprovals.length === 0) {
33468
33504
  return;
33469
33505
  }
33470
33506
  const wallet = Trustware.getWallet();
@@ -33480,80 +33516,57 @@ function useTransactionActionModel({
33480
33516
  });
33481
33517
  setIsApproving(true);
33482
33518
  try {
33483
- const targetChain = Number(
33484
- routeResult?.txReq?.chainId ?? selectedChain?.chainId ?? selectedChain?.id
33485
- );
33486
- console.log("[approve] targetChain:", targetChain, {
33487
- txReqChainId: routeResult?.txReq?.chainId,
33488
- selectedChainId: selectedChain?.chainId,
33489
- selectedChainIdAlt: selectedChain?.id
33490
- });
33491
- if (Number.isFinite(targetChain)) {
33492
- const current = await wallet.getChainId();
33493
- if (current !== targetChain) {
33494
- try {
33495
- await wallet.switchChain(targetChain);
33496
- } catch {
33497
- }
33498
- }
33499
- }
33500
- const data = encodeFunctionData9({
33501
- abi: erc20Abi,
33502
- functionName: "approve",
33503
- args: [spender, amountWei]
33504
- });
33505
- let hash;
33506
- if (wallet.type === "eip1193") {
33507
- const from = await wallet.getAddress();
33508
- const params = {
33509
- from,
33510
- to: selectedToken.address,
33511
- data,
33512
- value: "0x0"
33513
- };
33514
- if (Number.isFinite(targetChain)) {
33515
- params.chainId = `0x${targetChain.toString(16)}`;
33516
- }
33517
- hash = await wallet.request({
33518
- method: "eth_sendTransaction",
33519
- params: [params]
33519
+ for (const approval of unmetApprovals) {
33520
+ const approvalChain = Number(approval.chainId);
33521
+ await ensureWalletOnChain(wallet, approvalChain);
33522
+ const data = encodeFunctionData9({
33523
+ abi: erc20Abi,
33524
+ functionName: "approve",
33525
+ args: [approval.spender, approval.amountWei]
33520
33526
  });
33521
- } else {
33522
- const response = await wallet.sendTransaction({
33523
- to: selectedToken.address,
33524
- data,
33525
- value: 0n,
33526
- chainId: Number.isFinite(targetChain) ? targetChain : void 0
33527
- });
33528
- hash = response.hash;
33529
- }
33530
- if (backendChainId2) {
33531
- await waitForApprovalConfirmation2(backendChainId2, hash);
33532
- await readAllowance();
33533
- } else {
33534
- setAllowanceWei(amountWei);
33527
+ let hash;
33528
+ if (wallet.type === "eip1193") {
33529
+ const from = await wallet.getAddress();
33530
+ hash = await wallet.request({
33531
+ method: "eth_sendTransaction",
33532
+ params: [
33533
+ {
33534
+ from,
33535
+ to: approval.token,
33536
+ data,
33537
+ value: "0x0",
33538
+ chainId: `0x${approvalChain.toString(16)}`
33539
+ }
33540
+ ]
33541
+ });
33542
+ } else {
33543
+ const response = await wallet.sendTransaction({
33544
+ to: approval.token,
33545
+ data,
33546
+ value: 0n,
33547
+ chainId: approvalChain
33548
+ });
33549
+ hash = response.hash;
33550
+ }
33551
+ await waitForApprovalConfirmation(approval.chainId, hash);
33535
33552
  }
33553
+ await readAllowance();
33536
33554
  } catch {
33537
33555
  } finally {
33538
33556
  setIsApproving(false);
33539
33557
  }
33540
33558
  }, [
33541
33559
  amountWei,
33542
- backendChainId2,
33543
33560
  destinationConfig?.routes.toChain,
33544
33561
  destinationConfig?.routes.toToken,
33545
33562
  isApproving,
33546
33563
  readAllowance,
33547
- routeResult?.txReq?.chainId,
33548
33564
  selectedChain?.axelarChainName,
33549
33565
  selectedChain?.chainId,
33550
- selectedChain?.id,
33551
33566
  selectedChain?.networkName,
33552
- selectedToken?.address,
33553
33567
  selectedToken?.symbol,
33554
- spender,
33555
33568
  trackEvent,
33556
- waitForApprovalConfirmation2,
33569
+ unmetApprovals,
33557
33570
  walletAddress
33558
33571
  ]);
33559
33572
  const getCachedFeeData = useCallback20(async () => {
@@ -33740,32 +33753,13 @@ function useTransactionActionModel({
33740
33753
  const handleSwipeConfirm = useCallback20(async () => {
33741
33754
  if (needsApproval) {
33742
33755
  await handleApproveExact();
33743
- if (!backendChainId2 || !selectedTokenOnBackendChain || !selectedToken?.address || !walletAddress || !spender) {
33744
- return;
33745
- }
33746
- const freshAllowance = await getEVMAllowance({
33747
- chainId: backendChainId2,
33748
- tokenAddress: selectedToken.address,
33749
- ownerAddress: walletAddress,
33750
- spenderAddress: spender
33751
- }).then((r) => BigInt(r.allowance || "0")).catch(() => 0n);
33752
- if (freshAllowance >= amountWei) {
33756
+ if ((await findUnmetApprovals()).length === 0) {
33753
33757
  await handleConfirm();
33754
33758
  }
33755
33759
  return;
33756
33760
  }
33757
33761
  await handleConfirm();
33758
- }, [
33759
- amountWei,
33760
- backendChainId2,
33761
- handleApproveExact,
33762
- handleConfirm,
33763
- needsApproval,
33764
- selectedTokenOnBackendChain,
33765
- selectedToken?.address,
33766
- spender,
33767
- walletAddress
33768
- ]);
33762
+ }, [findUnmetApprovals, handleApproveExact, handleConfirm, needsApproval]);
33769
33763
  const isWalletConnected = walletStatus === "connected";
33770
33764
  const canSwipe = parsedAmount > 0 && !!selectedToken && isWalletConnected && !isLoadingRoute && !isSubmitting && !!routeResult && !actionErrorMessage && !isApproving && !isReadingAllowance;
33771
33765
  const swipeResetKey = useMemo22(() => {
@@ -33809,9 +33803,11 @@ var init_useTransactionActionModel = __esm({
33809
33803
  "use strict";
33810
33804
  init_core2();
33811
33805
  init_sdkRpc();
33806
+ init_tx();
33812
33807
  init_hooks2();
33813
33808
  init_chains();
33814
33809
  init_utils();
33810
+ init_plannedApprovals();
33815
33811
  init_hooks();
33816
33812
  init_routeChange();
33817
33813
  }
@@ -35250,33 +35246,15 @@ function useSwapExecution(fromChain) {
35250
35246
  const chainIdStr = String(txReq?.chainId ?? fromChain?.chainId ?? "");
35251
35247
  const amountWei = BigInt(routeResult.route?.estimate?.fromAmount ?? "0");
35252
35248
  const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
35253
- const toCheck = plannedApprovals.length ? plannedApprovals.flatMap((a) => {
35254
- const token = a.tokenAddress ?? fromTokenAddress;
35255
- const required = requiredApprovalAmount(
35256
- BigInt(a.amount || "0"),
35257
- amountWei
35258
- );
35259
- if (!isEvmAddress2(token) || !isEvmAddress2(a.spender)) return [];
35260
- return [
35261
- {
35262
- chainId: a.chainId || chainIdStr,
35263
- token,
35264
- spender: a.spender,
35265
- required
35266
- }
35267
- ];
35268
- }) : (() => {
35269
- const spender = txReq?.to ?? txReq?.target;
35270
- if (!spender) return [];
35271
- return [
35272
- {
35273
- chainId: chainIdStr,
35274
- token: fromTokenAddress,
35275
- spender,
35276
- required: amountWei
35277
- }
35278
- ];
35279
- })();
35249
+ if (plannedApprovals.length === 0) {
35250
+ setState((p) => ({ ...p, allowanceStatus: "sufficient" }));
35251
+ return;
35252
+ }
35253
+ const toCheck = plannedErc20Approvals(plannedApprovals, {
35254
+ fromTokenAddress,
35255
+ chainId: chainIdStr,
35256
+ amountWei
35257
+ });
35280
35258
  if (toCheck.length === 0 || !chainIdStr || amountWei === 0n) {
35281
35259
  setState((p) => ({ ...p, allowanceStatus: "unknown" }));
35282
35260
  return;
@@ -35291,7 +35269,7 @@ function useSwapExecution(fromChain) {
35291
35269
  ownerAddress: walletAddress,
35292
35270
  spenderAddress: c.spender
35293
35271
  });
35294
- return approvalSatisfied(BigInt(allowance || "0"), c.required);
35272
+ return approvalSatisfied(BigInt(allowance || "0"), c.amountWei);
35295
35273
  })
35296
35274
  );
35297
35275
  setState((p) => ({
@@ -35346,7 +35324,6 @@ function useSwapExecution(fromChain) {
35346
35324
  const txReq = routeResult.txReq;
35347
35325
  const numericChainId = Number(txReq.chainId ?? fromChain?.chainId) || void 0;
35348
35326
  const chainIdStr = String(txReq.chainId ?? fromChain?.chainId ?? "");
35349
- const spender = txReq.to ?? txReq.target;
35350
35327
  const chainType = normalizeChainType(fromChain);
35351
35328
  const isNative = !fromTokenAddress || isNativeTokenAddress(fromTokenAddress, chainType);
35352
35329
  const fromAmountWei = BigInt(
@@ -35426,34 +35403,12 @@ function useSwapExecution(fromChain) {
35426
35403
  const acceptance = { tracking: false };
35427
35404
  try {
35428
35405
  const plannedApprovals = routeResult.route?.execution?.approvals ?? [];
35429
- const requiredApprovals = plannedApprovals.length ? plannedApprovals.flatMap((a, index) => {
35430
- const token = a.tokenAddress ?? fromTokenAddress;
35431
- const approvalSpender = a.spender;
35432
- if (!isEvmAddress2(token) || !isEvmAddress2(approvalSpender))
35433
- return [];
35434
- const amountWei = requiredApprovalAmount(
35435
- BigInt(a.amount || "0"),
35436
- fromAmountWei
35437
- );
35438
- return [
35439
- {
35440
- stepIndex: index,
35441
- token,
35442
- spender: approvalSpender,
35443
- amountWei,
35444
- chainId: a.chainId || chainIdStr
35445
- }
35446
- ];
35447
- }) : spender && fromTokenAddress ? [
35448
- {
35449
- stepIndex: null,
35450
- token: fromTokenAddress,
35451
- spender,
35452
- amountWei: fromAmountWei,
35453
- chainId: chainIdStr
35454
- }
35455
- ] : [];
35456
- const coversEveryPlannedApproval = plannedApprovals.length === 0 || requiredApprovals.length === plannedApprovals.length;
35406
+ const requiredApprovals = plannedErc20Approvals(plannedApprovals, {
35407
+ fromTokenAddress,
35408
+ chainId: chainIdStr,
35409
+ amountWei: fromAmountWei
35410
+ });
35411
+ const coversEveryPlannedApproval = requiredApprovals.length === plannedApprovals.length;
35457
35412
  const ownsApprovals = coversEveryPlannedApproval && needsErc20Approval(fromTokenAddress, chainType) && !!walletAddress && !!chainIdStr;
35458
35413
  const activeAddress = ownsApprovals ? await requireActiveAddress(wallet, walletAddress) : void 0;
35459
35414
  for (const required of requiredApprovals) {
@@ -35515,14 +35470,12 @@ function useSwapExecution(fromChain) {
35515
35470
  });
35516
35471
  approvalHash = response.hash;
35517
35472
  }
35518
- if (required.stepIndex !== null) {
35519
- void submitStepReceipt(
35520
- routeResult.intentId,
35521
- required.stepIndex,
35522
- approvalHash
35523
- ).catch(() => {
35524
- });
35525
- }
35473
+ void submitStepReceipt(
35474
+ routeResult.intentId,
35475
+ required.stepIndex,
35476
+ approvalHash
35477
+ ).catch(() => {
35478
+ });
35526
35479
  await waitForApprovalConfirmation(required.chainId, approvalHash);
35527
35480
  setState((p) => ({
35528
35481
  ...p,
@@ -35639,6 +35592,7 @@ var init_useSwapExecution = __esm({
35639
35592
  init_tx();
35640
35593
  init_sdkRpc();
35641
35594
  init_chainHelpers();
35595
+ init_plannedApprovals();
35642
35596
  init_chains();
35643
35597
  SA_COOLDOWN_MS = 3e4;
35644
35598
  }
@@ -37890,6 +37844,26 @@ var init_currency = __esm({
37890
37844
  }
37891
37845
  });
37892
37846
 
37847
+ // src/modes/swap/amountFormat.ts
37848
+ function fmtAmount(n, maxFractionDigits = 6) {
37849
+ if (!isFinite(n) || n === 0) return "0";
37850
+ if (Math.abs(n) < 10 ** -maxFractionDigits) {
37851
+ return n.toLocaleString(void 0, {
37852
+ maximumSignificantDigits: SMALL_AMOUNT_SIGNIFICANT_DIGITS
37853
+ });
37854
+ }
37855
+ return n.toLocaleString(void 0, {
37856
+ maximumFractionDigits: maxFractionDigits
37857
+ });
37858
+ }
37859
+ var SMALL_AMOUNT_SIGNIFICANT_DIGITS;
37860
+ var init_amountFormat = __esm({
37861
+ "src/modes/swap/amountFormat.ts"() {
37862
+ "use strict";
37863
+ SMALL_AMOUNT_SIGNIFICANT_DIGITS = 2;
37864
+ }
37865
+ });
37866
+
37893
37867
  // src/widget/lib/quoteCountdown.ts
37894
37868
  function quoteCountdown(elapsedMs, intervalMs) {
37895
37869
  if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
@@ -39219,10 +39193,6 @@ import {
39219
39193
  } from "react";
39220
39194
  import ReactDOM from "react-dom";
39221
39195
  import { Fragment as Fragment15, jsx as jsx57, jsxs as jsxs47 } from "react/jsx-runtime";
39222
- function fmtAmount(n, max = 6) {
39223
- if (!isFinite(n) || n === 0) return "0";
39224
- return n.toLocaleString(void 0, { maximumFractionDigits: max });
39225
- }
39226
39196
  function buildExplorerUrl(hash, chain, backendUrl) {
39227
39197
  if (backendUrl) return backendUrl;
39228
39198
  const base2 = chain?.blockExplorerUrls?.[0];
@@ -40959,249 +40929,34 @@ function SwapMode({
40959
40929
  }
40960
40930
  ),
40961
40931
  completedStr && /* @__PURE__ */ jsx57(SuccessReceiptRow, { label: "Completed", value: completedStr }),
40962
- txHash && (() => {
40963
- const srcUrl = buildExplorerUrl(
40964
- txHash,
40965
- fromChain,
40966
- fromChainTxUrl
40967
- );
40968
- const isCopied = copiedHash === txHash;
40969
- return /* @__PURE__ */ jsxs47(
40970
- "div",
40971
- {
40972
- style: {
40973
- display: "flex",
40974
- alignItems: "center",
40975
- justifyContent: "space-between"
40976
- },
40977
- children: [
40978
- /* @__PURE__ */ jsx57("span", { style: { color: colors.mutedForeground }, children: destTxHash ? "Source tx" : "Transaction" }),
40979
- /* @__PURE__ */ jsxs47(
40980
- "div",
40981
- {
40982
- style: {
40983
- display: "inline-flex",
40984
- alignItems: "center",
40985
- gap: spacing[2]
40986
- },
40987
- children: [
40988
- srcUrl && /* @__PURE__ */ jsxs47(
40989
- "a",
40990
- {
40991
- href: srcUrl,
40992
- target: "_blank",
40993
- rel: "noopener noreferrer",
40994
- style: {
40995
- display: "inline-flex",
40996
- alignItems: "center",
40997
- gap: "0.25rem",
40998
- color: colors.foreground,
40999
- fontWeight: fontWeight.medium,
41000
- textDecorationLine: "underline",
41001
- textDecorationStyle: "dotted",
41002
- textUnderlineOffset: "4px"
41003
- },
41004
- children: [
41005
- txHash.slice(0, 6),
41006
- "\u2026",
41007
- txHash.slice(-4),
41008
- /* @__PURE__ */ jsxs47(
41009
- "svg",
41010
- {
41011
- style: { width: "0.75rem", height: "0.75rem" },
41012
- viewBox: "0 0 24 24",
41013
- fill: "none",
41014
- stroke: "currentColor",
41015
- strokeWidth: 2,
41016
- strokeLinecap: "round",
41017
- strokeLinejoin: "round",
41018
- children: [
41019
- /* @__PURE__ */ jsx57("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
41020
- /* @__PURE__ */ jsx57("polyline", { points: "15 3 21 3 21 9" }),
41021
- /* @__PURE__ */ jsx57("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
41022
- ]
41023
- }
41024
- )
41025
- ]
41026
- }
41027
- ),
41028
- /* @__PURE__ */ jsx57(
41029
- "button",
41030
- {
41031
- onClick: () => handleCopyHash(txHash),
41032
- title: "Copy full hash",
41033
- style: {
41034
- display: "inline-flex",
41035
- alignItems: "center",
41036
- gap: "0.2rem",
41037
- fontSize: fontSize.xs,
41038
- color: isCopied ? colors.green[500] : colors.mutedForeground,
41039
- backgroundColor: "transparent",
41040
- border: 0,
41041
- cursor: "pointer",
41042
- fontFamily: "inherit",
41043
- padding: 0,
41044
- transition: "color 0.15s"
41045
- },
41046
- children: isCopied ? "Copied!" : /* @__PURE__ */ jsxs47(
41047
- "svg",
41048
- {
41049
- style: {
41050
- width: "0.875rem",
41051
- height: "0.875rem"
41052
- },
41053
- viewBox: "0 0 24 24",
41054
- fill: "none",
41055
- stroke: "currentColor",
41056
- strokeWidth: 2,
41057
- strokeLinecap: "round",
41058
- strokeLinejoin: "round",
41059
- children: [
41060
- /* @__PURE__ */ jsx57(
41061
- "rect",
41062
- {
41063
- x: "9",
41064
- y: "9",
41065
- width: "13",
41066
- height: "13",
41067
- rx: "2",
41068
- ry: "2"
41069
- }
41070
- ),
41071
- /* @__PURE__ */ jsx57("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
41072
- ]
41073
- }
41074
- )
41075
- }
41076
- )
41077
- ]
41078
- }
41079
- )
41080
- ]
41081
- }
41082
- );
41083
- })(),
41084
- destTxHash && (() => {
41085
- const destUrl = buildExplorerUrl(
41086
- destTxHash,
41087
- toChain,
41088
- toChainTxUrl
41089
- );
41090
- const isCopied = copiedHash === destTxHash;
41091
- return /* @__PURE__ */ jsxs47(
41092
- "div",
41093
- {
41094
- style: {
41095
- display: "flex",
41096
- alignItems: "center",
41097
- justifyContent: "space-between"
41098
- },
41099
- children: [
41100
- /* @__PURE__ */ jsx57("span", { style: { color: colors.mutedForeground }, children: "Destination tx" }),
41101
- /* @__PURE__ */ jsxs47(
41102
- "div",
41103
- {
41104
- style: {
41105
- display: "inline-flex",
41106
- alignItems: "center",
41107
- gap: spacing[2]
41108
- },
41109
- children: [
41110
- destUrl && /* @__PURE__ */ jsxs47(
41111
- "a",
41112
- {
41113
- href: destUrl,
41114
- target: "_blank",
41115
- rel: "noopener noreferrer",
41116
- style: {
41117
- display: "inline-flex",
41118
- alignItems: "center",
41119
- gap: "0.25rem",
41120
- color: colors.foreground,
41121
- fontWeight: fontWeight.medium,
41122
- textDecorationLine: "underline",
41123
- textDecorationStyle: "dotted",
41124
- textUnderlineOffset: "4px"
41125
- },
41126
- children: [
41127
- destTxHash.slice(0, 6),
41128
- "\u2026",
41129
- destTxHash.slice(-4),
41130
- /* @__PURE__ */ jsxs47(
41131
- "svg",
41132
- {
41133
- style: { width: "0.75rem", height: "0.75rem" },
41134
- viewBox: "0 0 24 24",
41135
- fill: "none",
41136
- stroke: "currentColor",
41137
- strokeWidth: 2,
41138
- strokeLinecap: "round",
41139
- strokeLinejoin: "round",
41140
- children: [
41141
- /* @__PURE__ */ jsx57("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
41142
- /* @__PURE__ */ jsx57("polyline", { points: "15 3 21 3 21 9" }),
41143
- /* @__PURE__ */ jsx57("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
41144
- ]
41145
- }
41146
- )
41147
- ]
41148
- }
41149
- ),
41150
- /* @__PURE__ */ jsx57(
41151
- "button",
41152
- {
41153
- onClick: () => handleCopyHash(destTxHash),
41154
- title: "Copy full hash",
41155
- style: {
41156
- display: "inline-flex",
41157
- alignItems: "center",
41158
- fontSize: fontSize.xs,
41159
- color: isCopied ? colors.green[500] : colors.mutedForeground,
41160
- backgroundColor: "transparent",
41161
- border: 0,
41162
- cursor: "pointer",
41163
- fontFamily: "inherit",
41164
- padding: 0,
41165
- transition: "color 0.15s"
41166
- },
41167
- children: isCopied ? "Copied!" : /* @__PURE__ */ jsxs47(
41168
- "svg",
41169
- {
41170
- style: {
41171
- width: "0.875rem",
41172
- height: "0.875rem"
41173
- },
41174
- viewBox: "0 0 24 24",
41175
- fill: "none",
41176
- stroke: "currentColor",
41177
- strokeWidth: 2,
41178
- strokeLinecap: "round",
41179
- strokeLinejoin: "round",
41180
- children: [
41181
- /* @__PURE__ */ jsx57(
41182
- "rect",
41183
- {
41184
- x: "9",
41185
- y: "9",
41186
- width: "13",
41187
- height: "13",
41188
- rx: "2",
41189
- ry: "2"
41190
- }
41191
- ),
41192
- /* @__PURE__ */ jsx57("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
41193
- ]
41194
- }
41195
- )
41196
- }
41197
- )
41198
- ]
41199
- }
41200
- )
41201
- ]
41202
- }
41203
- );
41204
- })()
40932
+ txHash && /* @__PURE__ */ jsx57(
40933
+ SuccessTxHashRow,
40934
+ {
40935
+ label: destTxHash ? "Source tx" : "Transaction",
40936
+ hash: txHash,
40937
+ explorerUrl: buildExplorerUrl(
40938
+ txHash,
40939
+ fromChain,
40940
+ fromChainTxUrl
40941
+ ),
40942
+ isCopied: copiedHash === txHash,
40943
+ onCopy: handleCopyHash
40944
+ }
40945
+ ),
40946
+ destTxHash && /* @__PURE__ */ jsx57(
40947
+ SuccessTxHashRow,
40948
+ {
40949
+ label: "Destination tx",
40950
+ hash: destTxHash,
40951
+ explorerUrl: buildExplorerUrl(
40952
+ destTxHash,
40953
+ toChain,
40954
+ toChainTxUrl
40955
+ ),
40956
+ isCopied: copiedHash === destTxHash,
40957
+ onCopy: handleCopyHash
40958
+ }
40959
+ )
41205
40960
  ]
41206
40961
  }
41207
40962
  )
@@ -43550,6 +43305,119 @@ function SuccessReceiptRow({
43550
43305
  }
43551
43306
  );
43552
43307
  }
43308
+ function SuccessTxHashRow({
43309
+ label,
43310
+ hash,
43311
+ explorerUrl,
43312
+ isCopied,
43313
+ onCopy
43314
+ }) {
43315
+ const shortHash = `${hash.slice(0, 6)}\u2026${hash.slice(-4)}`;
43316
+ return /* @__PURE__ */ jsxs47(
43317
+ "div",
43318
+ {
43319
+ style: {
43320
+ display: "flex",
43321
+ alignItems: "center",
43322
+ justifyContent: "space-between"
43323
+ },
43324
+ children: [
43325
+ /* @__PURE__ */ jsx57("span", { style: { color: colors.mutedForeground }, children: label }),
43326
+ /* @__PURE__ */ jsxs47(
43327
+ "div",
43328
+ {
43329
+ style: {
43330
+ display: "inline-flex",
43331
+ alignItems: "center",
43332
+ gap: spacing[2]
43333
+ },
43334
+ children: [
43335
+ explorerUrl ? /* @__PURE__ */ jsxs47(
43336
+ "a",
43337
+ {
43338
+ href: explorerUrl,
43339
+ target: "_blank",
43340
+ rel: "noopener noreferrer",
43341
+ style: {
43342
+ display: "inline-flex",
43343
+ alignItems: "center",
43344
+ gap: "0.25rem",
43345
+ color: colors.foreground,
43346
+ fontWeight: fontWeight.medium,
43347
+ textDecorationLine: "underline",
43348
+ textDecorationStyle: "dotted",
43349
+ textUnderlineOffset: "4px"
43350
+ },
43351
+ children: [
43352
+ shortHash,
43353
+ /* @__PURE__ */ jsxs47(
43354
+ "svg",
43355
+ {
43356
+ style: { width: "0.75rem", height: "0.75rem" },
43357
+ viewBox: "0 0 24 24",
43358
+ fill: "none",
43359
+ stroke: "currentColor",
43360
+ strokeWidth: 2,
43361
+ strokeLinecap: "round",
43362
+ strokeLinejoin: "round",
43363
+ children: [
43364
+ /* @__PURE__ */ jsx57("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
43365
+ /* @__PURE__ */ jsx57("polyline", { points: "15 3 21 3 21 9" }),
43366
+ /* @__PURE__ */ jsx57("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
43367
+ ]
43368
+ }
43369
+ )
43370
+ ]
43371
+ }
43372
+ ) : /* @__PURE__ */ jsx57(
43373
+ "span",
43374
+ {
43375
+ style: { color: colors.foreground, fontWeight: fontWeight.medium },
43376
+ children: shortHash
43377
+ }
43378
+ ),
43379
+ /* @__PURE__ */ jsx57(
43380
+ "button",
43381
+ {
43382
+ onClick: () => onCopy(hash),
43383
+ title: "Copy full hash",
43384
+ style: {
43385
+ display: "inline-flex",
43386
+ alignItems: "center",
43387
+ fontSize: fontSize.xs,
43388
+ color: isCopied ? colors.green[500] : colors.mutedForeground,
43389
+ backgroundColor: "transparent",
43390
+ border: 0,
43391
+ cursor: "pointer",
43392
+ fontFamily: "inherit",
43393
+ padding: 0,
43394
+ transition: "color 0.15s"
43395
+ },
43396
+ children: isCopied ? "Copied!" : /* @__PURE__ */ jsxs47(
43397
+ "svg",
43398
+ {
43399
+ style: { width: "0.875rem", height: "0.875rem" },
43400
+ viewBox: "0 0 24 24",
43401
+ fill: "none",
43402
+ stroke: "currentColor",
43403
+ strokeWidth: 2,
43404
+ strokeLinecap: "round",
43405
+ strokeLinejoin: "round",
43406
+ children: [
43407
+ /* @__PURE__ */ jsx57("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
43408
+ /* @__PURE__ */ jsx57("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
43409
+ ]
43410
+ }
43411
+ )
43412
+ }
43413
+ )
43414
+ ]
43415
+ }
43416
+ )
43417
+ ]
43418
+ }
43419
+ );
43420
+ }
43553
43421
  function ProcessingStepNode({
43554
43422
  active,
43555
43423
  done,
@@ -43704,6 +43572,7 @@ var init_SwapMode = __esm({
43704
43572
  init_SwapTokenSelect();
43705
43573
  init_VaultDiscovery();
43706
43574
  init_currency();
43575
+ init_amountFormat();
43707
43576
  init_quoteCountdown();
43708
43577
  init_components2();
43709
43578
  ConfettiEffect3 = lazy2(