diff --git a/packages/web/components/bridge/immersive/amount-and-confirmation-screen.tsx b/packages/web/components/bridge/immersive/amount-and-confirmation-screen.tsx new file mode 100644 index 0000000000..49e1ede849 --- /dev/null +++ b/packages/web/components/bridge/immersive/amount-and-confirmation-screen.tsx @@ -0,0 +1,126 @@ +import { CoinPretty } from "@keplr-wallet/unit"; +import { BridgeChain } from "@osmosis-labs/bridge"; +import { MinimalAsset } from "@osmosis-labs/types"; +import { isNil } from "@osmosis-labs/utils"; +import { observer } from "mobx-react-lite"; +import { useState } from "react"; + +import { AmountScreen } from "~/components/bridge/immersive/amount-screen"; +import { ImmersiveBridgeScreens } from "~/components/bridge/immersive/immersive-bridge"; +import { useBridgeQuote } from "~/components/bridge/immersive/use-bridge-quote"; +import { useBridgesSupportedAssets } from "~/components/bridge/immersive/use-bridges-supported-assets"; +import { Screen } from "~/components/screen-manager"; +import { Button } from "~/components/ui/button"; +import { useEvmWalletAccount } from "~/hooks/evm-wallet"; +import { useStore } from "~/stores"; + +export type SupportedAsset = ReturnType< + typeof useBridgesSupportedAssets +>["supportedAssetsByChainId"][string][number]; + +export type SupportedAssetWithAmount = SupportedAsset & { amount: CoinPretty }; + +interface AmountAndConfirmationScreenProps { + direction: "deposit" | "withdraw"; + selectedAssetDenom: string | undefined; + onClose: () => void; +} + +export const AmountAndConfirmationScreen = observer( + ({ + direction, + selectedAssetDenom, + onClose, + }: AmountAndConfirmationScreenProps) => { + const { accountStore } = useStore(); + + const [sourceAsset, setSourceAsset] = useState(); + const [destinationAsset, setDestinationAsset] = useState(); + const [fromChain, setFromChain] = useState(); + const [toChain, setToChain] = useState(); + + const [cryptoAmount, setCryptoAmount] = useState("0"); + const [fiatAmount, setFiatAmount] = useState("0"); + + // Wallets + const destinationAccount = accountStore.getWallet( + accountStore.osmosisChainId + ); + const { address: evmAddress } = useEvmWalletAccount(); + + const sourceChain = direction === "deposit" ? fromChain : toChain; + const destinationChain = direction === "deposit" ? toChain : fromChain; + + const cosmosCounterpartyAccount = + sourceChain?.chainType === "evm" || isNil(sourceChain) + ? undefined + : accountStore.getWallet(sourceChain.chainId); + + const sourceAddress = + sourceChain?.chainType === "evm" + ? evmAddress + : cosmosCounterpartyAccount?.address; + + const quote = useBridgeQuote({ + destinationAddress: destinationAccount?.address, + destinationChain, + destinationAsset: destinationAsset + ? { + address: destinationAsset.coinMinimalDenom, + decimals: destinationAsset.coinDecimals, + denom: destinationAsset.coinDenom, + } + : undefined, + sourceAddress, + sourceChain, + sourceAsset, + direction, + onRequestClose: onClose, + inputAmount: cryptoAmount, + bridges: sourceAsset?.supportedProviders, + onTransfer: () => { + setCryptoAmount("0"); + setFiatAmount("0"); + }, + }); + + if (!selectedAssetDenom) return; + + return ( + <> + + {() => ( + + )} + + + {({ goBack }) => ( +
+
Step 3: Review
+ + +
+ )} +
+ + ); + } +); diff --git a/packages/web/components/bridge/immersive/amount-screen.tsx b/packages/web/components/bridge/immersive/amount-screen.tsx index d275404294..a26163fa9d 100644 --- a/packages/web/components/bridge/immersive/amount-screen.tsx +++ b/packages/web/components/bridge/immersive/amount-screen.tsx @@ -24,6 +24,7 @@ import { } from "react"; import { Icon } from "~/components/assets"; +import { SupportedAssetWithAmount } from "~/components/bridge/immersive/amount-and-confirmation-screen"; import { BridgeNetworkSelectModal } from "~/components/bridge/immersive/bridge-network-select-modal"; import { BridgeProviderDropdown } from "~/components/bridge/immersive/bridge-provider-dropdown"; import { BridgeQuoteRemainingTime } from "~/components/bridge/immersive/bridge-quote-remaining-time"; @@ -56,24 +57,75 @@ interface AmountScreenProps { selectedDenom: string; /** - * Includes both the canonical asset and its variants. + * Chain taking into account the direction. */ - assetsInOsmosis: MinimalAsset[] | undefined; + sourceChain: BridgeChain | undefined; + destinationChain: BridgeChain | undefined; - onClose: () => void; + fromChain: BridgeChain | undefined; + setFromChain: (chain: BridgeChain) => void; + toChain: BridgeChain | undefined; + setToChain: (chain: BridgeChain) => void; + + sourceAsset: SupportedAssetWithAmount | undefined; + setSourceAsset: (asset: SupportedAssetWithAmount | undefined) => void; + destinationAsset: MinimalAsset | undefined; + setDestinationAsset: (asset: MinimalAsset | undefined) => void; + + cryptoAmount: string; + fiatAmount: string; + setCryptoAmount: (amount: string) => void; + setFiatAmount: (amount: string) => void; + + quote: ReturnType; } export const AmountScreen = observer( ({ direction, - assetsInOsmosis, selectedDenom, - onClose, + + sourceChain, + + fromChain, + setFromChain, + toChain, + setToChain, + + sourceAsset, + setSourceAsset, + + destinationAsset, + setDestinationAsset, + + cryptoAmount, + setCryptoAmount, + fiatAmount, + setFiatAmount, + + quote, }: AmountScreenProps) => { const { accountStore } = useStore(); const { onOpenWalletSelect } = useWalletSelect(); const { t } = useTranslation(); + const { + selectedQuote, + successfulQuotes, + setSelectedBridgeProvider, + buttonErrorMessage, + buttonText, + isLoadingBridgeQuote, + isLoadingBridgeTransaction, + isRefetchingQuote, + selectedQuoteUpdatedAt, + refetchInterval, + isInsufficientBal, + isInsufficientFee, + warnUserOfPriceImpact, + warnUserOfSlippage, + } = quote; + const { accountActionButton: connectWalletButton, walletConnected } = useConnectWalletModalRedirect( { @@ -82,18 +134,9 @@ export const AmountScreen = observer( noop ); - const [sourceAsset, setSourceAsset] = useState< - SupportedAsset & { amount: CoinPretty } - >(); - const [destinationAsset, setDestinationAsset] = useState(); - const [fromChain, setFromChain] = useState(); - const [toChain, setToChain] = useState(); - const [areMoreOptionsVisible, setAreMoreOptionsVisible] = useState(false); const [inputUnit, setInputUnit] = useState<"crypto" | "fiat">("fiat"); - const [cryptoAmount, setCryptoAmount] = useState("0"); - const [fiatAmount, setFiatAmount] = useState("0"); const { isOpen: isBridgeWalletSelectOpen, onClose: onCloseBridgeWalletSelect, @@ -110,9 +153,6 @@ export const AmountScreen = observer( isConnected: isEvmWalletConnected, } = useEvmWalletAccount(); - const sourceChain = direction === "deposit" ? fromChain : toChain; - const destinationChain = direction === "deposit" ? toChain : fromChain; - const cosmosCounterpartyAccountRepo = sourceChain?.chainType === "evm" || isNil(sourceChain) ? undefined @@ -127,6 +167,18 @@ export const AmountScreen = observer( ? evmAddress : cosmosCounterpartyAccount?.address; + const { data: assetsInOsmosis } = + api.edge.assets.getCanonicalAssetWithVariants.useQuery( + { + findMinDenomOrSymbol: selectedDenom!, + }, + { + enabled: !isNil(selectedDenom), + cacheTime: 10 * 60 * 1000, // 10 minutes + staleTime: 10 * 60 * 1000, // 10 minutes + } + ); + const { data: osmosisChain } = api.edge.chains.getChain.useQuery({ findChainNameOrId: accountStore.osmosisChainId, }); @@ -259,7 +311,7 @@ export const AmountScreen = observer( setDestinationAsset(destinationAsset); } - }, [assetsInOsmosis, selectedDenom, sourceAsset]); + }, [assetsInOsmosis, setDestinationAsset, sourceAsset]); /** * Set the osmosis chain based on the direction @@ -274,13 +326,7 @@ export const AmountScreen = observer( chainType: "cosmos", }); } - }, [ - accountStore.osmosisChainId, - direction, - fromChain, - osmosisChain, - toChain, - ]); + }, [direction, fromChain, osmosisChain, setFromChain, setToChain, toChain]); /** * Set the initial chain based on the direction. @@ -302,7 +348,14 @@ export const AmountScreen = observer( chainType: firstChain.chainType, } as BridgeChain); } - }, [direction, fromChain, supportedChains, toChain]); + }, [ + direction, + fromChain, + setFromChain, + setToChain, + supportedChains, + toChain, + ]); /** * Connect cosmos wallet to the counterparty chain @@ -376,44 +429,6 @@ export const AmountScreen = observer( toChain, ]); - const { - selectedQuote, - successfulQuotes, - setSelectedBridgeProvider, - buttonErrorMessage, - buttonText, - isLoadingBridgeQuote, - isLoadingBridgeTransaction, - isRefetchingQuote, - selectedQuoteUpdatedAt, - refetchInterval, - isInsufficientBal, - isInsufficientFee, - warnUserOfPriceImpact, - warnUserOfSlippage, - } = useBridgeQuote({ - destinationAddress: destinationAccount?.address, - destinationChain, - destinationAsset: destinationAsset - ? { - address: destinationAsset.coinMinimalDenom, - decimals: destinationAsset.coinDecimals, - denom: destinationAsset.coinDenom, - } - : undefined, - sourceAddress, - sourceChain, - sourceAsset, - direction, - onRequestClose: onClose, - inputAmount: cryptoAmount, - bridges: sourceAsset?.supportedProviders, - onTransfer: () => { - setCryptoAmount("0"); - setFiatAmount("0"); - }, - }); - if ( isLoadingCanonicalAssetPrice || isNil(supportedAssets) || diff --git a/packages/web/components/bridge/immersive/immersive-bridge.tsx b/packages/web/components/bridge/immersive/immersive-bridge.tsx index b2e594d113..4b352da241 100644 --- a/packages/web/components/bridge/immersive/immersive-bridge.tsx +++ b/packages/web/components/bridge/immersive/immersive-bridge.tsx @@ -5,11 +5,11 @@ import { memo, PropsWithChildren, useState } from "react"; import { useLockBodyScroll } from "react-use"; import { Icon } from "~/components/assets"; -import { AmountScreen } from "~/components/bridge/immersive/amount-screen"; +import { AmountAndConfirmationScreen } from "~/components/bridge/immersive/amount-and-confirmation-screen"; import { AssetSelectScreen } from "~/components/bridge/immersive/asset-select-screen"; import { Screen, ScreenManager } from "~/components/screen-manager"; import { StepProgress } from "~/components/stepper/progress-bar"; -import { Button, IconButton } from "~/components/ui/button"; +import { IconButton } from "~/components/ui/button"; import { EventName } from "~/config"; import { useTranslation } from "~/hooks"; import { BridgeFlowProvider } from "~/hooks/bridge"; @@ -19,9 +19,8 @@ import { FiatRampKey } from "~/integrations"; import { ModalCloseButton } from "~/modals"; import { FiatOnrampSelectionModal } from "~/modals/fiat-on-ramp-selection"; import { FiatRampsModal } from "~/modals/fiat-ramps"; -import { api } from "~/utils/trpc"; -const enum ImmersiveBridgeScreens { +export const enum ImmersiveBridgeScreens { Asset = "0", Amount = "1", Review = "2", @@ -46,18 +45,6 @@ export const ImmersiveBridgeFlow = ({ const [selectedAssetDenom, setSelectedAssetDenom] = useState(); - const { data: canonicalAssetsWithVariants } = - api.edge.assets.getCanonicalAssetWithVariants.useQuery( - { - findMinDenomOrSymbol: selectedAssetDenom!, - }, - { - enabled: !isNil(selectedAssetDenom), - cacheTime: 10 * 60 * 1000, // 10 minutes - staleTime: 10 * 60 * 1000, // 10 minutes - } - ); - const [fiatRampParams, setFiatRampParams] = useState<{ fiatRampKey: FiatRampKey; assetKey: string; @@ -189,25 +176,11 @@ export const ImmersiveBridgeFlow = ({ /> )} - - {() => ( - setIsVisible(false)} - /> - )} - - - {({ goBack }) => ( -
-
Step 3: Review
- - -
- )} -
+