@shapeshiftoss/swap-widget 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { useCallback as useCallback11, useEffect as useEffect10, useMemo as useM
4
4
 
5
5
  // src/api/client.ts
6
6
  var DEFAULT_API_BASE_URL = "https://api.shapeshift.com";
7
+ var getTradeAmount = (params) => params.buyAmountCryptoBaseUnit !== void 0 ? { buyAmountCryptoBaseUnit: params.buyAmountCryptoBaseUnit } : { sellAmountCryptoBaseUnit: params.sellAmountCryptoBaseUnit };
7
8
  var createApiClient = (config = {}) => {
8
9
  const baseUrl = config.baseUrl ?? DEFAULT_API_BASE_URL;
9
10
  const fetchWithConfig = async (endpoint, params, method = "GET", timeoutMs = 3e4) => {
@@ -41,7 +42,7 @@ var createApiClient = (config = {}) => {
41
42
  getRates: (params) => fetchWithConfig("/v1/swap/rates", {
42
43
  sellAssetId: params.sellAssetId,
43
44
  buyAssetId: params.buyAssetId,
44
- sellAmountCryptoBaseUnit: params.sellAmountCryptoBaseUnit
45
+ ...getTradeAmount(params)
45
46
  }),
46
47
  getSwapStatus: (params) => fetchWithConfig("/v1/swap/status", {
47
48
  quoteId: params.quoteId,
@@ -52,7 +53,7 @@ var createApiClient = (config = {}) => {
52
53
  {
53
54
  sellAssetId: params.sellAssetId,
54
55
  buyAssetId: params.buyAssetId,
55
- sellAmountCryptoBaseUnit: params.sellAmountCryptoBaseUnit,
56
+ ...getTradeAmount(params),
56
57
  sendAddress: params.sendAddress,
57
58
  receiveAddress: params.receiveAddress,
58
59
  swapperName: params.swapperName,
@@ -680,6 +681,7 @@ var formatAmount = (amount, decimals, maxDecimals) => {
680
681
  minimumFractionDigits: 0
681
682
  });
682
683
  };
684
+ var formatAmountForInput = (amount, decimals) => BigAmount.fromBaseUnit({ value: amount, precision: decimals }).toPrecision();
683
685
  var parseAmount = (amount, decimals) => {
684
686
  return BigAmount.fromPrecision({ value: amount, precision: decimals }).toBaseUnit();
685
687
  };
@@ -689,8 +691,11 @@ var truncateAddress = (address, chars = 4) => {
689
691
  };
690
692
 
691
693
  // src/machines/guards.ts
694
+ var isExactOutput = (context) => !!context.buyAmountBaseUnit;
692
695
  var hasValidInput = (context) => {
693
- return !!context.sellAsset && !!context.buyAsset && !!context.sellAmountBaseUnit && context.sellAmountBaseUnit !== "0";
696
+ if (!context.sellAsset || !context.buyAsset) return false;
697
+ const amountBaseUnit = isExactOutput(context) ? context.buyAmountBaseUnit : context.sellAmountBaseUnit;
698
+ return !!amountBaseUnit && amountBaseUnit !== "0";
694
699
  };
695
700
  var hasQuote = (context) => context.quote !== null;
696
701
  var canRetry = (context) => context.retryCount < 3;
@@ -711,6 +716,8 @@ var createInitialContext = (input) => {
711
716
  buyAsset,
712
717
  sellAmount: "",
713
718
  sellAmountBaseUnit: void 0,
719
+ buyAmount: input?.buyAmount ?? "",
720
+ buyAmountBaseUnit: input?.buyAmountBaseUnit,
714
721
  isSellAmountFiat: false,
715
722
  sellAmountFiat: "",
716
723
  selectedRate: null,
@@ -773,12 +780,14 @@ var swapMachine = setup({
773
780
  quote: null
774
781
  };
775
782
  }),
776
- assignBuyAsset: assign(({ event }) => {
783
+ assignBuyAsset: assign(({ context, event }) => {
777
784
  const { asset } = event;
778
785
  const buyChainType = getChainType(asset.chainId);
779
786
  return {
780
787
  buyAsset: asset,
781
788
  isBuyAssetEvm: buyChainType === "evm",
789
+ buyAmount: context.buyAmount,
790
+ buyAmountBaseUnit: context.buyAmount ? parseAmount(context.buyAmount, asset.precision) : void 0,
782
791
  selectedRate: null,
783
792
  quote: null
784
793
  };
@@ -788,7 +797,11 @@ var swapMachine = setup({
788
797
  return {
789
798
  sellAmount: amount,
790
799
  sellAmountBaseUnit: amountBaseUnit,
791
- sellAmountFiat: fiatValue
800
+ sellAmountFiat: fiatValue,
801
+ buyAmount: "",
802
+ buyAmountBaseUnit: void 0,
803
+ selectedRate: null,
804
+ quote: null
792
805
  };
793
806
  }),
794
807
  assignSellFiatMode: assign(({ event }) => {
@@ -832,6 +845,19 @@ var swapMachine = setup({
832
845
  assignReceiveAddress: assign(({ event }) => ({
833
846
  receiveAddress: event.address
834
847
  })),
848
+ assignBuyAmount: assign(({ event }) => {
849
+ const { amount, amountBaseUnit } = event;
850
+ return {
851
+ buyAmount: amount,
852
+ buyAmountBaseUnit: amountBaseUnit,
853
+ sellAmount: "",
854
+ sellAmountBaseUnit: void 0,
855
+ sellAmountFiat: "",
856
+ isSellAmountFiat: false,
857
+ selectedRate: null,
858
+ quote: null
859
+ };
860
+ }),
835
861
  assignChainInfo: assign(({ event }) => {
836
862
  const e = event;
837
863
  return {
@@ -859,6 +885,8 @@ var swapMachine = setup({
859
885
  buyAsset: context.buyAsset,
860
886
  sellAmount: context.sellAmount,
861
887
  sellAmountBaseUnit: context.sellAmountBaseUnit,
888
+ buyAmount: context.buyAmount,
889
+ buyAmountBaseUnit: context.buyAmountBaseUnit,
862
890
  slippage: context.slippage,
863
891
  sendAddress: context.sendAddress,
864
892
  receiveAddress: context.receiveAddress
@@ -877,6 +905,7 @@ var swapMachine = setup({
877
905
  SET_SELL_ASSET: { actions: "assignSellAsset" },
878
906
  SET_BUY_ASSET: { actions: "assignBuyAsset" },
879
907
  SET_SELL_AMOUNT: { actions: "assignSellAmount" },
908
+ SET_BUY_AMOUNT: { actions: "assignBuyAmount" },
880
909
  SET_SELL_FIAT_MODE: { actions: "assignSellFiatMode" },
881
910
  SET_SLIPPAGE: { actions: "assignSlippage" },
882
911
  SELECT_RATE: { actions: "assignSelectedRate" },
@@ -1350,11 +1379,17 @@ import {
1350
1379
  mayachain,
1351
1380
  megaeth as megaeth2,
1352
1381
  monad as monad2,
1382
+ near,
1353
1383
  optimism as optimism2,
1354
1384
  plasma as plasma2,
1355
1385
  polygon as polygon2,
1356
1386
  solana,
1357
- thorchain
1387
+ starknet,
1388
+ sui,
1389
+ thorchain,
1390
+ ton,
1391
+ tron,
1392
+ zcash
1358
1393
  } from "@shapeshiftoss/utils";
1359
1394
  var BASE_ASSETS_BY_CHAIN_ID = {
1360
1395
  [ethereum2.chainId]: ethereum2,
@@ -1374,10 +1409,16 @@ var BASE_ASSETS_BY_CHAIN_ID = {
1374
1409
  [bitcoincash.chainId]: bitcoincash,
1375
1410
  [dogecoin.chainId]: dogecoin,
1376
1411
  [litecoin.chainId]: litecoin,
1412
+ [zcash.chainId]: zcash,
1377
1413
  [atom.chainId]: atom,
1378
1414
  [thorchain.chainId]: thorchain,
1379
1415
  [mayachain.chainId]: mayachain,
1380
- [solana.chainId]: solana
1416
+ [solana.chainId]: solana,
1417
+ [tron.chainId]: tron,
1418
+ [sui.chainId]: sui,
1419
+ [ton.chainId]: ton,
1420
+ [near.chainId]: near,
1421
+ [starknet.chainId]: starknet
1381
1422
  };
1382
1423
  var SUPPORTED_CHAIN_IDS_SET = new Set(
1383
1424
  Object.keys(BASE_ASSETS_BY_CHAIN_ID)
@@ -2237,28 +2278,55 @@ var formatUsdValue = (cryptoAmount, precision, usdPrice) => {
2237
2278
  };
2238
2279
 
2239
2280
  // src/hooks/useSwapRates.ts
2240
- import { bnOrZero } from "@shapeshiftoss/utils";
2241
2281
  import { useQuery as useQuery3 } from "@tanstack/react-query";
2282
+
2283
+ // src/utils/rateDisplay.ts
2284
+ import { bnOrZero } from "@shapeshiftoss/utils";
2285
+ var getRateAmountBaseUnit = (rate, isExactOutput2) => (isExactOutput2 ? rate.sellAmountCryptoBaseUnit : rate.buyAmountCryptoBaseUnit) ?? "0";
2286
+ var sortRatesByValue = (rates, isExactOutput2) => [...rates].filter((rate) => !rate.error && getRateAmountBaseUnit(rate, isExactOutput2) !== "0").sort((a, b) => {
2287
+ const aAmount = bnOrZero(getRateAmountBaseUnit(a, isExactOutput2));
2288
+ const bAmount = bnOrZero(getRateAmountBaseUnit(b, isExactOutput2));
2289
+ return isExactOutput2 ? aAmount.minus(bAmount).toNumber() : bAmount.minus(aAmount).toNumber();
2290
+ });
2291
+ var getRateDiffPercent = (bestAmountBaseUnit, amountBaseUnit, isExactOutput2) => {
2292
+ const best = bnOrZero(bestAmountBaseUnit);
2293
+ const current = bnOrZero(amountBaseUnit);
2294
+ if (best.isZero()) return null;
2295
+ const diff = isExactOutput2 ? current.minus(best).div(best).times(100) : best.minus(current).div(best).times(100);
2296
+ return diff.gte("0.01") ? diff.toFixed(2) : null;
2297
+ };
2298
+
2299
+ // src/hooks/useSwapRates.ts
2242
2300
  var ENABLED_SWAPPER_NAMES = new Set(Object.values(SwapperName));
2243
2301
  var useSwapRates = (apiClient, params) => {
2244
2302
  const {
2245
2303
  sellAssetId,
2246
2304
  buyAssetId,
2247
2305
  sellAmountCryptoBaseUnit,
2306
+ buyAmountCryptoBaseUnit,
2248
2307
  enabled = true,
2249
2308
  allowedSwapperNames,
2250
2309
  refetchInterval = 15e3
2251
2310
  } = params;
2311
+ const isExactOutput2 = buyAmountCryptoBaseUnit !== void 0;
2312
+ const amountCryptoBaseUnit = isExactOutput2 ? buyAmountCryptoBaseUnit : sellAmountCryptoBaseUnit;
2252
2313
  return useQuery3({
2253
- queryKey: ["swapRates", sellAssetId, buyAssetId, sellAmountCryptoBaseUnit, allowedSwapperNames],
2314
+ queryKey: [
2315
+ "swapRates",
2316
+ sellAssetId,
2317
+ buyAssetId,
2318
+ amountCryptoBaseUnit,
2319
+ isExactOutput2,
2320
+ allowedSwapperNames
2321
+ ],
2254
2322
  queryFn: async () => {
2255
- if (!sellAssetId || !buyAssetId || !sellAmountCryptoBaseUnit) {
2323
+ if (!sellAssetId || !buyAssetId || !amountCryptoBaseUnit) {
2256
2324
  return [];
2257
2325
  }
2258
2326
  const response = await apiClient.getRates({
2259
2327
  sellAssetId,
2260
2328
  buyAssetId,
2261
- sellAmountCryptoBaseUnit
2329
+ ...isExactOutput2 ? { buyAmountCryptoBaseUnit: amountCryptoBaseUnit } : { sellAmountCryptoBaseUnit: amountCryptoBaseUnit }
2262
2330
  });
2263
2331
  let filteredRates = response.rates.filter(
2264
2332
  (rate) => !rate.error && rate.buyAmountCryptoBaseUnit !== "0" && ENABLED_SWAPPER_NAMES.has(rate.swapperName)
@@ -2266,16 +2334,15 @@ var useSwapRates = (apiClient, params) => {
2266
2334
  if (allowedSwapperNames?.length) {
2267
2335
  filteredRates = filteredRates.filter((rate) => allowedSwapperNames.includes(rate.swapperName));
2268
2336
  }
2269
- return filteredRates.map((rate, index) => ({
2270
- ...rate,
2271
- id: rate.id ?? `${rate.swapperName}-${index}`
2272
- })).sort((a, b) => {
2273
- const aAmount = bnOrZero(a.buyAmountCryptoBaseUnit);
2274
- const bAmount = bnOrZero(b.buyAmountCryptoBaseUnit);
2275
- return bAmount.minus(aAmount).toNumber();
2276
- });
2337
+ return sortRatesByValue(
2338
+ filteredRates.map((rate, index) => ({
2339
+ ...rate,
2340
+ id: rate.id ?? `${rate.swapperName}-${index}`
2341
+ })),
2342
+ isExactOutput2
2343
+ );
2277
2344
  },
2278
- enabled: enabled && !!sellAssetId && !!buyAssetId && !!sellAmountCryptoBaseUnit,
2345
+ enabled: enabled && !!sellAssetId && !!buyAssetId && !!amountCryptoBaseUnit,
2279
2346
  staleTime: 1e4,
2280
2347
  refetchInterval
2281
2348
  });
@@ -2287,18 +2354,23 @@ var useSwapDisplayValues = ({
2287
2354
  allowedSwapperNames,
2288
2355
  ratesRefetchInterval
2289
2356
  }) => {
2290
- const sellAsset = SwapMachineCtx.useSelector((s) => s.context.sellAsset);
2291
- const buyAsset = SwapMachineCtx.useSelector((s) => s.context.buyAsset);
2292
- const sellAmountBaseUnit = SwapMachineCtx.useSelector((s) => s.context.sellAmountBaseUnit);
2293
- const isSellAssetEvm = SwapMachineCtx.useSelector((s) => s.context.isSellAssetEvm);
2294
- const isSellAssetUtxo = SwapMachineCtx.useSelector((s) => s.context.isSellAssetUtxo);
2295
- const isSellAssetSolana = SwapMachineCtx.useSelector((s) => s.context.isSellAssetSolana);
2296
- const selectedRate = SwapMachineCtx.useSelector((s) => s.context.selectedRate);
2297
- const { receiveAddress, evm, bitcoin: bitcoin3, solana: solana3 } = useSwapWallet();
2357
+ const context = SwapMachineCtx.useSelector((s) => s.context);
2358
+ const {
2359
+ sellAsset,
2360
+ buyAsset,
2361
+ buyAmountBaseUnit,
2362
+ isSellAssetEvm,
2363
+ isSellAssetUtxo,
2364
+ isSellAssetSolana,
2365
+ selectedRate
2366
+ } = context;
2367
+ const { receiveAddress, isReceiveAddressBlocked, evm, bitcoin: bitcoin3, solana: solana3 } = useSwapWallet();
2298
2368
  const evmAddress = evm.address;
2299
2369
  const bitcoinAddress = bitcoin3.address;
2300
2370
  const solanaAddress = solana3.address;
2301
2371
  const buyChainType = getChainType(buyAsset.chainId);
2372
+ const isExactOutput2 = !!buyAmountBaseUnit;
2373
+ const amountBaseUnit = isExactOutput2 ? buyAmountBaseUnit : context.sellAmountBaseUnit;
2302
2374
  const {
2303
2375
  data: rates,
2304
2376
  isLoading: isLoadingRates,
@@ -2306,10 +2378,12 @@ var useSwapDisplayValues = ({
2306
2378
  } = useSwapRates(apiClient, {
2307
2379
  sellAssetId: sellAsset.assetId,
2308
2380
  buyAssetId: buyAsset.assetId,
2309
- sellAmountCryptoBaseUnit: sellAmountBaseUnit,
2381
+ sellAmountCryptoBaseUnit: context.sellAmountBaseUnit,
2382
+ buyAmountCryptoBaseUnit: buyAmountBaseUnit,
2310
2383
  allowedSwapperNames,
2311
2384
  refetchInterval: ratesRefetchInterval,
2312
- enabled: !!sellAmountBaseUnit && sellAmountBaseUnit !== "0" && (isSellAssetEvm || isSellAssetUtxo || isSellAssetSolana)
2385
+ // Rates need no destination, but a locked one the buy chain rejects can never be quoted
2386
+ enabled: !!amountBaseUnit && amountBaseUnit !== "0" && !isReceiveAddressBlocked && (isSellAssetEvm || isSellAssetUtxo || isSellAssetSolana)
2313
2387
  });
2314
2388
  const {
2315
2389
  data: sellAssetBalance,
@@ -2343,6 +2417,7 @@ var useSwapDisplayValues = ({
2343
2417
  const { data: buyChainInfo } = useChainInfo(buyAsset.chainId);
2344
2418
  const displayRate = useMemo5(() => selectedRate ?? rates?.[0], [selectedRate, rates]);
2345
2419
  const buyAmount = displayRate?.buyAmountCryptoBaseUnit;
2420
+ const sellAmountBaseUnit = isExactOutput2 ? displayRate?.sellAmountCryptoBaseUnit : context.sellAmountBaseUnit;
2346
2421
  const sellChainNativeAsset = useMemo5(() => getBaseAsset(sellAsset.chainId), [sellAsset.chainId]);
2347
2422
  const assetIdsForPrices = useMemo5(() => {
2348
2423
  const ids = [sellAsset.assetId, buyAsset.assetId];
@@ -2400,6 +2475,8 @@ var useSwapDisplayValues = ({
2400
2475
  buyChainInfo,
2401
2476
  displayRate,
2402
2477
  buyAmount,
2478
+ isExactOutput: isExactOutput2,
2479
+ sellAmountBaseUnit,
2403
2480
  sellChainNativeAsset,
2404
2481
  networkFeeDisplay,
2405
2482
  sellUsdValue,
@@ -2423,6 +2500,8 @@ var useSwapDisplayValues = ({
2423
2500
  buyChainInfo,
2424
2501
  displayRate,
2425
2502
  buyAmount,
2503
+ isExactOutput2,
2504
+ sellAmountBaseUnit,
2426
2505
  sellChainNativeAsset,
2427
2506
  networkFeeDisplay,
2428
2507
  sellUsdValue,
@@ -2553,6 +2632,10 @@ var useSwapExecution = () => {
2553
2632
  });
2554
2633
  return;
2555
2634
  }
2635
+ if (Date.now() >= quote.expiresAt) {
2636
+ actorRef.send({ type: "EXECUTE_ERROR", error: "Quote expired \u2014 please try again" });
2637
+ return;
2638
+ }
2556
2639
  const txData = quote.steps[0]?.transactionData;
2557
2640
  if (!txData) {
2558
2641
  console.error("Quote missing transactionData in steps[0]", quote);
@@ -2662,15 +2745,27 @@ var useSwapHandlers = ({
2662
2745
  },
2663
2746
  [actorRef]
2664
2747
  );
2748
+ const handleBuyAmountChange = useCallback4(
2749
+ (value) => {
2750
+ const { buyAsset } = actorRef.getSnapshot().context;
2751
+ const amountBaseUnit = value ? parseAmount(value, buyAsset.precision) : void 0;
2752
+ actorRef.send({ type: "SET_BUY_AMOUNT", amount: value, amountBaseUnit });
2753
+ },
2754
+ [actorRef]
2755
+ );
2665
2756
  const handleToggleSellFiat = useCallback4(
2666
2757
  (sellAssetUsdPrice) => {
2667
2758
  if (!sellAssetUsdPrice) return;
2668
2759
  const snap = actorRef.getSnapshot();
2669
- const { sellAmount, sellAmountBaseUnit, sellAsset, isSellAmountFiat } = snap.context;
2760
+ const { sellAmount, sellAmountBaseUnit, sellAsset, isSellAmountFiat, buyAmountBaseUnit } = snap.context;
2670
2761
  if (isSellAmountFiat) {
2671
2762
  actorRef.send({ type: "SET_SELL_FIAT_MODE", isFiat: false });
2672
2763
  return;
2673
2764
  }
2765
+ if (buyAmountBaseUnit) {
2766
+ actorRef.send({ type: "SET_SELL_FIAT_MODE", isFiat: true });
2767
+ return;
2768
+ }
2674
2769
  const fiatValue = cryptoToFiat(sellAmountBaseUnit, sellAssetUsdPrice, sellAsset.precision);
2675
2770
  actorRef.send({
2676
2771
  type: "SET_SELL_AMOUNT",
@@ -2746,6 +2841,7 @@ var useSwapHandlers = ({
2746
2841
  handleSellAssetSelect,
2747
2842
  handleBuyAssetSelect,
2748
2843
  handleSellAmountChange,
2844
+ handleBuyAmountChange,
2749
2845
  handleToggleSellFiat,
2750
2846
  handleSelectRate,
2751
2847
  handleSlippageChange,
@@ -2768,9 +2864,12 @@ var useSwapQuoting = ({ apiClient, rates, sellAssetBalance }) => {
2768
2864
  quotingRef.current = true;
2769
2865
  const fetchQuote = async () => {
2770
2866
  try {
2771
- if (sellAssetBalance?.balance && context.sellAmountBaseUnit) {
2867
+ const isExactOutput2 = !!context.buyAmountBaseUnit;
2868
+ const rateToUse = context.selectedRate ?? rates?.[0];
2869
+ const sellAmountBaseUnit = isExactOutput2 ? rateToUse?.sellAmountCryptoBaseUnit : context.sellAmountBaseUnit;
2870
+ if (sellAssetBalance?.balance && sellAmountBaseUnit) {
2772
2871
  const balanceBigInt = BigInt(sellAssetBalance.balance);
2773
- const amountBigInt = BigInt(context.sellAmountBaseUnit);
2872
+ const amountBigInt = BigInt(sellAmountBaseUnit);
2774
2873
  if (amountBigInt > balanceBigInt) {
2775
2874
  actorRef.send({ type: "QUOTE_ERROR", error: "Insufficient balance" });
2776
2875
  return;
@@ -2782,8 +2881,8 @@ var useSwapQuoting = ({ apiClient, rates, sellAssetBalance }) => {
2782
2881
  return;
2783
2882
  }
2784
2883
  const slippageDecimal = (parsedSlippage / 100).toString();
2785
- const rateToUse = context.selectedRate ?? rates?.[0];
2786
- if (!rateToUse || !context.sellAmountBaseUnit) {
2884
+ const amountBaseUnit = isExactOutput2 ? context.buyAmountBaseUnit : context.sellAmountBaseUnit;
2885
+ if (!rateToUse || !amountBaseUnit) {
2787
2886
  actorRef.send({ type: "QUOTE_ERROR", error: "No rate or amount available" });
2788
2887
  return;
2789
2888
  }
@@ -2799,7 +2898,7 @@ var useSwapQuoting = ({ apiClient, rates, sellAssetBalance }) => {
2799
2898
  const response = await apiClient.getQuote({
2800
2899
  sellAssetId: context.sellAsset.assetId,
2801
2900
  buyAssetId: context.buyAsset.assetId,
2802
- sellAmountCryptoBaseUnit: context.sellAmountBaseUnit,
2901
+ ...isExactOutput2 ? { buyAmountCryptoBaseUnit: amountBaseUnit } : { sellAmountCryptoBaseUnit: amountBaseUnit },
2803
2902
  sendAddress,
2804
2903
  receiveAddress: resolvedReceiveAddress,
2805
2904
  swapperName: rateToUse.swapperName,
@@ -2956,6 +3055,19 @@ var getAddressFormatHint = (chainId) => {
2956
3055
  }
2957
3056
  };
2958
3057
 
3058
+ // src/utils/receiveAddress.ts
3059
+ var resolveReceiveAddress = ({
3060
+ isLocked,
3061
+ defaultAddress,
3062
+ customAddress,
3063
+ walletAddress,
3064
+ buyChainId
3065
+ }) => {
3066
+ const isValidForBuyChain = (address) => !!address && validateAddress(address, buyChainId).valid;
3067
+ if (isLocked) return isValidForBuyChain(defaultAddress) ? defaultAddress : void 0;
3068
+ return isValidForBuyChain(customAddress) ? customAddress : walletAddress;
3069
+ };
3070
+
2959
3071
  // src/components/ApprovalStep.tsx
2960
3072
  import { jsx, jsxs } from "react/jsx-runtime";
2961
3073
  var ApprovalStep = () => {
@@ -3117,9 +3229,8 @@ var getSwapperColor = (swapperName) => {
3117
3229
  };
3118
3230
 
3119
3231
  // src/components/QuotesModal.tsx
3120
- import { bnOrZero as bnOrZero2 } from "@shapeshiftoss/utils";
3121
3232
  import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo6 } from "react";
3122
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3233
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3123
3234
  var useLockBodyScroll = (isLocked) => {
3124
3235
  useEffect6(() => {
3125
3236
  if (!isLocked) return;
@@ -3130,13 +3241,6 @@ var useLockBodyScroll = (isLocked) => {
3130
3241
  };
3131
3242
  }, [isLocked]);
3132
3243
  };
3133
- var calculateSavingsPercent = (bestAmount, currentAmount) => {
3134
- const best = bnOrZero2(bestAmount);
3135
- const current = bnOrZero2(currentAmount);
3136
- if (best.isZero()) return null;
3137
- const diff = best.minus(current).div(best).times(100);
3138
- return diff.gt(0.1) ? diff.toFixed(2) : null;
3139
- };
3140
3244
  var QuotesModal = ({
3141
3245
  isOpen,
3142
3246
  onClose,
@@ -3146,6 +3250,9 @@ var QuotesModal = ({
3146
3250
  buyAsset,
3147
3251
  sellAsset,
3148
3252
  sellAmountBaseUnit,
3253
+ buyAmountBaseUnit,
3254
+ isExactOutput: isExactOutput2,
3255
+ sellAssetUsdPrice,
3149
3256
  buyAssetUsdPrice
3150
3257
  }) => {
3151
3258
  useLockBodyScroll(isOpen);
@@ -3164,15 +3271,11 @@ var QuotesModal = ({
3164
3271
  },
3165
3272
  [onSelectRate, onClose]
3166
3273
  );
3167
- const sortedRates = useMemo6(() => {
3168
- return [...rates].filter((r) => !r.error && r.buyAmountCryptoBaseUnit !== "0").sort((a, b) => {
3169
- const aAmount = bnOrZero2(a.buyAmountCryptoBaseUnit);
3170
- const bAmount = bnOrZero2(b.buyAmountCryptoBaseUnit);
3171
- return bAmount.minus(aAmount).toNumber();
3172
- });
3173
- }, [rates]);
3274
+ const sortedRates = useMemo6(() => sortRatesByValue(rates, isExactOutput2), [rates, isExactOutput2]);
3174
3275
  const bestRate = useMemo6(() => sortedRates[0], [sortedRates]);
3175
- const bestBuyAmount = bestRate?.buyAmountCryptoBaseUnit ?? "0";
3276
+ const bestAmountBaseUnit = bestRate ? getRateAmountBaseUnit(bestRate, isExactOutput2) : "0";
3277
+ const asset = isExactOutput2 ? sellAsset : buyAsset;
3278
+ const usdPrice = isExactOutput2 ? sellAssetUsdPrice : buyAssetUsdPrice;
3176
3279
  if (!isOpen) return null;
3177
3280
  return (
3178
3281
  // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
@@ -3189,14 +3292,20 @@ var QuotesModal = ({
3189
3292
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quotes-modal-header", children: [
3190
3293
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quotes-header-content", children: [
3191
3294
  /* @__PURE__ */ jsx3("h2", { id: "quotes-modal-title", className: "ssw-quotes-modal-title", children: "Select Route" }),
3192
- /* @__PURE__ */ jsxs3("span", { className: "ssw-quotes-modal-subtitle", children: [
3295
+ /* @__PURE__ */ jsx3("span", { className: "ssw-quotes-modal-subtitle", children: isExactOutput2 ? /* @__PURE__ */ jsxs3(Fragment, { children: [
3296
+ sellAsset.symbol,
3297
+ " \u2192 ",
3298
+ formatAmount(buyAmountBaseUnit, buyAsset.precision),
3299
+ " ",
3300
+ buyAsset.symbol
3301
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
3193
3302
  formatAmount(sellAmountBaseUnit, sellAsset.precision),
3194
3303
  " ",
3195
3304
  sellAsset.symbol,
3196
3305
  " \u2192",
3197
3306
  " ",
3198
3307
  buyAsset.symbol
3199
- ] })
3308
+ ] }) })
3200
3309
  ] }),
3201
3310
  /* @__PURE__ */ jsx3("button", { className: "ssw-quotes-modal-close", onClick: onClose, type: "button", children: /* @__PURE__ */ jsx3(
3202
3311
  "svg",
@@ -3212,15 +3321,15 @@ var QuotesModal = ({
3212
3321
  ) })
3213
3322
  ] }),
3214
3323
  /* @__PURE__ */ jsx3("div", { className: "ssw-quotes-modal-list", children: sortedRates.map((rate, index) => {
3215
- const buyAmount = rate.buyAmountCryptoBaseUnit ?? "0";
3324
+ const amountBaseUnit = getRateAmountBaseUnit(rate, isExactOutput2);
3216
3325
  const estimatedTime = rate.estimatedExecutionTimeMs;
3217
3326
  const isBest = index === 0;
3218
3327
  const isSelected = selectedRate?.id === rate.id;
3219
3328
  const swapperIcon = getSwapperIcon(rate.swapperName);
3220
3329
  const swapperColor = getSwapperColor(rate.swapperName);
3221
- const formattedBuyAmount = formatAmount(buyAmount, buyAsset.precision);
3222
- const usdValue = formatUsdValue(buyAmount, buyAsset.precision, buyAssetUsdPrice);
3223
- const savingsPercent = isBest ? null : calculateSavingsPercent(bestBuyAmount, buyAmount);
3330
+ const formattedAmount = formatAmount(amountBaseUnit, asset.precision);
3331
+ const usdValue = formatUsdValue(amountBaseUnit, asset.precision, usdPrice);
3332
+ const diffPercent = isBest ? null : getRateDiffPercent(bestAmountBaseUnit, amountBaseUnit, isExactOutput2);
3224
3333
  const estimatedSeconds = estimatedTime ? Math.round(estimatedTime / 1e3) : 0;
3225
3334
  const hasTime = estimatedSeconds > 0;
3226
3335
  return /* @__PURE__ */ jsxs3(
@@ -3243,9 +3352,9 @@ var QuotesModal = ({
3243
3352
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quote-row-name-row", children: [
3244
3353
  /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-name", children: rate.swapperName }),
3245
3354
  isBest && /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-best", children: "Best" }),
3246
- savingsPercent && /* @__PURE__ */ jsxs3("span", { className: "ssw-quote-row-diff", children: [
3247
- "-",
3248
- savingsPercent,
3355
+ diffPercent && /* @__PURE__ */ jsxs3("span", { className: "ssw-quote-row-diff", children: [
3356
+ isExactOutput2 ? "+" : "-",
3357
+ diffPercent,
3249
3358
  "%"
3250
3359
  ] })
3251
3360
  ] }),
@@ -3258,9 +3367,9 @@ var QuotesModal = ({
3258
3367
  ] }),
3259
3368
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quote-row-right", children: [
3260
3369
  /* @__PURE__ */ jsxs3("span", { className: "ssw-quote-row-amount", children: [
3261
- formattedBuyAmount,
3370
+ formattedAmount,
3262
3371
  " ",
3263
- /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-symbol", children: buyAsset.symbol })
3372
+ /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-symbol", children: asset.symbol })
3264
3373
  ] }),
3265
3374
  /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-usd", children: usdValue })
3266
3375
  ] })
@@ -3276,7 +3385,7 @@ var QuotesModal = ({
3276
3385
  };
3277
3386
 
3278
3387
  // src/components/QuoteSelector.tsx
3279
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3388
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3280
3389
  var QuoteSelector = ({
3281
3390
  rates,
3282
3391
  selectedRate,
@@ -3284,7 +3393,10 @@ var QuoteSelector = ({
3284
3393
  buyAsset,
3285
3394
  sellAsset,
3286
3395
  sellAmountBaseUnit,
3396
+ buyAmountBaseUnit,
3397
+ isExactOutput: isExactOutput2,
3287
3398
  isLoading,
3399
+ sellAssetUsdPrice,
3288
3400
  buyAssetUsdPrice
3289
3401
  }) => {
3290
3402
  const [isModalOpen, setIsModalOpen] = useState3(false);
@@ -3314,12 +3426,17 @@ var QuoteSelector = ({
3314
3426
  return null;
3315
3427
  }
3316
3428
  const displayRate = selectedRate ?? bestRate;
3317
- const buyAmount = displayRate.buyAmountCryptoBaseUnit ?? "0";
3318
3429
  const swapperIcon = getSwapperIcon(displayRate.swapperName);
3319
3430
  const swapperColor = getSwapperColor(displayRate.swapperName);
3320
- const formattedBuyAmount = formatAmount(buyAmount, buyAsset.precision);
3321
- const usdValue = formatUsdValue(buyAmount, buyAsset.precision, buyAssetUsdPrice);
3322
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
3431
+ const asset = isExactOutput2 ? sellAsset : buyAsset;
3432
+ const amountBaseUnit = getRateAmountBaseUnit(displayRate, isExactOutput2);
3433
+ const formattedAmount = formatAmount(amountBaseUnit, asset.precision);
3434
+ const usdValue = formatUsdValue(
3435
+ amountBaseUnit,
3436
+ asset.precision,
3437
+ isExactOutput2 ? sellAssetUsdPrice : buyAssetUsdPrice
3438
+ );
3439
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
3323
3440
  /* @__PURE__ */ jsxs4("button", { className: "ssw-quote-selector", onClick: handleOpenModal, type: "button", children: [
3324
3441
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-left", children: [
3325
3442
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-provider", children: [
@@ -3345,8 +3462,8 @@ var QuoteSelector = ({
3345
3462
  ] }),
3346
3463
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-right", children: [
3347
3464
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-amount-row", children: [
3348
- /* @__PURE__ */ jsx4("span", { className: "ssw-quote-amount", children: formattedBuyAmount }),
3349
- /* @__PURE__ */ jsx4("span", { className: "ssw-quote-symbol", children: buyAsset.symbol })
3465
+ /* @__PURE__ */ jsx4("span", { className: "ssw-quote-amount", children: formattedAmount }),
3466
+ /* @__PURE__ */ jsx4("span", { className: "ssw-quote-symbol", children: asset.symbol })
3350
3467
  ] }),
3351
3468
  alternativeRatesCount > 0 && /* @__PURE__ */ jsxs4("span", { className: "ssw-quote-more", children: [
3352
3469
  "+",
@@ -3378,6 +3495,9 @@ var QuoteSelector = ({
3378
3495
  buyAsset,
3379
3496
  sellAsset,
3380
3497
  sellAmountBaseUnit,
3498
+ buyAmountBaseUnit,
3499
+ isExactOutput: isExactOutput2,
3500
+ sellAssetUsdPrice,
3381
3501
  buyAssetUsdPrice
3382
3502
  }
3383
3503
  )
@@ -3386,11 +3506,12 @@ var QuoteSelector = ({
3386
3506
 
3387
3507
  // src/components/ReceiveAddressRow.tsx
3388
3508
  import { useCallback as useCallback7, useLayoutEffect, useMemo as useMemo8, useRef as useRef5, useState as useState4 } from "react";
3389
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3509
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3390
3510
  var ReceiveAddressRow = ({
3391
3511
  receiveAddress,
3392
3512
  isResolving,
3393
3513
  buyChainId,
3514
+ isLocked,
3394
3515
  onSetCustomReceiveAddress
3395
3516
  }) => {
3396
3517
  const [isEditing, setIsEditing] = useState4(false);
@@ -3398,7 +3519,7 @@ var ReceiveAddressRow = ({
3398
3519
  const [hasInteracted, setHasInteracted] = useState4(false);
3399
3520
  const inputRef = useRef5(null);
3400
3521
  const needsAddress = !receiveAddress;
3401
- const showInput = needsAddress && !isResolving || isEditing;
3522
+ const showInput = !isLocked && (needsAddress && !isResolving || isEditing);
3402
3523
  const showAttention = needsAddress && !isResolving;
3403
3524
  useLayoutEffect(() => {
3404
3525
  if (isEditing) inputRef.current?.focus();
@@ -3418,6 +3539,7 @@ var ReceiveAddressRow = ({
3418
3539
  [trimmedDraft, buyChainId]
3419
3540
  );
3420
3541
  const formatHint = useMemo8(() => getAddressFormatHint(buyChainId), [buyChainId]);
3542
+ const chainName = useMemo8(() => getChainName(buyChainId), [buyChainId]);
3421
3543
  const startEditing = useCallback7(() => {
3422
3544
  setDraft(receiveAddress ?? "");
3423
3545
  setHasInteracted(false);
@@ -3441,36 +3563,48 @@ var ReceiveAddressRow = ({
3441
3563
  }, [onSetCustomReceiveAddress]);
3442
3564
  const showReset = !!draft || isEditing;
3443
3565
  if (!showInput) {
3444
- return /* @__PURE__ */ jsxs5("div", { className: "ssw-receive-row ssw-receive-row-resolved", children: [
3445
- /* @__PURE__ */ jsx5("span", { className: "ssw-receive-label", children: "Receive address" }),
3446
- /* @__PURE__ */ jsx5("div", { className: "ssw-receive-resolved-value", children: isResolving ? /* @__PURE__ */ jsx5("span", { className: "ssw-balance-skeleton" }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
3447
- /* @__PURE__ */ jsx5("span", { className: "ssw-receive-address", children: truncateAddress(receiveAddress ?? "", 6) }),
3448
- /* @__PURE__ */ jsx5(
3449
- "button",
3450
- {
3451
- className: "ssw-receive-edit-btn",
3452
- onClick: startEditing,
3453
- type: "button",
3454
- "aria-label": "Edit receive address",
3455
- children: /* @__PURE__ */ jsxs5(
3456
- "svg",
3566
+ return /* @__PURE__ */ jsxs5(
3567
+ "div",
3568
+ {
3569
+ className: `ssw-receive-row ssw-receive-row-resolved${showAttention ? " ssw-receive-row-invalid" : ""}`,
3570
+ children: [
3571
+ /* @__PURE__ */ jsx5("span", { className: "ssw-receive-label", children: "Receive address" }),
3572
+ /* @__PURE__ */ jsx5("div", { className: "ssw-receive-resolved-value", children: isResolving ? /* @__PURE__ */ jsx5("span", { className: "ssw-balance-skeleton" }) : showAttention ? (
3573
+ // Locked only - an unlocked row with no address shows the input instead
3574
+ /* @__PURE__ */ jsxs5("span", { className: "ssw-receive-error", children: [
3575
+ "Not valid for ",
3576
+ chainName
3577
+ ] })
3578
+ ) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
3579
+ /* @__PURE__ */ jsx5("span", { className: "ssw-receive-address", children: truncateAddress(receiveAddress ?? "", 6) }),
3580
+ !isLocked && /* @__PURE__ */ jsx5(
3581
+ "button",
3457
3582
  {
3458
- width: "14",
3459
- height: "14",
3460
- viewBox: "0 0 24 24",
3461
- fill: "none",
3462
- stroke: "currentColor",
3463
- strokeWidth: "2",
3464
- children: [
3465
- /* @__PURE__ */ jsx5("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
3466
- /* @__PURE__ */ jsx5("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
3467
- ]
3583
+ className: "ssw-receive-edit-btn",
3584
+ onClick: startEditing,
3585
+ type: "button",
3586
+ "aria-label": "Edit receive address",
3587
+ children: /* @__PURE__ */ jsxs5(
3588
+ "svg",
3589
+ {
3590
+ width: "14",
3591
+ height: "14",
3592
+ viewBox: "0 0 24 24",
3593
+ fill: "none",
3594
+ stroke: "currentColor",
3595
+ strokeWidth: "2",
3596
+ children: [
3597
+ /* @__PURE__ */ jsx5("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
3598
+ /* @__PURE__ */ jsx5("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
3599
+ ]
3600
+ }
3601
+ )
3468
3602
  }
3469
3603
  )
3470
- }
3471
- )
3472
- ] }) })
3473
- ] });
3604
+ ] }) })
3605
+ ]
3606
+ }
3607
+ );
3474
3608
  }
3475
3609
  return /* @__PURE__ */ jsxs5(
3476
3610
  "div",
@@ -3554,39 +3688,23 @@ var ReceiveAddressRow = ({
3554
3688
  };
3555
3689
 
3556
3690
  // src/components/InputStep.tsx
3557
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3691
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3558
3692
  var InputStep = ({
3559
3693
  displayValues,
3560
3694
  onOpenTokenModal,
3561
3695
  onSellAmountChange,
3696
+ onBuyAmountChange,
3562
3697
  onToggleSellFiat,
3563
3698
  onSwapTokens,
3564
3699
  onSelectRate,
3565
3700
  onButtonClick,
3566
3701
  isBuyAssetLocked,
3702
+ isBuyAmountLocked,
3703
+ isReceiveAddressLocked,
3567
3704
  allowShapeshiftRedirect
3568
3705
  }) => {
3569
3706
  const context = SwapMachineCtx.useSelector((s) => s.context);
3570
3707
  const isQuoting = SwapMachineCtx.useSelector((s) => s.matches("quoting"));
3571
- const {
3572
- rates,
3573
- isLoadingRates,
3574
- ratesError,
3575
- sellAssetBalance,
3576
- buyAssetBalance,
3577
- isSellBalanceLoading,
3578
- isBuyBalanceLoading,
3579
- sellUsdValue,
3580
- sellAssetUsdPrice,
3581
- buyUsdValue,
3582
- sellChainInfo,
3583
- buyChainInfo,
3584
- buyAmount,
3585
- buyAssetUsdPrice,
3586
- networkFeeDisplay,
3587
- sellBalanceFiatValue,
3588
- buyBalanceFiatValue
3589
- } = displayValues;
3590
3708
  const {
3591
3709
  sendAddress,
3592
3710
  receiveAddress,
@@ -3596,22 +3714,48 @@ var InputStep = ({
3596
3714
  bitcoin: bitcoin3,
3597
3715
  solana: solana3
3598
3716
  } = useSwapWallet();
3599
- const {
3600
- sellAsset,
3601
- buyAsset,
3602
- selectedRate,
3603
- sellAmount,
3604
- sellAmountBaseUnit,
3605
- isSellAmountFiat,
3606
- sellAmountFiat,
3607
- isSellAssetEvm,
3608
- isSellAssetUtxo,
3609
- isSellAssetSolana
3610
- } = context;
3611
- const buyChainId = buyAsset.chainId;
3717
+ const buyChainId = context.buyAsset.chainId;
3612
3718
  const hasAnyWalletAddress = !!evm.address || !!bitcoin3.address || !!solana3.address;
3613
3719
  const hasActiveWallet = !!receiveAddress || hasAnyWalletAddress || isReceiveAddressResolving;
3614
- const isUnsupportedChain = !isSellAssetEvm && !isSellAssetUtxo && !isSellAssetSolana;
3720
+ const isUnsupportedChain = !context.isSellAssetEvm && !context.isSellAssetUtxo && !context.isSellAssetSolana;
3721
+ const amountBaseUnit = displayValues.isExactOutput ? context.buyAmountBaseUnit : context.sellAmountBaseUnit;
3722
+ const isSellAmountReadOnly = displayValues.isExactOutput && isBuyAmountLocked;
3723
+ const isSellAmountPending = displayValues.isExactOutput && displayValues.isLoadingRates;
3724
+ const isBuyAmountPending = !displayValues.isExactOutput && displayValues.isLoadingRates;
3725
+ const sellAmountCrypto = useMemo9(
3726
+ () => displayValues.sellAmountBaseUnit ? formatAmount(displayValues.sellAmountBaseUnit, context.sellAsset.precision, 6) : "",
3727
+ [displayValues.sellAmountBaseUnit, context.sellAsset.precision]
3728
+ );
3729
+ const buyAmountValue = useMemo9(() => {
3730
+ if (displayValues.isExactOutput) return context.buyAmount;
3731
+ if (!displayValues.buyAmount) return "";
3732
+ return formatAmount(displayValues.buyAmount, context.buyAsset.precision);
3733
+ }, [
3734
+ displayValues.isExactOutput,
3735
+ displayValues.buyAmount,
3736
+ context.buyAmount,
3737
+ context.buyAsset.precision
3738
+ ]);
3739
+ const sellAmountValue = useMemo9(() => {
3740
+ if (!displayValues.isExactOutput) {
3741
+ return context.isSellAmountFiat ? context.sellAmountFiat : context.sellAmount;
3742
+ }
3743
+ if (!context.isSellAmountFiat) return sellAmountCrypto;
3744
+ return cryptoToFiat(
3745
+ displayValues.sellAmountBaseUnit,
3746
+ displayValues.sellAssetUsdPrice ?? "",
3747
+ context.sellAsset.precision
3748
+ );
3749
+ }, [
3750
+ displayValues.isExactOutput,
3751
+ displayValues.sellAmountBaseUnit,
3752
+ displayValues.sellAssetUsdPrice,
3753
+ context.isSellAmountFiat,
3754
+ context.sellAmountFiat,
3755
+ context.sellAmount,
3756
+ context.sellAsset.precision,
3757
+ sellAmountCrypto
3758
+ ]);
3615
3759
  const { text: buttonText, disabled: isButtonDisabled } = useMemo9(() => {
3616
3760
  if (isUnsupportedChain) {
3617
3761
  if (!allowShapeshiftRedirect) return { text: "Route not supported", disabled: true };
@@ -3619,39 +3763,43 @@ var InputStep = ({
3619
3763
  }
3620
3764
  if (!sendAddress) return { text: "Connect Wallet", disabled: false };
3621
3765
  if (!receiveAddress) return { text: "Enter receive address", disabled: true };
3622
- if (!sellAmount) return { text: "Enter an amount", disabled: true };
3623
- if (isLoadingRates) return { text: "Finding rates...", disabled: true };
3624
- if (ratesError) return { text: "No routes available", disabled: true };
3625
- if (!rates?.length) return { text: "No routes found", disabled: true };
3766
+ const drivingAmount = displayValues.isExactOutput ? context.buyAmountBaseUnit : context.sellAmount;
3767
+ if (!drivingAmount || drivingAmount === "0") return { text: "Enter an amount", disabled: true };
3768
+ if (displayValues.isLoadingRates) return { text: "Finding rates...", disabled: true };
3769
+ if (displayValues.ratesError) return { text: "No routes available", disabled: true };
3770
+ if (!displayValues.rates?.length) return { text: "No routes found", disabled: true };
3626
3771
  return { text: "Swap", disabled: false };
3627
3772
  }, [
3628
3773
  isUnsupportedChain,
3629
3774
  allowShapeshiftRedirect,
3630
3775
  sendAddress,
3631
3776
  receiveAddress,
3632
- sellAmount,
3633
- isLoadingRates,
3634
- ratesError,
3635
- rates
3777
+ context.sellAmount,
3778
+ displayValues.isExactOutput,
3779
+ context.buyAmountBaseUnit,
3780
+ displayValues.isLoadingRates,
3781
+ displayValues.ratesError,
3782
+ displayValues.rates
3636
3783
  ]);
3637
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
3784
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
3638
3785
  /* @__PURE__ */ jsxs6("div", { className: "ssw-swap-container", children: [
3639
3786
  /* @__PURE__ */ jsxs6("div", { className: "ssw-token-section ssw-sell", children: [
3640
3787
  /* @__PURE__ */ jsx6("div", { className: "ssw-section-header", children: /* @__PURE__ */ jsx6("span", { className: "ssw-section-label", children: "Sell" }) }),
3641
3788
  /* @__PURE__ */ jsxs6("div", { className: "ssw-input-row", children: [
3642
- isSellAmountFiat && /* @__PURE__ */ jsx6("span", { className: "ssw-fiat-prefix", children: "$" }),
3789
+ context.isSellAmountFiat && /* @__PURE__ */ jsx6("span", { className: "ssw-fiat-prefix", children: "$" }),
3643
3790
  /* @__PURE__ */ jsx6(
3644
3791
  "input",
3645
3792
  {
3646
3793
  type: "text",
3647
- className: "ssw-amount-input",
3648
- placeholder: "0",
3649
- value: isSellAmountFiat ? sellAmountFiat : sellAmount,
3794
+ className: `ssw-amount-input${isSellAmountReadOnly ? " ssw-amount-input-locked" : ""}${isSellAmountPending ? " ssw-amount-input-pending" : ""}`,
3795
+ placeholder: isSellAmountPending ? "..." : "0",
3796
+ value: sellAmountValue,
3797
+ readOnly: isSellAmountReadOnly,
3650
3798
  onChange: (e) => {
3651
3799
  const raw = e.target.value.replace(/[^0-9.]/g, "");
3652
3800
  const parts = raw.split(".");
3653
3801
  const sanitized = parts.length > 2 ? parts[0] + "." + parts.slice(1).join("") : raw;
3654
- onSellAmountChange(sanitized, sellAssetUsdPrice);
3802
+ onSellAmountChange(sanitized, displayValues.sellAssetUsdPrice);
3655
3803
  }
3656
3804
  }
3657
3805
  ),
@@ -3662,10 +3810,17 @@ var InputStep = ({
3662
3810
  onClick: () => onOpenTokenModal("sell"),
3663
3811
  type: "button",
3664
3812
  children: [
3665
- sellAsset.icon ? /* @__PURE__ */ jsx6("img", { src: sellAsset.icon, alt: sellAsset.symbol, className: "ssw-token-icon" }) : /* @__PURE__ */ jsx6("div", { className: "ssw-token-icon-placeholder", children: sellAsset.symbol.charAt(0) }),
3813
+ context.sellAsset.icon ? /* @__PURE__ */ jsx6(
3814
+ "img",
3815
+ {
3816
+ src: context.sellAsset.icon,
3817
+ alt: context.sellAsset.symbol,
3818
+ className: "ssw-token-icon"
3819
+ }
3820
+ ) : /* @__PURE__ */ jsx6("div", { className: "ssw-token-icon-placeholder", children: context.sellAsset.symbol.charAt(0) }),
3666
3821
  /* @__PURE__ */ jsxs6("div", { className: "ssw-token-info", children: [
3667
- /* @__PURE__ */ jsx6("span", { className: "ssw-token-symbol", children: sellAsset.symbol }),
3668
- /* @__PURE__ */ jsx6("span", { className: "ssw-token-chain", children: sellChainInfo?.name ?? sellAsset.networkName ?? sellAsset.name })
3822
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-symbol", children: context.sellAsset.symbol }),
3823
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-chain", children: displayValues.sellChainInfo?.name ?? context.sellAsset.networkName ?? context.sellAsset.name })
3669
3824
  ] }),
3670
3825
  /* @__PURE__ */ jsx6(
3671
3826
  "svg",
@@ -3684,14 +3839,14 @@ var InputStep = ({
3684
3839
  )
3685
3840
  ] }),
3686
3841
  /* @__PURE__ */ jsxs6("div", { className: "ssw-section-footer", children: [
3687
- sellAssetUsdPrice ? /* @__PURE__ */ jsxs6(
3842
+ !displayValues.sellAssetUsdPrice ? /* @__PURE__ */ jsx6("span", { className: "ssw-usd-value" }) : /* @__PURE__ */ jsxs6(
3688
3843
  "button",
3689
3844
  {
3690
3845
  type: "button",
3691
3846
  className: "ssw-usd-value ssw-usd-value-toggle",
3692
- onClick: () => onToggleSellFiat(sellAssetUsdPrice),
3847
+ onClick: () => onToggleSellFiat(displayValues.sellAssetUsdPrice),
3693
3848
  children: [
3694
- /* @__PURE__ */ jsx6("span", { children: isSellAmountFiat ? `\u2248 ${sellAmountBaseUnit ? formatAmount(sellAmountBaseUnit, sellAsset.precision, 6) : "0"} ${sellAsset.symbol}` : sellUsdValue }),
3849
+ /* @__PURE__ */ jsx6("span", { children: context.isSellAmountFiat ? `\u2248 ${sellAmountCrypto || "0"} ${context.sellAsset.symbol}` : displayValues.sellUsdValue }),
3695
3850
  /* @__PURE__ */ jsx6(
3696
3851
  "svg",
3697
3852
  {
@@ -3707,13 +3862,14 @@ var InputStep = ({
3707
3862
  )
3708
3863
  ]
3709
3864
  }
3710
- ) : /* @__PURE__ */ jsx6("span", { className: "ssw-usd-value" }),
3711
- hasAnyWalletAddress && (isSellBalanceLoading ? /* @__PURE__ */ jsx6("span", { className: "ssw-balance-skeleton" }) : sellAssetBalance ? /* @__PURE__ */ jsxs6("span", { className: "ssw-balance", children: [
3865
+ ),
3866
+ hasAnyWalletAddress && (displayValues.isSellBalanceLoading ? /* @__PURE__ */ jsx6("span", { className: "ssw-balance-skeleton" }) : displayValues.sellAssetBalance ? /* @__PURE__ */ jsxs6("span", { className: "ssw-balance", children: [
3712
3867
  "Balance: ",
3713
- sellAssetBalance.balanceFormatted,
3714
- sellBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3715
- " (",
3716
- sellBalanceFiatValue,
3868
+ displayValues.sellAssetBalance.balanceFormatted,
3869
+ displayValues.sellBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3870
+ " ",
3871
+ "(",
3872
+ displayValues.sellBalanceFiatValue,
3717
3873
  ")"
3718
3874
  ] })
3719
3875
  ] }) : null)
@@ -3747,10 +3903,15 @@ var InputStep = ({
3747
3903
  "input",
3748
3904
  {
3749
3905
  type: "text",
3750
- className: "ssw-amount-input",
3751
- placeholder: "0",
3752
- value: buyAmount ? formatAmount(buyAmount, buyAsset.precision) : "",
3753
- readOnly: true
3906
+ className: `ssw-amount-input${isBuyAmountLocked ? " ssw-amount-input-locked" : ""}${isBuyAmountPending ? " ssw-amount-input-pending" : ""}`,
3907
+ placeholder: isBuyAmountPending ? "..." : "0",
3908
+ value: buyAmountValue,
3909
+ readOnly: isBuyAmountLocked,
3910
+ onChange: (e) => {
3911
+ const raw = e.target.value.replace(/[^0-9.]/g, "");
3912
+ const parts = raw.split(".");
3913
+ onBuyAmountChange(parts.length > 2 ? parts[0] + "." + parts.slice(1).join("") : raw);
3914
+ }
3754
3915
  }
3755
3916
  ),
3756
3917
  /* @__PURE__ */ jsxs6(
@@ -3761,10 +3922,17 @@ var InputStep = ({
3761
3922
  disabled: isBuyAssetLocked,
3762
3923
  type: "button",
3763
3924
  children: [
3764
- buyAsset.icon ? /* @__PURE__ */ jsx6("img", { src: buyAsset.icon, alt: buyAsset.symbol, className: "ssw-token-icon" }) : /* @__PURE__ */ jsx6("div", { className: "ssw-token-icon-placeholder", children: buyAsset.symbol.charAt(0) }),
3925
+ context.buyAsset.icon ? /* @__PURE__ */ jsx6(
3926
+ "img",
3927
+ {
3928
+ src: context.buyAsset.icon,
3929
+ alt: context.buyAsset.symbol,
3930
+ className: "ssw-token-icon"
3931
+ }
3932
+ ) : /* @__PURE__ */ jsx6("div", { className: "ssw-token-icon-placeholder", children: context.buyAsset.symbol.charAt(0) }),
3765
3933
  /* @__PURE__ */ jsxs6("div", { className: "ssw-token-info", children: [
3766
- /* @__PURE__ */ jsx6("span", { className: "ssw-token-symbol", children: buyAsset.symbol }),
3767
- /* @__PURE__ */ jsx6("span", { className: "ssw-token-chain", children: buyChainInfo?.name ?? buyAsset.networkName ?? buyAsset.name })
3934
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-symbol", children: context.buyAsset.symbol }),
3935
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-chain", children: displayValues.buyChainInfo?.name ?? context.buyAsset.networkName ?? context.buyAsset.name })
3768
3936
  ] }),
3769
3937
  !isBuyAssetLocked && /* @__PURE__ */ jsx6(
3770
3938
  "svg",
@@ -3783,13 +3951,13 @@ var InputStep = ({
3783
3951
  )
3784
3952
  ] }),
3785
3953
  /* @__PURE__ */ jsxs6("div", { className: "ssw-section-footer", children: [
3786
- /* @__PURE__ */ jsx6("span", { className: "ssw-usd-value", children: buyUsdValue }),
3787
- hasAnyWalletAddress && (isBuyBalanceLoading ? /* @__PURE__ */ jsx6("span", { className: "ssw-balance-skeleton" }) : buyAssetBalance ? /* @__PURE__ */ jsxs6("span", { className: "ssw-balance", children: [
3954
+ /* @__PURE__ */ jsx6("span", { className: "ssw-usd-value", children: displayValues.buyUsdValue }),
3955
+ hasAnyWalletAddress && (displayValues.isBuyBalanceLoading ? /* @__PURE__ */ jsx6("span", { className: "ssw-balance-skeleton" }) : displayValues.buyAssetBalance ? /* @__PURE__ */ jsxs6("span", { className: "ssw-balance", children: [
3788
3956
  "Balance: ",
3789
- buyAssetBalance.balanceFormatted,
3790
- buyBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3957
+ displayValues.buyAssetBalance.balanceFormatted,
3958
+ displayValues.buyBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3791
3959
  " (",
3792
- buyBalanceFiatValue,
3960
+ displayValues.buyBalanceFiatValue,
3793
3961
  ")"
3794
3962
  ] })
3795
3963
  ] }) : null)
@@ -3801,26 +3969,30 @@ var InputStep = ({
3801
3969
  receiveAddress,
3802
3970
  isResolving: isReceiveAddressResolving,
3803
3971
  buyChainId,
3972
+ isLocked: isReceiveAddressLocked,
3804
3973
  onSetCustomReceiveAddress: setCustomReceiveAddress
3805
3974
  }
3806
3975
  )
3807
3976
  ] }),
3808
- sellAmountBaseUnit && sellAmountBaseUnit !== "0" && (rates?.length || isLoadingRates) && /* @__PURE__ */ jsx6("div", { className: "ssw-quotes", children: /* @__PURE__ */ jsx6(
3977
+ amountBaseUnit && amountBaseUnit !== "0" && (displayValues.rates?.length || displayValues.isLoadingRates) && /* @__PURE__ */ jsx6("div", { className: "ssw-quotes", children: /* @__PURE__ */ jsx6(
3809
3978
  QuoteSelector,
3810
3979
  {
3811
- rates: rates ?? [],
3812
- selectedRate,
3980
+ rates: displayValues.rates ?? [],
3981
+ selectedRate: context.selectedRate,
3813
3982
  onSelectRate,
3814
- buyAsset,
3815
- sellAsset,
3816
- sellAmountBaseUnit,
3817
- isLoading: isLoadingRates,
3818
- buyAssetUsdPrice
3983
+ buyAsset: context.buyAsset,
3984
+ sellAsset: context.sellAsset,
3985
+ sellAmountBaseUnit: displayValues.sellAmountBaseUnit ?? "0",
3986
+ buyAmountBaseUnit: context.buyAmountBaseUnit ?? "0",
3987
+ isExactOutput: displayValues.isExactOutput,
3988
+ isLoading: displayValues.isLoadingRates,
3989
+ sellAssetUsdPrice: displayValues.sellAssetUsdPrice,
3990
+ buyAssetUsdPrice: displayValues.buyAssetUsdPrice
3819
3991
  }
3820
3992
  ) }),
3821
- networkFeeDisplay && /* @__PURE__ */ jsxs6("div", { className: "ssw-network-fee", children: [
3993
+ displayValues.networkFeeDisplay && /* @__PURE__ */ jsxs6("div", { className: "ssw-network-fee", children: [
3822
3994
  /* @__PURE__ */ jsx6("span", { className: "ssw-network-fee-label", children: "Est. network fee" }),
3823
- /* @__PURE__ */ jsx6("span", { className: "ssw-network-fee-value", children: networkFeeDisplay })
3995
+ /* @__PURE__ */ jsx6("span", { className: "ssw-network-fee-value", children: displayValues.networkFeeDisplay })
3824
3996
  ] }),
3825
3997
  /* @__PURE__ */ jsx6(
3826
3998
  "button",
@@ -3830,7 +4002,7 @@ var InputStep = ({
3830
4002
  onClick: onButtonClick,
3831
4003
  type: "button",
3832
4004
  style: isQuoting ? { opacity: 0.7 } : void 0,
3833
- children: isQuoting ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
4005
+ children: isQuoting ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
3834
4006
  /* @__PURE__ */ jsxs6(
3835
4007
  "svg",
3836
4008
  {
@@ -4017,7 +4189,7 @@ var SettingsModal = ({ isOpen, onClose, onSlippageChange }) => {
4017
4189
 
4018
4190
  // src/components/StatusStep.tsx
4019
4191
  import { useMemo as useMemo10 } from "react";
4020
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
4192
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
4021
4193
  var ExplorerLink = ({ url }) => /* @__PURE__ */ jsxs8("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "ssw-step-explorer-link", children: [
4022
4194
  "View on Explorer",
4023
4195
  /* @__PURE__ */ jsx8(
@@ -4033,7 +4205,7 @@ var ExplorerLink = ({ url }) => /* @__PURE__ */ jsxs8("a", { href: url, target:
4033
4205
  }
4034
4206
  )
4035
4207
  ] });
4036
- var StatusStep = () => {
4208
+ var StatusStep = ({ isPayment }) => {
4037
4209
  const context = SwapMachineCtx.useSelector((s) => s.context);
4038
4210
  const isPolling = SwapMachineCtx.useSelector((s) => s.matches("polling_status"));
4039
4211
  const isComplete = SwapMachineCtx.useSelector((s) => s.matches("complete"));
@@ -4051,7 +4223,7 @@ var StatusStep = () => {
4051
4223
  [error]
4052
4224
  );
4053
4225
  return /* @__PURE__ */ jsxs8("div", { className: "ssw-step-screen", children: [
4054
- isPolling && /* @__PURE__ */ jsxs8(Fragment4, { children: [
4226
+ isPolling && /* @__PURE__ */ jsxs8(Fragment5, { children: [
4055
4227
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-icon-circle ssw-ic-accent", children: /* @__PURE__ */ jsxs8(
4056
4228
  "svg",
4057
4229
  {
@@ -4072,7 +4244,7 @@ var StatusStep = () => {
4072
4244
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-subtitle", children: "Your swap is being processed\u2026" }),
4073
4245
  explorerUrl && /* @__PURE__ */ jsx8(ExplorerLink, { url: explorerUrl })
4074
4246
  ] }),
4075
- isComplete && /* @__PURE__ */ jsxs8(Fragment4, { children: [
4247
+ isComplete && /* @__PURE__ */ jsxs8(Fragment5, { children: [
4076
4248
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-icon-circle ssw-ic-success", children: /* @__PURE__ */ jsx8(
4077
4249
  "svg",
4078
4250
  {
@@ -4093,7 +4265,7 @@ var StatusStep = () => {
4093
4265
  buyAsset.symbol
4094
4266
  ] }),
4095
4267
  explorerUrl && /* @__PURE__ */ jsx8(ExplorerLink, { url: explorerUrl }),
4096
- /* @__PURE__ */ jsx8("div", { className: "ssw-step-actions", children: /* @__PURE__ */ jsx8(
4268
+ !isPayment && /* @__PURE__ */ jsx8("div", { className: "ssw-step-actions", children: /* @__PURE__ */ jsx8(
4097
4269
  "button",
4098
4270
  {
4099
4271
  className: "ssw-action-btn",
@@ -4103,7 +4275,7 @@ var StatusStep = () => {
4103
4275
  }
4104
4276
  ) })
4105
4277
  ] }),
4106
- isError && /* @__PURE__ */ jsxs8(Fragment4, { children: [
4278
+ isError && /* @__PURE__ */ jsxs8(Fragment5, { children: [
4107
4279
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-icon-circle ssw-ic-error", children: /* @__PURE__ */ jsxs8(
4108
4280
  "svg",
4109
4281
  {
@@ -4146,10 +4318,10 @@ var StatusStep = () => {
4146
4318
  };
4147
4319
 
4148
4320
  // src/components/TokenSelectModal.tsx
4149
- import { bnOrZero as bnOrZero3 } from "@shapeshiftoss/utils";
4321
+ import { bnOrZero as bnOrZero2 } from "@shapeshiftoss/utils";
4150
4322
  import { useCallback as useCallback9, useEffect as useEffect8, useMemo as useMemo11, useRef as useRef6, useState as useState6 } from "react";
4151
4323
  import { Virtuoso } from "react-virtuoso";
4152
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
4324
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
4153
4325
  var VISIBLE_BUFFER = 10;
4154
4326
  var useLockBodyScroll3 = (isLocked) => {
4155
4327
  useEffect8(() => {
@@ -4330,7 +4502,7 @@ var TokenSelectModal = ({
4330
4502
  const balance = balances[asset.assetId];
4331
4503
  if (balance && balance.balance !== "0") {
4332
4504
  const price = marketData?.[asset.assetId]?.price;
4333
- const fiatValue = price ? bnOrZero3(balance.balance).div(bnOrZero3(10).pow(asset.precision)).times(bnOrZero3(price)).toNumber() : 0;
4505
+ const fiatValue = price ? bnOrZero2(balance.balance).div(bnOrZero2(10).pow(asset.precision)).times(bnOrZero2(price)).toNumber() : 0;
4334
4506
  withBalance.push({ asset, fiatValue });
4335
4507
  } else {
4336
4508
  withoutBalance.push(asset);
@@ -4534,10 +4706,10 @@ var TokenSelectModal = ({
4534
4706
  /* @__PURE__ */ jsx9("span", { className: "ssw-token-symbol", children: asset.symbol }),
4535
4707
  /* @__PURE__ */ jsx9("span", { className: "ssw-token-name", children: chainInfo?.name ?? asset.networkName ?? asset.name })
4536
4708
  ] }),
4537
- /* @__PURE__ */ jsx9("div", { className: "ssw-token-right", children: (evmAddress || bitcoinAddress || solanaAddress) && (loadingAssetIds.has(asset.assetId) ? /* @__PURE__ */ jsx9("span", { className: "ssw-token-balance-skeleton" }) : balance && balance.balance !== "0" ? /* @__PURE__ */ jsxs9(Fragment5, { children: [
4709
+ /* @__PURE__ */ jsx9("div", { className: "ssw-token-right", children: (evmAddress || bitcoinAddress || solanaAddress) && (loadingAssetIds.has(asset.assetId) ? /* @__PURE__ */ jsx9("span", { className: "ssw-token-balance-skeleton" }) : balance && balance.balance !== "0" ? /* @__PURE__ */ jsxs9(Fragment6, { children: [
4538
4710
  marketData?.[asset.assetId]?.price && /* @__PURE__ */ jsxs9("span", { className: "ssw-token-fiat-value", children: [
4539
4711
  "$",
4540
- bnOrZero3(balance.balance).div(bnOrZero3(10).pow(asset.precision)).times(bnOrZero3(marketData[asset.assetId].price)).toNumber().toLocaleString(void 0, {
4712
+ bnOrZero2(balance.balance).div(bnOrZero2(10).pow(asset.precision)).times(bnOrZero2(marketData[asset.assetId].price)).toNumber().toLocaleString(void 0, {
4541
4713
  minimumFractionDigits: 2,
4542
4714
  maximumFractionDigits: 2
4543
4715
  })
@@ -4586,7 +4758,8 @@ import { createAppKit, modal } from "@reown/appkit/react";
4586
4758
  import { BitcoinAdapter } from "@reown/appkit-adapter-bitcoin";
4587
4759
  import { SolanaAdapter } from "@reown/appkit-adapter-solana/react";
4588
4760
  import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
4589
- import { PhantomWalletAdapter, SolflareWalletAdapter } from "@solana/wallet-adapter-wallets";
4761
+ import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom";
4762
+ import { SolflareWalletAdapter } from "@solana/wallet-adapter-solflare";
4590
4763
  var EVM_NETWORKS = [
4591
4764
  mainnet3,
4592
4765
  polygon3,
@@ -4679,8 +4852,11 @@ var SwapWidgetContent = ({
4679
4852
  showPoweredBy,
4680
4853
  showConnectButton,
4681
4854
  isBuyAssetLocked,
4855
+ isBuyAmountLocked,
4856
+ isReceiveAddressLocked,
4857
+ isPayment,
4682
4858
  partnerCode,
4683
- allowShapeshiftRedirect,
4859
+ canRedirectToShapeshift,
4684
4860
  onSwapSuccess,
4685
4861
  onSwapError,
4686
4862
  sellFilters,
@@ -4704,11 +4880,12 @@ var SwapWidgetContent = ({
4704
4880
  handleSellAssetSelect,
4705
4881
  handleBuyAssetSelect,
4706
4882
  handleSellAmountChange,
4883
+ handleBuyAmountChange,
4707
4884
  handleToggleSellFiat,
4708
4885
  handleSelectRate,
4709
4886
  handleSlippageChange,
4710
4887
  handleButtonClick
4711
- } = useSwapHandlers({ partnerCode, allowShapeshiftRedirect });
4888
+ } = useSwapHandlers({ partnerCode, allowShapeshiftRedirect: canRedirectToShapeshift });
4712
4889
  useSwapQuoting({ apiClient, rates, sellAssetBalance });
4713
4890
  useSwapApproval();
4714
4891
  useSwapExecution();
@@ -4804,17 +4981,20 @@ var SwapWidgetContent = ({
4804
4981
  displayValues,
4805
4982
  onOpenTokenModal: setTokenModalType,
4806
4983
  onSellAmountChange: handleSellAmountChange,
4984
+ onBuyAmountChange: handleBuyAmountChange,
4807
4985
  onToggleSellFiat: handleToggleSellFiat,
4808
4986
  onSwapTokens: handleSwapTokens,
4809
4987
  onSelectRate: handleSelectRate,
4810
4988
  onButtonClick: handleButtonClick,
4811
4989
  isBuyAssetLocked,
4812
- allowShapeshiftRedirect
4990
+ isBuyAmountLocked,
4991
+ isReceiveAddressLocked,
4992
+ allowShapeshiftRedirect: canRedirectToShapeshift
4813
4993
  }
4814
4994
  ),
4815
4995
  (state.matches("approval_needed") || state.matches("approving")) && /* @__PURE__ */ jsx11(ApprovalStep, {}),
4816
4996
  state.matches("executing") && /* @__PURE__ */ jsx11(ExecutionStep, {}),
4817
- (state.matches("polling_status") || state.matches("complete") || state.matches("error")) && /* @__PURE__ */ jsx11(StatusStep, {})
4997
+ (state.matches("polling_status") || state.matches("complete") || state.matches("error")) && /* @__PURE__ */ jsx11(StatusStep, { isPayment })
4818
4998
  ] }),
4819
4999
  showPoweredBy && /* @__PURE__ */ jsxs11("div", { className: "ssw-powered-by", children: [
4820
5000
  "Powered by",
@@ -4843,7 +5023,7 @@ var SwapWidgetContent = ({
4843
5023
  disabledChainIds: (tokenModalType === "buy" ? buyFilters : sellFilters).disabledChainIds ?? [],
4844
5024
  allowedChainIds: (tokenModalType === "buy" ? buyFilters : sellFilters).allowedChainIds,
4845
5025
  allowedAssetIds: (tokenModalType === "buy" ? buyFilters : sellFilters).allowedAssetIds,
4846
- allowShapeshiftRedirect
5026
+ allowShapeshiftRedirect: canRedirectToShapeshift
4847
5027
  }
4848
5028
  ),
4849
5029
  /* @__PURE__ */ jsx11(
@@ -4867,6 +5047,10 @@ var SwapWidgetCore = ({
4867
5047
  showPoweredBy,
4868
5048
  showConnectButton,
4869
5049
  isBuyAssetLocked,
5050
+ defaultReceiveAddress,
5051
+ isReceiveAddressLocked,
5052
+ defaultBuyAmountCryptoBaseUnit,
5053
+ isBuyAmountLocked,
4870
5054
  partnerCode,
4871
5055
  allowShapeshiftRedirect,
4872
5056
  onSwapSuccess,
@@ -4880,7 +5064,9 @@ var SwapWidgetCore = ({
4880
5064
  const evm = useEvmSigning();
4881
5065
  const bitcoin3 = useBitcoinSigning();
4882
5066
  const solana3 = useSolanaSigning();
4883
- const [customReceiveAddress, setCustomReceiveAddress] = useState8("");
5067
+ const [customReceiveAddress, setCustomReceiveAddress] = useState8(
5068
+ defaultReceiveAddress ?? ""
5069
+ );
4884
5070
  const sellChainId = SwapMachineCtx.useSelector((s) => s.context.sellAsset.chainId);
4885
5071
  const buyChainId = SwapMachineCtx.useSelector((s) => s.context.buyAsset.chainId);
4886
5072
  const sellChainType = getChainType(sellChainId);
@@ -4913,14 +5099,30 @@ var SwapWidgetCore = ({
4913
5099
  () => addressForChain(buyChainType),
4914
5100
  [addressForChain, buyChainType]
4915
5101
  );
4916
- const isCustomReceiveAddressValid = useMemo12(
4917
- () => !!customReceiveAddress && validateAddress(customReceiveAddress, buyChainId).valid,
4918
- [customReceiveAddress, buyChainId]
4919
- );
4920
5102
  const receiveAddress = useMemo12(
4921
- () => isCustomReceiveAddressValid ? customReceiveAddress : walletReceiveAddress,
4922
- [isCustomReceiveAddressValid, customReceiveAddress, walletReceiveAddress]
5103
+ () => resolveReceiveAddress({
5104
+ isLocked: isReceiveAddressLocked,
5105
+ defaultAddress: defaultReceiveAddress,
5106
+ customAddress: customReceiveAddress,
5107
+ walletAddress: walletReceiveAddress,
5108
+ buyChainId
5109
+ }),
5110
+ [
5111
+ isReceiveAddressLocked,
5112
+ defaultReceiveAddress,
5113
+ customReceiveAddress,
5114
+ walletReceiveAddress,
5115
+ buyChainId
5116
+ ]
4923
5117
  );
5118
+ useEffect10(() => {
5119
+ if (!isBuyAmountLocked) return;
5120
+ actorRef.send({
5121
+ type: "SET_BUY_AMOUNT",
5122
+ amount: defaultBuyAmountCryptoBaseUnit ? formatAmountForInput(defaultBuyAmountCryptoBaseUnit, defaultBuyAsset.precision) : "",
5123
+ amountBaseUnit: defaultBuyAmountCryptoBaseUnit
5124
+ });
5125
+ }, [isBuyAmountLocked, defaultBuyAmountCryptoBaseUnit, defaultBuyAsset.precision, actorRef]);
4924
5126
  const initialSyncRef = useRef7(false);
4925
5127
  useEffect10(() => {
4926
5128
  if (initialSyncRef.current) return;
@@ -4928,6 +5130,11 @@ var SwapWidgetCore = ({
4928
5130
  actorRef.send({ type: "SET_SELL_ASSET", asset: defaultSellAsset });
4929
5131
  actorRef.send({ type: "SET_BUY_ASSET", asset: defaultBuyAsset });
4930
5132
  actorRef.send({ type: "SET_SLIPPAGE", slippage: defaultSlippage });
5133
+ actorRef.send({
5134
+ type: "SET_BUY_AMOUNT",
5135
+ amount: defaultBuyAmountCryptoBaseUnit ? formatAmountForInput(defaultBuyAmountCryptoBaseUnit, defaultBuyAsset.precision) : "",
5136
+ amountBaseUnit: defaultBuyAmountCryptoBaseUnit
5137
+ });
4931
5138
  }, [actorRef]);
4932
5139
  useEffect10(() => {
4933
5140
  actorRef.send({ type: "SET_SEND_ADDRESS", address: sendAddress });
@@ -4944,6 +5151,7 @@ var SwapWidgetCore = ({
4944
5151
  sendAddress,
4945
5152
  receiveAddress,
4946
5153
  isReceiveAddressResolving,
5154
+ isReceiveAddressBlocked: isReceiveAddressLocked && !receiveAddress,
4947
5155
  customReceiveAddress,
4948
5156
  setCustomReceiveAddress,
4949
5157
  evm,
@@ -4954,12 +5162,16 @@ var SwapWidgetCore = ({
4954
5162
  sendAddress,
4955
5163
  receiveAddress,
4956
5164
  isReceiveAddressResolving,
5165
+ isReceiveAddressLocked,
4957
5166
  customReceiveAddress,
4958
5167
  evm,
4959
5168
  bitcoin3,
4960
5169
  solana3
4961
5170
  ]
4962
5171
  );
5172
+ const hasLockedBuyAmount = isBuyAmountLocked && !!defaultBuyAmountCryptoBaseUnit;
5173
+ const isPayment = hasLockedBuyAmount && isReceiveAddressLocked && !!defaultReceiveAddress;
5174
+ const canRedirectToShapeshift = allowShapeshiftRedirect && !hasLockedBuyAmount && !isReceiveAddressLocked;
4963
5175
  return /* @__PURE__ */ jsx11(SwapWalletProvider, { value: walletValue, children: /* @__PURE__ */ jsx11(
4964
5176
  SwapWidgetContent,
4965
5177
  {
@@ -4967,9 +5179,12 @@ var SwapWidgetCore = ({
4967
5179
  theme,
4968
5180
  showPoweredBy,
4969
5181
  showConnectButton,
4970
- isBuyAssetLocked,
5182
+ isBuyAssetLocked: isBuyAssetLocked || hasLockedBuyAmount,
5183
+ isBuyAmountLocked,
5184
+ isReceiveAddressLocked,
5185
+ isPayment,
4971
5186
  partnerCode,
4972
- allowShapeshiftRedirect,
5187
+ canRedirectToShapeshift,
4973
5188
  onSwapSuccess,
4974
5189
  onSwapError,
4975
5190
  sellFilters,
@@ -4998,6 +5213,10 @@ var SwapWidget = (props) => {
4998
5213
  showPoweredBy: props.showPoweredBy ?? true,
4999
5214
  showConnectButton: props.showConnectButton ?? true,
5000
5215
  isBuyAssetLocked: props.isBuyAssetLocked ?? false,
5216
+ defaultReceiveAddress: props.defaultReceiveAddress,
5217
+ isReceiveAddressLocked: props.isReceiveAddressLocked ?? false,
5218
+ defaultBuyAmountCryptoBaseUnit: props.defaultBuyAmountCryptoBaseUnit,
5219
+ isBuyAmountLocked: props.isBuyAmountLocked ?? false,
5001
5220
  partnerCode: props.partnerCode,
5002
5221
  allowShapeshiftRedirect: props.allowShapeshiftRedirect ?? true,
5003
5222
  onSwapSuccess: props.onSwapSuccess,