@shapeshiftoss/swap-widget 0.7.0 → 0.8.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" },
@@ -2237,28 +2266,55 @@ var formatUsdValue = (cryptoAmount, precision, usdPrice) => {
2237
2266
  };
2238
2267
 
2239
2268
  // src/hooks/useSwapRates.ts
2240
- import { bnOrZero } from "@shapeshiftoss/utils";
2241
2269
  import { useQuery as useQuery3 } from "@tanstack/react-query";
2270
+
2271
+ // src/utils/rateDisplay.ts
2272
+ import { bnOrZero } from "@shapeshiftoss/utils";
2273
+ var getRateAmountBaseUnit = (rate, isExactOutput2) => (isExactOutput2 ? rate.sellAmountCryptoBaseUnit : rate.buyAmountCryptoBaseUnit) ?? "0";
2274
+ var sortRatesByValue = (rates, isExactOutput2) => [...rates].filter((rate) => !rate.error && getRateAmountBaseUnit(rate, isExactOutput2) !== "0").sort((a, b) => {
2275
+ const aAmount = bnOrZero(getRateAmountBaseUnit(a, isExactOutput2));
2276
+ const bAmount = bnOrZero(getRateAmountBaseUnit(b, isExactOutput2));
2277
+ return isExactOutput2 ? aAmount.minus(bAmount).toNumber() : bAmount.minus(aAmount).toNumber();
2278
+ });
2279
+ var getRateDiffPercent = (bestAmountBaseUnit, amountBaseUnit, isExactOutput2) => {
2280
+ const best = bnOrZero(bestAmountBaseUnit);
2281
+ const current = bnOrZero(amountBaseUnit);
2282
+ if (best.isZero()) return null;
2283
+ const diff = isExactOutput2 ? current.minus(best).div(best).times(100) : best.minus(current).div(best).times(100);
2284
+ return diff.gte("0.01") ? diff.toFixed(2) : null;
2285
+ };
2286
+
2287
+ // src/hooks/useSwapRates.ts
2242
2288
  var ENABLED_SWAPPER_NAMES = new Set(Object.values(SwapperName));
2243
2289
  var useSwapRates = (apiClient, params) => {
2244
2290
  const {
2245
2291
  sellAssetId,
2246
2292
  buyAssetId,
2247
2293
  sellAmountCryptoBaseUnit,
2294
+ buyAmountCryptoBaseUnit,
2248
2295
  enabled = true,
2249
2296
  allowedSwapperNames,
2250
2297
  refetchInterval = 15e3
2251
2298
  } = params;
2299
+ const isExactOutput2 = buyAmountCryptoBaseUnit !== void 0;
2300
+ const amountCryptoBaseUnit = isExactOutput2 ? buyAmountCryptoBaseUnit : sellAmountCryptoBaseUnit;
2252
2301
  return useQuery3({
2253
- queryKey: ["swapRates", sellAssetId, buyAssetId, sellAmountCryptoBaseUnit, allowedSwapperNames],
2302
+ queryKey: [
2303
+ "swapRates",
2304
+ sellAssetId,
2305
+ buyAssetId,
2306
+ amountCryptoBaseUnit,
2307
+ isExactOutput2,
2308
+ allowedSwapperNames
2309
+ ],
2254
2310
  queryFn: async () => {
2255
- if (!sellAssetId || !buyAssetId || !sellAmountCryptoBaseUnit) {
2311
+ if (!sellAssetId || !buyAssetId || !amountCryptoBaseUnit) {
2256
2312
  return [];
2257
2313
  }
2258
2314
  const response = await apiClient.getRates({
2259
2315
  sellAssetId,
2260
2316
  buyAssetId,
2261
- sellAmountCryptoBaseUnit
2317
+ ...isExactOutput2 ? { buyAmountCryptoBaseUnit: amountCryptoBaseUnit } : { sellAmountCryptoBaseUnit: amountCryptoBaseUnit }
2262
2318
  });
2263
2319
  let filteredRates = response.rates.filter(
2264
2320
  (rate) => !rate.error && rate.buyAmountCryptoBaseUnit !== "0" && ENABLED_SWAPPER_NAMES.has(rate.swapperName)
@@ -2266,16 +2322,15 @@ var useSwapRates = (apiClient, params) => {
2266
2322
  if (allowedSwapperNames?.length) {
2267
2323
  filteredRates = filteredRates.filter((rate) => allowedSwapperNames.includes(rate.swapperName));
2268
2324
  }
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
- });
2325
+ return sortRatesByValue(
2326
+ filteredRates.map((rate, index) => ({
2327
+ ...rate,
2328
+ id: rate.id ?? `${rate.swapperName}-${index}`
2329
+ })),
2330
+ isExactOutput2
2331
+ );
2277
2332
  },
2278
- enabled: enabled && !!sellAssetId && !!buyAssetId && !!sellAmountCryptoBaseUnit,
2333
+ enabled: enabled && !!sellAssetId && !!buyAssetId && !!amountCryptoBaseUnit,
2279
2334
  staleTime: 1e4,
2280
2335
  refetchInterval
2281
2336
  });
@@ -2287,18 +2342,23 @@ var useSwapDisplayValues = ({
2287
2342
  allowedSwapperNames,
2288
2343
  ratesRefetchInterval
2289
2344
  }) => {
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();
2345
+ const context = SwapMachineCtx.useSelector((s) => s.context);
2346
+ const {
2347
+ sellAsset,
2348
+ buyAsset,
2349
+ buyAmountBaseUnit,
2350
+ isSellAssetEvm,
2351
+ isSellAssetUtxo,
2352
+ isSellAssetSolana,
2353
+ selectedRate
2354
+ } = context;
2355
+ const { receiveAddress, isReceiveAddressBlocked, evm, bitcoin: bitcoin3, solana: solana3 } = useSwapWallet();
2298
2356
  const evmAddress = evm.address;
2299
2357
  const bitcoinAddress = bitcoin3.address;
2300
2358
  const solanaAddress = solana3.address;
2301
2359
  const buyChainType = getChainType(buyAsset.chainId);
2360
+ const isExactOutput2 = !!buyAmountBaseUnit;
2361
+ const amountBaseUnit = isExactOutput2 ? buyAmountBaseUnit : context.sellAmountBaseUnit;
2302
2362
  const {
2303
2363
  data: rates,
2304
2364
  isLoading: isLoadingRates,
@@ -2306,10 +2366,12 @@ var useSwapDisplayValues = ({
2306
2366
  } = useSwapRates(apiClient, {
2307
2367
  sellAssetId: sellAsset.assetId,
2308
2368
  buyAssetId: buyAsset.assetId,
2309
- sellAmountCryptoBaseUnit: sellAmountBaseUnit,
2369
+ sellAmountCryptoBaseUnit: context.sellAmountBaseUnit,
2370
+ buyAmountCryptoBaseUnit: buyAmountBaseUnit,
2310
2371
  allowedSwapperNames,
2311
2372
  refetchInterval: ratesRefetchInterval,
2312
- enabled: !!sellAmountBaseUnit && sellAmountBaseUnit !== "0" && (isSellAssetEvm || isSellAssetUtxo || isSellAssetSolana)
2373
+ // Rates need no destination, but a locked one the buy chain rejects can never be quoted
2374
+ enabled: !!amountBaseUnit && amountBaseUnit !== "0" && !isReceiveAddressBlocked && (isSellAssetEvm || isSellAssetUtxo || isSellAssetSolana)
2313
2375
  });
2314
2376
  const {
2315
2377
  data: sellAssetBalance,
@@ -2343,6 +2405,7 @@ var useSwapDisplayValues = ({
2343
2405
  const { data: buyChainInfo } = useChainInfo(buyAsset.chainId);
2344
2406
  const displayRate = useMemo5(() => selectedRate ?? rates?.[0], [selectedRate, rates]);
2345
2407
  const buyAmount = displayRate?.buyAmountCryptoBaseUnit;
2408
+ const sellAmountBaseUnit = isExactOutput2 ? displayRate?.sellAmountCryptoBaseUnit : context.sellAmountBaseUnit;
2346
2409
  const sellChainNativeAsset = useMemo5(() => getBaseAsset(sellAsset.chainId), [sellAsset.chainId]);
2347
2410
  const assetIdsForPrices = useMemo5(() => {
2348
2411
  const ids = [sellAsset.assetId, buyAsset.assetId];
@@ -2400,6 +2463,8 @@ var useSwapDisplayValues = ({
2400
2463
  buyChainInfo,
2401
2464
  displayRate,
2402
2465
  buyAmount,
2466
+ isExactOutput: isExactOutput2,
2467
+ sellAmountBaseUnit,
2403
2468
  sellChainNativeAsset,
2404
2469
  networkFeeDisplay,
2405
2470
  sellUsdValue,
@@ -2423,6 +2488,8 @@ var useSwapDisplayValues = ({
2423
2488
  buyChainInfo,
2424
2489
  displayRate,
2425
2490
  buyAmount,
2491
+ isExactOutput2,
2492
+ sellAmountBaseUnit,
2426
2493
  sellChainNativeAsset,
2427
2494
  networkFeeDisplay,
2428
2495
  sellUsdValue,
@@ -2553,6 +2620,10 @@ var useSwapExecution = () => {
2553
2620
  });
2554
2621
  return;
2555
2622
  }
2623
+ if (Date.now() >= quote.expiresAt) {
2624
+ actorRef.send({ type: "EXECUTE_ERROR", error: "Quote expired \u2014 please try again" });
2625
+ return;
2626
+ }
2556
2627
  const txData = quote.steps[0]?.transactionData;
2557
2628
  if (!txData) {
2558
2629
  console.error("Quote missing transactionData in steps[0]", quote);
@@ -2662,15 +2733,27 @@ var useSwapHandlers = ({
2662
2733
  },
2663
2734
  [actorRef]
2664
2735
  );
2736
+ const handleBuyAmountChange = useCallback4(
2737
+ (value) => {
2738
+ const { buyAsset } = actorRef.getSnapshot().context;
2739
+ const amountBaseUnit = value ? parseAmount(value, buyAsset.precision) : void 0;
2740
+ actorRef.send({ type: "SET_BUY_AMOUNT", amount: value, amountBaseUnit });
2741
+ },
2742
+ [actorRef]
2743
+ );
2665
2744
  const handleToggleSellFiat = useCallback4(
2666
2745
  (sellAssetUsdPrice) => {
2667
2746
  if (!sellAssetUsdPrice) return;
2668
2747
  const snap = actorRef.getSnapshot();
2669
- const { sellAmount, sellAmountBaseUnit, sellAsset, isSellAmountFiat } = snap.context;
2748
+ const { sellAmount, sellAmountBaseUnit, sellAsset, isSellAmountFiat, buyAmountBaseUnit } = snap.context;
2670
2749
  if (isSellAmountFiat) {
2671
2750
  actorRef.send({ type: "SET_SELL_FIAT_MODE", isFiat: false });
2672
2751
  return;
2673
2752
  }
2753
+ if (buyAmountBaseUnit) {
2754
+ actorRef.send({ type: "SET_SELL_FIAT_MODE", isFiat: true });
2755
+ return;
2756
+ }
2674
2757
  const fiatValue = cryptoToFiat(sellAmountBaseUnit, sellAssetUsdPrice, sellAsset.precision);
2675
2758
  actorRef.send({
2676
2759
  type: "SET_SELL_AMOUNT",
@@ -2746,6 +2829,7 @@ var useSwapHandlers = ({
2746
2829
  handleSellAssetSelect,
2747
2830
  handleBuyAssetSelect,
2748
2831
  handleSellAmountChange,
2832
+ handleBuyAmountChange,
2749
2833
  handleToggleSellFiat,
2750
2834
  handleSelectRate,
2751
2835
  handleSlippageChange,
@@ -2768,9 +2852,12 @@ var useSwapQuoting = ({ apiClient, rates, sellAssetBalance }) => {
2768
2852
  quotingRef.current = true;
2769
2853
  const fetchQuote = async () => {
2770
2854
  try {
2771
- if (sellAssetBalance?.balance && context.sellAmountBaseUnit) {
2855
+ const isExactOutput2 = !!context.buyAmountBaseUnit;
2856
+ const rateToUse = context.selectedRate ?? rates?.[0];
2857
+ const sellAmountBaseUnit = isExactOutput2 ? rateToUse?.sellAmountCryptoBaseUnit : context.sellAmountBaseUnit;
2858
+ if (sellAssetBalance?.balance && sellAmountBaseUnit) {
2772
2859
  const balanceBigInt = BigInt(sellAssetBalance.balance);
2773
- const amountBigInt = BigInt(context.sellAmountBaseUnit);
2860
+ const amountBigInt = BigInt(sellAmountBaseUnit);
2774
2861
  if (amountBigInt > balanceBigInt) {
2775
2862
  actorRef.send({ type: "QUOTE_ERROR", error: "Insufficient balance" });
2776
2863
  return;
@@ -2782,8 +2869,8 @@ var useSwapQuoting = ({ apiClient, rates, sellAssetBalance }) => {
2782
2869
  return;
2783
2870
  }
2784
2871
  const slippageDecimal = (parsedSlippage / 100).toString();
2785
- const rateToUse = context.selectedRate ?? rates?.[0];
2786
- if (!rateToUse || !context.sellAmountBaseUnit) {
2872
+ const amountBaseUnit = isExactOutput2 ? context.buyAmountBaseUnit : context.sellAmountBaseUnit;
2873
+ if (!rateToUse || !amountBaseUnit) {
2787
2874
  actorRef.send({ type: "QUOTE_ERROR", error: "No rate or amount available" });
2788
2875
  return;
2789
2876
  }
@@ -2799,7 +2886,7 @@ var useSwapQuoting = ({ apiClient, rates, sellAssetBalance }) => {
2799
2886
  const response = await apiClient.getQuote({
2800
2887
  sellAssetId: context.sellAsset.assetId,
2801
2888
  buyAssetId: context.buyAsset.assetId,
2802
- sellAmountCryptoBaseUnit: context.sellAmountBaseUnit,
2889
+ ...isExactOutput2 ? { buyAmountCryptoBaseUnit: amountBaseUnit } : { sellAmountCryptoBaseUnit: amountBaseUnit },
2803
2890
  sendAddress,
2804
2891
  receiveAddress: resolvedReceiveAddress,
2805
2892
  swapperName: rateToUse.swapperName,
@@ -2956,6 +3043,19 @@ var getAddressFormatHint = (chainId) => {
2956
3043
  }
2957
3044
  };
2958
3045
 
3046
+ // src/utils/receiveAddress.ts
3047
+ var resolveReceiveAddress = ({
3048
+ isLocked,
3049
+ defaultAddress,
3050
+ customAddress,
3051
+ walletAddress,
3052
+ buyChainId
3053
+ }) => {
3054
+ const isValidForBuyChain = (address) => !!address && validateAddress(address, buyChainId).valid;
3055
+ if (isLocked) return isValidForBuyChain(defaultAddress) ? defaultAddress : void 0;
3056
+ return isValidForBuyChain(customAddress) ? customAddress : walletAddress;
3057
+ };
3058
+
2959
3059
  // src/components/ApprovalStep.tsx
2960
3060
  import { jsx, jsxs } from "react/jsx-runtime";
2961
3061
  var ApprovalStep = () => {
@@ -3117,9 +3217,8 @@ var getSwapperColor = (swapperName) => {
3117
3217
  };
3118
3218
 
3119
3219
  // src/components/QuotesModal.tsx
3120
- import { bnOrZero as bnOrZero2 } from "@shapeshiftoss/utils";
3121
3220
  import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo6 } from "react";
3122
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3221
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3123
3222
  var useLockBodyScroll = (isLocked) => {
3124
3223
  useEffect6(() => {
3125
3224
  if (!isLocked) return;
@@ -3130,13 +3229,6 @@ var useLockBodyScroll = (isLocked) => {
3130
3229
  };
3131
3230
  }, [isLocked]);
3132
3231
  };
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
3232
  var QuotesModal = ({
3141
3233
  isOpen,
3142
3234
  onClose,
@@ -3146,6 +3238,9 @@ var QuotesModal = ({
3146
3238
  buyAsset,
3147
3239
  sellAsset,
3148
3240
  sellAmountBaseUnit,
3241
+ buyAmountBaseUnit,
3242
+ isExactOutput: isExactOutput2,
3243
+ sellAssetUsdPrice,
3149
3244
  buyAssetUsdPrice
3150
3245
  }) => {
3151
3246
  useLockBodyScroll(isOpen);
@@ -3164,15 +3259,11 @@ var QuotesModal = ({
3164
3259
  },
3165
3260
  [onSelectRate, onClose]
3166
3261
  );
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]);
3262
+ const sortedRates = useMemo6(() => sortRatesByValue(rates, isExactOutput2), [rates, isExactOutput2]);
3174
3263
  const bestRate = useMemo6(() => sortedRates[0], [sortedRates]);
3175
- const bestBuyAmount = bestRate?.buyAmountCryptoBaseUnit ?? "0";
3264
+ const bestAmountBaseUnit = bestRate ? getRateAmountBaseUnit(bestRate, isExactOutput2) : "0";
3265
+ const asset = isExactOutput2 ? sellAsset : buyAsset;
3266
+ const usdPrice = isExactOutput2 ? sellAssetUsdPrice : buyAssetUsdPrice;
3176
3267
  if (!isOpen) return null;
3177
3268
  return (
3178
3269
  // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
@@ -3189,14 +3280,20 @@ var QuotesModal = ({
3189
3280
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quotes-modal-header", children: [
3190
3281
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quotes-header-content", children: [
3191
3282
  /* @__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: [
3283
+ /* @__PURE__ */ jsx3("span", { className: "ssw-quotes-modal-subtitle", children: isExactOutput2 ? /* @__PURE__ */ jsxs3(Fragment, { children: [
3284
+ sellAsset.symbol,
3285
+ " \u2192 ",
3286
+ formatAmount(buyAmountBaseUnit, buyAsset.precision),
3287
+ " ",
3288
+ buyAsset.symbol
3289
+ ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
3193
3290
  formatAmount(sellAmountBaseUnit, sellAsset.precision),
3194
3291
  " ",
3195
3292
  sellAsset.symbol,
3196
3293
  " \u2192",
3197
3294
  " ",
3198
3295
  buyAsset.symbol
3199
- ] })
3296
+ ] }) })
3200
3297
  ] }),
3201
3298
  /* @__PURE__ */ jsx3("button", { className: "ssw-quotes-modal-close", onClick: onClose, type: "button", children: /* @__PURE__ */ jsx3(
3202
3299
  "svg",
@@ -3212,15 +3309,15 @@ var QuotesModal = ({
3212
3309
  ) })
3213
3310
  ] }),
3214
3311
  /* @__PURE__ */ jsx3("div", { className: "ssw-quotes-modal-list", children: sortedRates.map((rate, index) => {
3215
- const buyAmount = rate.buyAmountCryptoBaseUnit ?? "0";
3312
+ const amountBaseUnit = getRateAmountBaseUnit(rate, isExactOutput2);
3216
3313
  const estimatedTime = rate.estimatedExecutionTimeMs;
3217
3314
  const isBest = index === 0;
3218
3315
  const isSelected = selectedRate?.id === rate.id;
3219
3316
  const swapperIcon = getSwapperIcon(rate.swapperName);
3220
3317
  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);
3318
+ const formattedAmount = formatAmount(amountBaseUnit, asset.precision);
3319
+ const usdValue = formatUsdValue(amountBaseUnit, asset.precision, usdPrice);
3320
+ const diffPercent = isBest ? null : getRateDiffPercent(bestAmountBaseUnit, amountBaseUnit, isExactOutput2);
3224
3321
  const estimatedSeconds = estimatedTime ? Math.round(estimatedTime / 1e3) : 0;
3225
3322
  const hasTime = estimatedSeconds > 0;
3226
3323
  return /* @__PURE__ */ jsxs3(
@@ -3243,9 +3340,9 @@ var QuotesModal = ({
3243
3340
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quote-row-name-row", children: [
3244
3341
  /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-name", children: rate.swapperName }),
3245
3342
  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,
3343
+ diffPercent && /* @__PURE__ */ jsxs3("span", { className: "ssw-quote-row-diff", children: [
3344
+ isExactOutput2 ? "+" : "-",
3345
+ diffPercent,
3249
3346
  "%"
3250
3347
  ] })
3251
3348
  ] }),
@@ -3258,9 +3355,9 @@ var QuotesModal = ({
3258
3355
  ] }),
3259
3356
  /* @__PURE__ */ jsxs3("div", { className: "ssw-quote-row-right", children: [
3260
3357
  /* @__PURE__ */ jsxs3("span", { className: "ssw-quote-row-amount", children: [
3261
- formattedBuyAmount,
3358
+ formattedAmount,
3262
3359
  " ",
3263
- /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-symbol", children: buyAsset.symbol })
3360
+ /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-symbol", children: asset.symbol })
3264
3361
  ] }),
3265
3362
  /* @__PURE__ */ jsx3("span", { className: "ssw-quote-row-usd", children: usdValue })
3266
3363
  ] })
@@ -3276,7 +3373,7 @@ var QuotesModal = ({
3276
3373
  };
3277
3374
 
3278
3375
  // src/components/QuoteSelector.tsx
3279
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3376
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3280
3377
  var QuoteSelector = ({
3281
3378
  rates,
3282
3379
  selectedRate,
@@ -3284,7 +3381,10 @@ var QuoteSelector = ({
3284
3381
  buyAsset,
3285
3382
  sellAsset,
3286
3383
  sellAmountBaseUnit,
3384
+ buyAmountBaseUnit,
3385
+ isExactOutput: isExactOutput2,
3287
3386
  isLoading,
3387
+ sellAssetUsdPrice,
3288
3388
  buyAssetUsdPrice
3289
3389
  }) => {
3290
3390
  const [isModalOpen, setIsModalOpen] = useState3(false);
@@ -3314,12 +3414,17 @@ var QuoteSelector = ({
3314
3414
  return null;
3315
3415
  }
3316
3416
  const displayRate = selectedRate ?? bestRate;
3317
- const buyAmount = displayRate.buyAmountCryptoBaseUnit ?? "0";
3318
3417
  const swapperIcon = getSwapperIcon(displayRate.swapperName);
3319
3418
  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: [
3419
+ const asset = isExactOutput2 ? sellAsset : buyAsset;
3420
+ const amountBaseUnit = getRateAmountBaseUnit(displayRate, isExactOutput2);
3421
+ const formattedAmount = formatAmount(amountBaseUnit, asset.precision);
3422
+ const usdValue = formatUsdValue(
3423
+ amountBaseUnit,
3424
+ asset.precision,
3425
+ isExactOutput2 ? sellAssetUsdPrice : buyAssetUsdPrice
3426
+ );
3427
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
3323
3428
  /* @__PURE__ */ jsxs4("button", { className: "ssw-quote-selector", onClick: handleOpenModal, type: "button", children: [
3324
3429
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-left", children: [
3325
3430
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-provider", children: [
@@ -3345,8 +3450,8 @@ var QuoteSelector = ({
3345
3450
  ] }),
3346
3451
  /* @__PURE__ */ jsxs4("div", { className: "ssw-quote-right", children: [
3347
3452
  /* @__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 })
3453
+ /* @__PURE__ */ jsx4("span", { className: "ssw-quote-amount", children: formattedAmount }),
3454
+ /* @__PURE__ */ jsx4("span", { className: "ssw-quote-symbol", children: asset.symbol })
3350
3455
  ] }),
3351
3456
  alternativeRatesCount > 0 && /* @__PURE__ */ jsxs4("span", { className: "ssw-quote-more", children: [
3352
3457
  "+",
@@ -3378,6 +3483,9 @@ var QuoteSelector = ({
3378
3483
  buyAsset,
3379
3484
  sellAsset,
3380
3485
  sellAmountBaseUnit,
3486
+ buyAmountBaseUnit,
3487
+ isExactOutput: isExactOutput2,
3488
+ sellAssetUsdPrice,
3381
3489
  buyAssetUsdPrice
3382
3490
  }
3383
3491
  )
@@ -3386,11 +3494,12 @@ var QuoteSelector = ({
3386
3494
 
3387
3495
  // src/components/ReceiveAddressRow.tsx
3388
3496
  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";
3497
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
3390
3498
  var ReceiveAddressRow = ({
3391
3499
  receiveAddress,
3392
3500
  isResolving,
3393
3501
  buyChainId,
3502
+ isLocked,
3394
3503
  onSetCustomReceiveAddress
3395
3504
  }) => {
3396
3505
  const [isEditing, setIsEditing] = useState4(false);
@@ -3398,7 +3507,7 @@ var ReceiveAddressRow = ({
3398
3507
  const [hasInteracted, setHasInteracted] = useState4(false);
3399
3508
  const inputRef = useRef5(null);
3400
3509
  const needsAddress = !receiveAddress;
3401
- const showInput = needsAddress && !isResolving || isEditing;
3510
+ const showInput = !isLocked && (needsAddress && !isResolving || isEditing);
3402
3511
  const showAttention = needsAddress && !isResolving;
3403
3512
  useLayoutEffect(() => {
3404
3513
  if (isEditing) inputRef.current?.focus();
@@ -3418,6 +3527,7 @@ var ReceiveAddressRow = ({
3418
3527
  [trimmedDraft, buyChainId]
3419
3528
  );
3420
3529
  const formatHint = useMemo8(() => getAddressFormatHint(buyChainId), [buyChainId]);
3530
+ const chainName = useMemo8(() => getChainName(buyChainId), [buyChainId]);
3421
3531
  const startEditing = useCallback7(() => {
3422
3532
  setDraft(receiveAddress ?? "");
3423
3533
  setHasInteracted(false);
@@ -3441,36 +3551,48 @@ var ReceiveAddressRow = ({
3441
3551
  }, [onSetCustomReceiveAddress]);
3442
3552
  const showReset = !!draft || isEditing;
3443
3553
  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",
3554
+ return /* @__PURE__ */ jsxs5(
3555
+ "div",
3556
+ {
3557
+ className: `ssw-receive-row ssw-receive-row-resolved${showAttention ? " ssw-receive-row-invalid" : ""}`,
3558
+ children: [
3559
+ /* @__PURE__ */ jsx5("span", { className: "ssw-receive-label", children: "Receive address" }),
3560
+ /* @__PURE__ */ jsx5("div", { className: "ssw-receive-resolved-value", children: isResolving ? /* @__PURE__ */ jsx5("span", { className: "ssw-balance-skeleton" }) : showAttention ? (
3561
+ // Locked only - an unlocked row with no address shows the input instead
3562
+ /* @__PURE__ */ jsxs5("span", { className: "ssw-receive-error", children: [
3563
+ "Not valid for ",
3564
+ chainName
3565
+ ] })
3566
+ ) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
3567
+ /* @__PURE__ */ jsx5("span", { className: "ssw-receive-address", children: truncateAddress(receiveAddress ?? "", 6) }),
3568
+ !isLocked && /* @__PURE__ */ jsx5(
3569
+ "button",
3457
3570
  {
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
- ]
3571
+ className: "ssw-receive-edit-btn",
3572
+ onClick: startEditing,
3573
+ type: "button",
3574
+ "aria-label": "Edit receive address",
3575
+ children: /* @__PURE__ */ jsxs5(
3576
+ "svg",
3577
+ {
3578
+ width: "14",
3579
+ height: "14",
3580
+ viewBox: "0 0 24 24",
3581
+ fill: "none",
3582
+ stroke: "currentColor",
3583
+ strokeWidth: "2",
3584
+ children: [
3585
+ /* @__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" }),
3586
+ /* @__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" })
3587
+ ]
3588
+ }
3589
+ )
3468
3590
  }
3469
3591
  )
3470
- }
3471
- )
3472
- ] }) })
3473
- ] });
3592
+ ] }) })
3593
+ ]
3594
+ }
3595
+ );
3474
3596
  }
3475
3597
  return /* @__PURE__ */ jsxs5(
3476
3598
  "div",
@@ -3554,39 +3676,23 @@ var ReceiveAddressRow = ({
3554
3676
  };
3555
3677
 
3556
3678
  // src/components/InputStep.tsx
3557
- import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3679
+ import { Fragment as Fragment4, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
3558
3680
  var InputStep = ({
3559
3681
  displayValues,
3560
3682
  onOpenTokenModal,
3561
3683
  onSellAmountChange,
3684
+ onBuyAmountChange,
3562
3685
  onToggleSellFiat,
3563
3686
  onSwapTokens,
3564
3687
  onSelectRate,
3565
3688
  onButtonClick,
3566
3689
  isBuyAssetLocked,
3690
+ isBuyAmountLocked,
3691
+ isReceiveAddressLocked,
3567
3692
  allowShapeshiftRedirect
3568
3693
  }) => {
3569
3694
  const context = SwapMachineCtx.useSelector((s) => s.context);
3570
3695
  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
3696
  const {
3591
3697
  sendAddress,
3592
3698
  receiveAddress,
@@ -3596,22 +3702,48 @@ var InputStep = ({
3596
3702
  bitcoin: bitcoin3,
3597
3703
  solana: solana3
3598
3704
  } = 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;
3705
+ const buyChainId = context.buyAsset.chainId;
3612
3706
  const hasAnyWalletAddress = !!evm.address || !!bitcoin3.address || !!solana3.address;
3613
3707
  const hasActiveWallet = !!receiveAddress || hasAnyWalletAddress || isReceiveAddressResolving;
3614
- const isUnsupportedChain = !isSellAssetEvm && !isSellAssetUtxo && !isSellAssetSolana;
3708
+ const isUnsupportedChain = !context.isSellAssetEvm && !context.isSellAssetUtxo && !context.isSellAssetSolana;
3709
+ const amountBaseUnit = displayValues.isExactOutput ? context.buyAmountBaseUnit : context.sellAmountBaseUnit;
3710
+ const isSellAmountReadOnly = displayValues.isExactOutput && isBuyAmountLocked;
3711
+ const isSellAmountPending = displayValues.isExactOutput && displayValues.isLoadingRates;
3712
+ const isBuyAmountPending = !displayValues.isExactOutput && displayValues.isLoadingRates;
3713
+ const sellAmountCrypto = useMemo9(
3714
+ () => displayValues.sellAmountBaseUnit ? formatAmount(displayValues.sellAmountBaseUnit, context.sellAsset.precision, 6) : "",
3715
+ [displayValues.sellAmountBaseUnit, context.sellAsset.precision]
3716
+ );
3717
+ const buyAmountValue = useMemo9(() => {
3718
+ if (displayValues.isExactOutput) return context.buyAmount;
3719
+ if (!displayValues.buyAmount) return "";
3720
+ return formatAmount(displayValues.buyAmount, context.buyAsset.precision);
3721
+ }, [
3722
+ displayValues.isExactOutput,
3723
+ displayValues.buyAmount,
3724
+ context.buyAmount,
3725
+ context.buyAsset.precision
3726
+ ]);
3727
+ const sellAmountValue = useMemo9(() => {
3728
+ if (!displayValues.isExactOutput) {
3729
+ return context.isSellAmountFiat ? context.sellAmountFiat : context.sellAmount;
3730
+ }
3731
+ if (!context.isSellAmountFiat) return sellAmountCrypto;
3732
+ return cryptoToFiat(
3733
+ displayValues.sellAmountBaseUnit,
3734
+ displayValues.sellAssetUsdPrice ?? "",
3735
+ context.sellAsset.precision
3736
+ );
3737
+ }, [
3738
+ displayValues.isExactOutput,
3739
+ displayValues.sellAmountBaseUnit,
3740
+ displayValues.sellAssetUsdPrice,
3741
+ context.isSellAmountFiat,
3742
+ context.sellAmountFiat,
3743
+ context.sellAmount,
3744
+ context.sellAsset.precision,
3745
+ sellAmountCrypto
3746
+ ]);
3615
3747
  const { text: buttonText, disabled: isButtonDisabled } = useMemo9(() => {
3616
3748
  if (isUnsupportedChain) {
3617
3749
  if (!allowShapeshiftRedirect) return { text: "Route not supported", disabled: true };
@@ -3619,39 +3751,43 @@ var InputStep = ({
3619
3751
  }
3620
3752
  if (!sendAddress) return { text: "Connect Wallet", disabled: false };
3621
3753
  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 };
3754
+ const drivingAmount = displayValues.isExactOutput ? context.buyAmountBaseUnit : context.sellAmount;
3755
+ if (!drivingAmount || drivingAmount === "0") return { text: "Enter an amount", disabled: true };
3756
+ if (displayValues.isLoadingRates) return { text: "Finding rates...", disabled: true };
3757
+ if (displayValues.ratesError) return { text: "No routes available", disabled: true };
3758
+ if (!displayValues.rates?.length) return { text: "No routes found", disabled: true };
3626
3759
  return { text: "Swap", disabled: false };
3627
3760
  }, [
3628
3761
  isUnsupportedChain,
3629
3762
  allowShapeshiftRedirect,
3630
3763
  sendAddress,
3631
3764
  receiveAddress,
3632
- sellAmount,
3633
- isLoadingRates,
3634
- ratesError,
3635
- rates
3765
+ context.sellAmount,
3766
+ displayValues.isExactOutput,
3767
+ context.buyAmountBaseUnit,
3768
+ displayValues.isLoadingRates,
3769
+ displayValues.ratesError,
3770
+ displayValues.rates
3636
3771
  ]);
3637
- return /* @__PURE__ */ jsxs6(Fragment3, { children: [
3772
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
3638
3773
  /* @__PURE__ */ jsxs6("div", { className: "ssw-swap-container", children: [
3639
3774
  /* @__PURE__ */ jsxs6("div", { className: "ssw-token-section ssw-sell", children: [
3640
3775
  /* @__PURE__ */ jsx6("div", { className: "ssw-section-header", children: /* @__PURE__ */ jsx6("span", { className: "ssw-section-label", children: "Sell" }) }),
3641
3776
  /* @__PURE__ */ jsxs6("div", { className: "ssw-input-row", children: [
3642
- isSellAmountFiat && /* @__PURE__ */ jsx6("span", { className: "ssw-fiat-prefix", children: "$" }),
3777
+ context.isSellAmountFiat && /* @__PURE__ */ jsx6("span", { className: "ssw-fiat-prefix", children: "$" }),
3643
3778
  /* @__PURE__ */ jsx6(
3644
3779
  "input",
3645
3780
  {
3646
3781
  type: "text",
3647
- className: "ssw-amount-input",
3648
- placeholder: "0",
3649
- value: isSellAmountFiat ? sellAmountFiat : sellAmount,
3782
+ className: `ssw-amount-input${isSellAmountReadOnly ? " ssw-amount-input-locked" : ""}${isSellAmountPending ? " ssw-amount-input-pending" : ""}`,
3783
+ placeholder: isSellAmountPending ? "..." : "0",
3784
+ value: sellAmountValue,
3785
+ readOnly: isSellAmountReadOnly,
3650
3786
  onChange: (e) => {
3651
3787
  const raw = e.target.value.replace(/[^0-9.]/g, "");
3652
3788
  const parts = raw.split(".");
3653
3789
  const sanitized = parts.length > 2 ? parts[0] + "." + parts.slice(1).join("") : raw;
3654
- onSellAmountChange(sanitized, sellAssetUsdPrice);
3790
+ onSellAmountChange(sanitized, displayValues.sellAssetUsdPrice);
3655
3791
  }
3656
3792
  }
3657
3793
  ),
@@ -3662,10 +3798,17 @@ var InputStep = ({
3662
3798
  onClick: () => onOpenTokenModal("sell"),
3663
3799
  type: "button",
3664
3800
  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) }),
3801
+ context.sellAsset.icon ? /* @__PURE__ */ jsx6(
3802
+ "img",
3803
+ {
3804
+ src: context.sellAsset.icon,
3805
+ alt: context.sellAsset.symbol,
3806
+ className: "ssw-token-icon"
3807
+ }
3808
+ ) : /* @__PURE__ */ jsx6("div", { className: "ssw-token-icon-placeholder", children: context.sellAsset.symbol.charAt(0) }),
3666
3809
  /* @__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 })
3810
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-symbol", children: context.sellAsset.symbol }),
3811
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-chain", children: displayValues.sellChainInfo?.name ?? context.sellAsset.networkName ?? context.sellAsset.name })
3669
3812
  ] }),
3670
3813
  /* @__PURE__ */ jsx6(
3671
3814
  "svg",
@@ -3684,14 +3827,14 @@ var InputStep = ({
3684
3827
  )
3685
3828
  ] }),
3686
3829
  /* @__PURE__ */ jsxs6("div", { className: "ssw-section-footer", children: [
3687
- sellAssetUsdPrice ? /* @__PURE__ */ jsxs6(
3830
+ !displayValues.sellAssetUsdPrice ? /* @__PURE__ */ jsx6("span", { className: "ssw-usd-value" }) : /* @__PURE__ */ jsxs6(
3688
3831
  "button",
3689
3832
  {
3690
3833
  type: "button",
3691
3834
  className: "ssw-usd-value ssw-usd-value-toggle",
3692
- onClick: () => onToggleSellFiat(sellAssetUsdPrice),
3835
+ onClick: () => onToggleSellFiat(displayValues.sellAssetUsdPrice),
3693
3836
  children: [
3694
- /* @__PURE__ */ jsx6("span", { children: isSellAmountFiat ? `\u2248 ${sellAmountBaseUnit ? formatAmount(sellAmountBaseUnit, sellAsset.precision, 6) : "0"} ${sellAsset.symbol}` : sellUsdValue }),
3837
+ /* @__PURE__ */ jsx6("span", { children: context.isSellAmountFiat ? `\u2248 ${sellAmountCrypto || "0"} ${context.sellAsset.symbol}` : displayValues.sellUsdValue }),
3695
3838
  /* @__PURE__ */ jsx6(
3696
3839
  "svg",
3697
3840
  {
@@ -3707,13 +3850,14 @@ var InputStep = ({
3707
3850
  )
3708
3851
  ]
3709
3852
  }
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: [
3853
+ ),
3854
+ hasAnyWalletAddress && (displayValues.isSellBalanceLoading ? /* @__PURE__ */ jsx6("span", { className: "ssw-balance-skeleton" }) : displayValues.sellAssetBalance ? /* @__PURE__ */ jsxs6("span", { className: "ssw-balance", children: [
3712
3855
  "Balance: ",
3713
- sellAssetBalance.balanceFormatted,
3714
- sellBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3715
- " (",
3716
- sellBalanceFiatValue,
3856
+ displayValues.sellAssetBalance.balanceFormatted,
3857
+ displayValues.sellBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3858
+ " ",
3859
+ "(",
3860
+ displayValues.sellBalanceFiatValue,
3717
3861
  ")"
3718
3862
  ] })
3719
3863
  ] }) : null)
@@ -3747,10 +3891,15 @@ var InputStep = ({
3747
3891
  "input",
3748
3892
  {
3749
3893
  type: "text",
3750
- className: "ssw-amount-input",
3751
- placeholder: "0",
3752
- value: buyAmount ? formatAmount(buyAmount, buyAsset.precision) : "",
3753
- readOnly: true
3894
+ className: `ssw-amount-input${isBuyAmountLocked ? " ssw-amount-input-locked" : ""}${isBuyAmountPending ? " ssw-amount-input-pending" : ""}`,
3895
+ placeholder: isBuyAmountPending ? "..." : "0",
3896
+ value: buyAmountValue,
3897
+ readOnly: isBuyAmountLocked,
3898
+ onChange: (e) => {
3899
+ const raw = e.target.value.replace(/[^0-9.]/g, "");
3900
+ const parts = raw.split(".");
3901
+ onBuyAmountChange(parts.length > 2 ? parts[0] + "." + parts.slice(1).join("") : raw);
3902
+ }
3754
3903
  }
3755
3904
  ),
3756
3905
  /* @__PURE__ */ jsxs6(
@@ -3761,10 +3910,17 @@ var InputStep = ({
3761
3910
  disabled: isBuyAssetLocked,
3762
3911
  type: "button",
3763
3912
  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) }),
3913
+ context.buyAsset.icon ? /* @__PURE__ */ jsx6(
3914
+ "img",
3915
+ {
3916
+ src: context.buyAsset.icon,
3917
+ alt: context.buyAsset.symbol,
3918
+ className: "ssw-token-icon"
3919
+ }
3920
+ ) : /* @__PURE__ */ jsx6("div", { className: "ssw-token-icon-placeholder", children: context.buyAsset.symbol.charAt(0) }),
3765
3921
  /* @__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 })
3922
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-symbol", children: context.buyAsset.symbol }),
3923
+ /* @__PURE__ */ jsx6("span", { className: "ssw-token-chain", children: displayValues.buyChainInfo?.name ?? context.buyAsset.networkName ?? context.buyAsset.name })
3768
3924
  ] }),
3769
3925
  !isBuyAssetLocked && /* @__PURE__ */ jsx6(
3770
3926
  "svg",
@@ -3783,13 +3939,13 @@ var InputStep = ({
3783
3939
  )
3784
3940
  ] }),
3785
3941
  /* @__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: [
3942
+ /* @__PURE__ */ jsx6("span", { className: "ssw-usd-value", children: displayValues.buyUsdValue }),
3943
+ hasAnyWalletAddress && (displayValues.isBuyBalanceLoading ? /* @__PURE__ */ jsx6("span", { className: "ssw-balance-skeleton" }) : displayValues.buyAssetBalance ? /* @__PURE__ */ jsxs6("span", { className: "ssw-balance", children: [
3788
3944
  "Balance: ",
3789
- buyAssetBalance.balanceFormatted,
3790
- buyBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3945
+ displayValues.buyAssetBalance.balanceFormatted,
3946
+ displayValues.buyBalanceFiatValue && /* @__PURE__ */ jsxs6("span", { className: "ssw-balance-fiat", children: [
3791
3947
  " (",
3792
- buyBalanceFiatValue,
3948
+ displayValues.buyBalanceFiatValue,
3793
3949
  ")"
3794
3950
  ] })
3795
3951
  ] }) : null)
@@ -3801,26 +3957,30 @@ var InputStep = ({
3801
3957
  receiveAddress,
3802
3958
  isResolving: isReceiveAddressResolving,
3803
3959
  buyChainId,
3960
+ isLocked: isReceiveAddressLocked,
3804
3961
  onSetCustomReceiveAddress: setCustomReceiveAddress
3805
3962
  }
3806
3963
  )
3807
3964
  ] }),
3808
- sellAmountBaseUnit && sellAmountBaseUnit !== "0" && (rates?.length || isLoadingRates) && /* @__PURE__ */ jsx6("div", { className: "ssw-quotes", children: /* @__PURE__ */ jsx6(
3965
+ amountBaseUnit && amountBaseUnit !== "0" && (displayValues.rates?.length || displayValues.isLoadingRates) && /* @__PURE__ */ jsx6("div", { className: "ssw-quotes", children: /* @__PURE__ */ jsx6(
3809
3966
  QuoteSelector,
3810
3967
  {
3811
- rates: rates ?? [],
3812
- selectedRate,
3968
+ rates: displayValues.rates ?? [],
3969
+ selectedRate: context.selectedRate,
3813
3970
  onSelectRate,
3814
- buyAsset,
3815
- sellAsset,
3816
- sellAmountBaseUnit,
3817
- isLoading: isLoadingRates,
3818
- buyAssetUsdPrice
3971
+ buyAsset: context.buyAsset,
3972
+ sellAsset: context.sellAsset,
3973
+ sellAmountBaseUnit: displayValues.sellAmountBaseUnit ?? "0",
3974
+ buyAmountBaseUnit: context.buyAmountBaseUnit ?? "0",
3975
+ isExactOutput: displayValues.isExactOutput,
3976
+ isLoading: displayValues.isLoadingRates,
3977
+ sellAssetUsdPrice: displayValues.sellAssetUsdPrice,
3978
+ buyAssetUsdPrice: displayValues.buyAssetUsdPrice
3819
3979
  }
3820
3980
  ) }),
3821
- networkFeeDisplay && /* @__PURE__ */ jsxs6("div", { className: "ssw-network-fee", children: [
3981
+ displayValues.networkFeeDisplay && /* @__PURE__ */ jsxs6("div", { className: "ssw-network-fee", children: [
3822
3982
  /* @__PURE__ */ jsx6("span", { className: "ssw-network-fee-label", children: "Est. network fee" }),
3823
- /* @__PURE__ */ jsx6("span", { className: "ssw-network-fee-value", children: networkFeeDisplay })
3983
+ /* @__PURE__ */ jsx6("span", { className: "ssw-network-fee-value", children: displayValues.networkFeeDisplay })
3824
3984
  ] }),
3825
3985
  /* @__PURE__ */ jsx6(
3826
3986
  "button",
@@ -3830,7 +3990,7 @@ var InputStep = ({
3830
3990
  onClick: onButtonClick,
3831
3991
  type: "button",
3832
3992
  style: isQuoting ? { opacity: 0.7 } : void 0,
3833
- children: isQuoting ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
3993
+ children: isQuoting ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
3834
3994
  /* @__PURE__ */ jsxs6(
3835
3995
  "svg",
3836
3996
  {
@@ -4017,7 +4177,7 @@ var SettingsModal = ({ isOpen, onClose, onSlippageChange }) => {
4017
4177
 
4018
4178
  // src/components/StatusStep.tsx
4019
4179
  import { useMemo as useMemo10 } from "react";
4020
- import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
4180
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
4021
4181
  var ExplorerLink = ({ url }) => /* @__PURE__ */ jsxs8("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "ssw-step-explorer-link", children: [
4022
4182
  "View on Explorer",
4023
4183
  /* @__PURE__ */ jsx8(
@@ -4033,7 +4193,7 @@ var ExplorerLink = ({ url }) => /* @__PURE__ */ jsxs8("a", { href: url, target:
4033
4193
  }
4034
4194
  )
4035
4195
  ] });
4036
- var StatusStep = () => {
4196
+ var StatusStep = ({ isPayment }) => {
4037
4197
  const context = SwapMachineCtx.useSelector((s) => s.context);
4038
4198
  const isPolling = SwapMachineCtx.useSelector((s) => s.matches("polling_status"));
4039
4199
  const isComplete = SwapMachineCtx.useSelector((s) => s.matches("complete"));
@@ -4051,7 +4211,7 @@ var StatusStep = () => {
4051
4211
  [error]
4052
4212
  );
4053
4213
  return /* @__PURE__ */ jsxs8("div", { className: "ssw-step-screen", children: [
4054
- isPolling && /* @__PURE__ */ jsxs8(Fragment4, { children: [
4214
+ isPolling && /* @__PURE__ */ jsxs8(Fragment5, { children: [
4055
4215
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-icon-circle ssw-ic-accent", children: /* @__PURE__ */ jsxs8(
4056
4216
  "svg",
4057
4217
  {
@@ -4072,7 +4232,7 @@ var StatusStep = () => {
4072
4232
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-subtitle", children: "Your swap is being processed\u2026" }),
4073
4233
  explorerUrl && /* @__PURE__ */ jsx8(ExplorerLink, { url: explorerUrl })
4074
4234
  ] }),
4075
- isComplete && /* @__PURE__ */ jsxs8(Fragment4, { children: [
4235
+ isComplete && /* @__PURE__ */ jsxs8(Fragment5, { children: [
4076
4236
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-icon-circle ssw-ic-success", children: /* @__PURE__ */ jsx8(
4077
4237
  "svg",
4078
4238
  {
@@ -4093,7 +4253,7 @@ var StatusStep = () => {
4093
4253
  buyAsset.symbol
4094
4254
  ] }),
4095
4255
  explorerUrl && /* @__PURE__ */ jsx8(ExplorerLink, { url: explorerUrl }),
4096
- /* @__PURE__ */ jsx8("div", { className: "ssw-step-actions", children: /* @__PURE__ */ jsx8(
4256
+ !isPayment && /* @__PURE__ */ jsx8("div", { className: "ssw-step-actions", children: /* @__PURE__ */ jsx8(
4097
4257
  "button",
4098
4258
  {
4099
4259
  className: "ssw-action-btn",
@@ -4103,7 +4263,7 @@ var StatusStep = () => {
4103
4263
  }
4104
4264
  ) })
4105
4265
  ] }),
4106
- isError && /* @__PURE__ */ jsxs8(Fragment4, { children: [
4266
+ isError && /* @__PURE__ */ jsxs8(Fragment5, { children: [
4107
4267
  /* @__PURE__ */ jsx8("div", { className: "ssw-step-icon-circle ssw-ic-error", children: /* @__PURE__ */ jsxs8(
4108
4268
  "svg",
4109
4269
  {
@@ -4146,10 +4306,10 @@ var StatusStep = () => {
4146
4306
  };
4147
4307
 
4148
4308
  // src/components/TokenSelectModal.tsx
4149
- import { bnOrZero as bnOrZero3 } from "@shapeshiftoss/utils";
4309
+ import { bnOrZero as bnOrZero2 } from "@shapeshiftoss/utils";
4150
4310
  import { useCallback as useCallback9, useEffect as useEffect8, useMemo as useMemo11, useRef as useRef6, useState as useState6 } from "react";
4151
4311
  import { Virtuoso } from "react-virtuoso";
4152
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
4312
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
4153
4313
  var VISIBLE_BUFFER = 10;
4154
4314
  var useLockBodyScroll3 = (isLocked) => {
4155
4315
  useEffect8(() => {
@@ -4330,7 +4490,7 @@ var TokenSelectModal = ({
4330
4490
  const balance = balances[asset.assetId];
4331
4491
  if (balance && balance.balance !== "0") {
4332
4492
  const price = marketData?.[asset.assetId]?.price;
4333
- const fiatValue = price ? bnOrZero3(balance.balance).div(bnOrZero3(10).pow(asset.precision)).times(bnOrZero3(price)).toNumber() : 0;
4493
+ const fiatValue = price ? bnOrZero2(balance.balance).div(bnOrZero2(10).pow(asset.precision)).times(bnOrZero2(price)).toNumber() : 0;
4334
4494
  withBalance.push({ asset, fiatValue });
4335
4495
  } else {
4336
4496
  withoutBalance.push(asset);
@@ -4534,10 +4694,10 @@ var TokenSelectModal = ({
4534
4694
  /* @__PURE__ */ jsx9("span", { className: "ssw-token-symbol", children: asset.symbol }),
4535
4695
  /* @__PURE__ */ jsx9("span", { className: "ssw-token-name", children: chainInfo?.name ?? asset.networkName ?? asset.name })
4536
4696
  ] }),
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: [
4697
+ /* @__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
4698
  marketData?.[asset.assetId]?.price && /* @__PURE__ */ jsxs9("span", { className: "ssw-token-fiat-value", children: [
4539
4699
  "$",
4540
- bnOrZero3(balance.balance).div(bnOrZero3(10).pow(asset.precision)).times(bnOrZero3(marketData[asset.assetId].price)).toNumber().toLocaleString(void 0, {
4700
+ bnOrZero2(balance.balance).div(bnOrZero2(10).pow(asset.precision)).times(bnOrZero2(marketData[asset.assetId].price)).toNumber().toLocaleString(void 0, {
4541
4701
  minimumFractionDigits: 2,
4542
4702
  maximumFractionDigits: 2
4543
4703
  })
@@ -4679,8 +4839,11 @@ var SwapWidgetContent = ({
4679
4839
  showPoweredBy,
4680
4840
  showConnectButton,
4681
4841
  isBuyAssetLocked,
4842
+ isBuyAmountLocked,
4843
+ isReceiveAddressLocked,
4844
+ isPayment,
4682
4845
  partnerCode,
4683
- allowShapeshiftRedirect,
4846
+ canRedirectToShapeshift,
4684
4847
  onSwapSuccess,
4685
4848
  onSwapError,
4686
4849
  sellFilters,
@@ -4704,11 +4867,12 @@ var SwapWidgetContent = ({
4704
4867
  handleSellAssetSelect,
4705
4868
  handleBuyAssetSelect,
4706
4869
  handleSellAmountChange,
4870
+ handleBuyAmountChange,
4707
4871
  handleToggleSellFiat,
4708
4872
  handleSelectRate,
4709
4873
  handleSlippageChange,
4710
4874
  handleButtonClick
4711
- } = useSwapHandlers({ partnerCode, allowShapeshiftRedirect });
4875
+ } = useSwapHandlers({ partnerCode, allowShapeshiftRedirect: canRedirectToShapeshift });
4712
4876
  useSwapQuoting({ apiClient, rates, sellAssetBalance });
4713
4877
  useSwapApproval();
4714
4878
  useSwapExecution();
@@ -4804,17 +4968,20 @@ var SwapWidgetContent = ({
4804
4968
  displayValues,
4805
4969
  onOpenTokenModal: setTokenModalType,
4806
4970
  onSellAmountChange: handleSellAmountChange,
4971
+ onBuyAmountChange: handleBuyAmountChange,
4807
4972
  onToggleSellFiat: handleToggleSellFiat,
4808
4973
  onSwapTokens: handleSwapTokens,
4809
4974
  onSelectRate: handleSelectRate,
4810
4975
  onButtonClick: handleButtonClick,
4811
4976
  isBuyAssetLocked,
4812
- allowShapeshiftRedirect
4977
+ isBuyAmountLocked,
4978
+ isReceiveAddressLocked,
4979
+ allowShapeshiftRedirect: canRedirectToShapeshift
4813
4980
  }
4814
4981
  ),
4815
4982
  (state.matches("approval_needed") || state.matches("approving")) && /* @__PURE__ */ jsx11(ApprovalStep, {}),
4816
4983
  state.matches("executing") && /* @__PURE__ */ jsx11(ExecutionStep, {}),
4817
- (state.matches("polling_status") || state.matches("complete") || state.matches("error")) && /* @__PURE__ */ jsx11(StatusStep, {})
4984
+ (state.matches("polling_status") || state.matches("complete") || state.matches("error")) && /* @__PURE__ */ jsx11(StatusStep, { isPayment })
4818
4985
  ] }),
4819
4986
  showPoweredBy && /* @__PURE__ */ jsxs11("div", { className: "ssw-powered-by", children: [
4820
4987
  "Powered by",
@@ -4843,7 +5010,7 @@ var SwapWidgetContent = ({
4843
5010
  disabledChainIds: (tokenModalType === "buy" ? buyFilters : sellFilters).disabledChainIds ?? [],
4844
5011
  allowedChainIds: (tokenModalType === "buy" ? buyFilters : sellFilters).allowedChainIds,
4845
5012
  allowedAssetIds: (tokenModalType === "buy" ? buyFilters : sellFilters).allowedAssetIds,
4846
- allowShapeshiftRedirect
5013
+ allowShapeshiftRedirect: canRedirectToShapeshift
4847
5014
  }
4848
5015
  ),
4849
5016
  /* @__PURE__ */ jsx11(
@@ -4867,6 +5034,10 @@ var SwapWidgetCore = ({
4867
5034
  showPoweredBy,
4868
5035
  showConnectButton,
4869
5036
  isBuyAssetLocked,
5037
+ defaultReceiveAddress,
5038
+ isReceiveAddressLocked,
5039
+ defaultBuyAmountCryptoBaseUnit,
5040
+ isBuyAmountLocked,
4870
5041
  partnerCode,
4871
5042
  allowShapeshiftRedirect,
4872
5043
  onSwapSuccess,
@@ -4880,7 +5051,9 @@ var SwapWidgetCore = ({
4880
5051
  const evm = useEvmSigning();
4881
5052
  const bitcoin3 = useBitcoinSigning();
4882
5053
  const solana3 = useSolanaSigning();
4883
- const [customReceiveAddress, setCustomReceiveAddress] = useState8("");
5054
+ const [customReceiveAddress, setCustomReceiveAddress] = useState8(
5055
+ defaultReceiveAddress ?? ""
5056
+ );
4884
5057
  const sellChainId = SwapMachineCtx.useSelector((s) => s.context.sellAsset.chainId);
4885
5058
  const buyChainId = SwapMachineCtx.useSelector((s) => s.context.buyAsset.chainId);
4886
5059
  const sellChainType = getChainType(sellChainId);
@@ -4913,14 +5086,30 @@ var SwapWidgetCore = ({
4913
5086
  () => addressForChain(buyChainType),
4914
5087
  [addressForChain, buyChainType]
4915
5088
  );
4916
- const isCustomReceiveAddressValid = useMemo12(
4917
- () => !!customReceiveAddress && validateAddress(customReceiveAddress, buyChainId).valid,
4918
- [customReceiveAddress, buyChainId]
4919
- );
4920
5089
  const receiveAddress = useMemo12(
4921
- () => isCustomReceiveAddressValid ? customReceiveAddress : walletReceiveAddress,
4922
- [isCustomReceiveAddressValid, customReceiveAddress, walletReceiveAddress]
5090
+ () => resolveReceiveAddress({
5091
+ isLocked: isReceiveAddressLocked,
5092
+ defaultAddress: defaultReceiveAddress,
5093
+ customAddress: customReceiveAddress,
5094
+ walletAddress: walletReceiveAddress,
5095
+ buyChainId
5096
+ }),
5097
+ [
5098
+ isReceiveAddressLocked,
5099
+ defaultReceiveAddress,
5100
+ customReceiveAddress,
5101
+ walletReceiveAddress,
5102
+ buyChainId
5103
+ ]
4923
5104
  );
5105
+ useEffect10(() => {
5106
+ if (!isBuyAmountLocked) return;
5107
+ actorRef.send({
5108
+ type: "SET_BUY_AMOUNT",
5109
+ amount: defaultBuyAmountCryptoBaseUnit ? formatAmountForInput(defaultBuyAmountCryptoBaseUnit, defaultBuyAsset.precision) : "",
5110
+ amountBaseUnit: defaultBuyAmountCryptoBaseUnit
5111
+ });
5112
+ }, [isBuyAmountLocked, defaultBuyAmountCryptoBaseUnit, defaultBuyAsset.precision, actorRef]);
4924
5113
  const initialSyncRef = useRef7(false);
4925
5114
  useEffect10(() => {
4926
5115
  if (initialSyncRef.current) return;
@@ -4928,6 +5117,11 @@ var SwapWidgetCore = ({
4928
5117
  actorRef.send({ type: "SET_SELL_ASSET", asset: defaultSellAsset });
4929
5118
  actorRef.send({ type: "SET_BUY_ASSET", asset: defaultBuyAsset });
4930
5119
  actorRef.send({ type: "SET_SLIPPAGE", slippage: defaultSlippage });
5120
+ actorRef.send({
5121
+ type: "SET_BUY_AMOUNT",
5122
+ amount: defaultBuyAmountCryptoBaseUnit ? formatAmountForInput(defaultBuyAmountCryptoBaseUnit, defaultBuyAsset.precision) : "",
5123
+ amountBaseUnit: defaultBuyAmountCryptoBaseUnit
5124
+ });
4931
5125
  }, [actorRef]);
4932
5126
  useEffect10(() => {
4933
5127
  actorRef.send({ type: "SET_SEND_ADDRESS", address: sendAddress });
@@ -4944,6 +5138,7 @@ var SwapWidgetCore = ({
4944
5138
  sendAddress,
4945
5139
  receiveAddress,
4946
5140
  isReceiveAddressResolving,
5141
+ isReceiveAddressBlocked: isReceiveAddressLocked && !receiveAddress,
4947
5142
  customReceiveAddress,
4948
5143
  setCustomReceiveAddress,
4949
5144
  evm,
@@ -4954,12 +5149,16 @@ var SwapWidgetCore = ({
4954
5149
  sendAddress,
4955
5150
  receiveAddress,
4956
5151
  isReceiveAddressResolving,
5152
+ isReceiveAddressLocked,
4957
5153
  customReceiveAddress,
4958
5154
  evm,
4959
5155
  bitcoin3,
4960
5156
  solana3
4961
5157
  ]
4962
5158
  );
5159
+ const hasLockedBuyAmount = isBuyAmountLocked && !!defaultBuyAmountCryptoBaseUnit;
5160
+ const isPayment = hasLockedBuyAmount && isReceiveAddressLocked && !!defaultReceiveAddress;
5161
+ const canRedirectToShapeshift = allowShapeshiftRedirect && !hasLockedBuyAmount && !isReceiveAddressLocked;
4963
5162
  return /* @__PURE__ */ jsx11(SwapWalletProvider, { value: walletValue, children: /* @__PURE__ */ jsx11(
4964
5163
  SwapWidgetContent,
4965
5164
  {
@@ -4967,9 +5166,12 @@ var SwapWidgetCore = ({
4967
5166
  theme,
4968
5167
  showPoweredBy,
4969
5168
  showConnectButton,
4970
- isBuyAssetLocked,
5169
+ isBuyAssetLocked: isBuyAssetLocked || hasLockedBuyAmount,
5170
+ isBuyAmountLocked,
5171
+ isReceiveAddressLocked,
5172
+ isPayment,
4971
5173
  partnerCode,
4972
- allowShapeshiftRedirect,
5174
+ canRedirectToShapeshift,
4973
5175
  onSwapSuccess,
4974
5176
  onSwapError,
4975
5177
  sellFilters,
@@ -4998,6 +5200,10 @@ var SwapWidget = (props) => {
4998
5200
  showPoweredBy: props.showPoweredBy ?? true,
4999
5201
  showConnectButton: props.showConnectButton ?? true,
5000
5202
  isBuyAssetLocked: props.isBuyAssetLocked ?? false,
5203
+ defaultReceiveAddress: props.defaultReceiveAddress,
5204
+ isReceiveAddressLocked: props.isReceiveAddressLocked ?? false,
5205
+ defaultBuyAmountCryptoBaseUnit: props.defaultBuyAmountCryptoBaseUnit,
5206
+ isBuyAmountLocked: props.isBuyAmountLocked ?? false,
5001
5207
  partnerCode: props.partnerCode,
5002
5208
  allowShapeshiftRedirect: props.allowShapeshiftRedirect ?? true,
5003
5209
  onSwapSuccess: props.onSwapSuccess,