From 5ff6729096db9b7db4f55504ceaf9312541278e3 Mon Sep 17 00:00:00 2001 From: jackburrus Date: Mon, 27 Jan 2025 14:10:12 -0700 Subject: [PATCH 1/3] adds approval prompt --- .../longs/CloseLongForm/CloseLongForm.tsx | 170 ++++++++++++++---- 1 file changed, 140 insertions(+), 30 deletions(-) diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx index c030f5d4b..d6453e0be 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx @@ -2,6 +2,7 @@ import { fixed } from "@delvtech/fixed-point-wasm"; import { appConfig, getBaseToken, + getHyperdriveConfig, getToken, HyperdriveConfig, TokenConfig, @@ -12,19 +13,32 @@ import { isTestnetChain } from "src/chains/isTestnetChain"; import { ConnectWalletButton } from "src/ui/base/components/ConnectWallet"; import { LoadingButton } from "src/ui/base/components/LoadingButton"; import { PrimaryStat } from "src/ui/base/components/PrimaryStat"; +import { useFeatureFlag } from "src/ui/base/featureFlags/featureFlags"; import { formatBalance } from "src/ui/base/formatting/formatBalance"; import { useActiveItem } from "src/ui/base/hooks/useActiveItem"; import { useNumericInput } from "src/ui/base/hooks/useNumericInput"; +import { SwitchNetworksButton } from "src/ui/chains/SwitchChainButton/SwitchChainButton"; import { useCloseLong } from "src/ui/hyperdrive/longs/hooks/useCloseLong"; import { usePreviewCloseLong } from "src/ui/hyperdrive/longs/hooks/usePreviewCloseLong"; import { StatusCell } from "src/ui/hyperdrive/longs/StatusCell"; import { TransactionView } from "src/ui/hyperdrive/TransactionView"; -import { useTokenBalance } from "src/ui/token/hooks/useTokenBalance"; +// import { useCloseLongZap } from "src/ui/hyperdrive/zaps/hooks/useCloseLongZap"; +import { getDepositAssets } from "src/hyperdrive/getDepositAssets"; +import { ETH_MAGIC_NUMBER } from "src/token/ETH_MAGIC_NUMBER"; +import { getHasEnoughAllowance } from "src/token/getHasEnoughAllowance"; +import { InvalidTransactionButton } from "src/ui/hyperdrive/InvalidTransactionButton"; +import { ApproveTokenChoices } from "src/ui/token/ApproveTokenChoices"; +import { useTokenAllowance } from "src/ui/token/hooks/useTokenAllowance"; import { useTokenFiatPrice } from "src/ui/token/hooks/useTokenFiatPrice"; import { TokenInput } from "src/ui/token/TokenInput"; -import { TokenChoice, TokenPicker } from "src/ui/token/TokenPicker"; +import { + TokenChoice, + TokenPicker, + ZapsTokenPicker, +} from "src/ui/token/TokenPicker"; +import { useTokenList } from "src/ui/tokenlist/useTokenList"; import { formatUnits, parseUnits } from "viem"; -import { useAccount } from "wagmi"; +import { useAccount, useChainId } from "wagmi"; interface CloseLongFormProps { hyperdrive: HyperdriveConfig; @@ -38,13 +52,21 @@ export function CloseLongForm({ onCloseLong, }: CloseLongFormProps): ReactElement { const { address: account } = useAccount(); - + const connectedChainId = useChainId(); const defaultItems: TokenConfig[] = []; const baseToken = getBaseToken({ hyperdriveAddress: hyperdrive.address, hyperdriveChainId: hyperdrive.chainId, appConfig, }); + + const { isFlagEnabled: isZapsEnabled } = useFeatureFlag("zaps"); + + const { tokenList } = useTokenList({ + chainId: hyperdrive.chainId, + enabled: isZapsEnabled, + }); + if (hyperdrive.withdrawOptions.isBaseTokenWithdrawalEnabled) { defaultItems.push(baseToken); } @@ -58,23 +80,29 @@ export function CloseLongForm({ defaultItems.push(sharesToken); } - const { balance: baseTokenBalance } = useTokenBalance({ - account, - tokenAddress: hyperdrive.poolConfig.baseToken, - decimals: hyperdrive.decimals, - }); - const { activeItem: activeWithdrawToken, setActiveItemId: setActiveWithdrawToken, } = useActiveItem({ - items: defaultItems, + items: isZapsEnabled ? [...defaultItems, ...tokenList] : defaultItems, idField: "address", defaultActiveItemId: hyperdrive.withdrawOptions.isBaseTokenWithdrawalEnabled ? hyperdrive.poolConfig.baseToken : hyperdrive.poolConfig.vaultSharesToken, }); + const depositAssets = getDepositAssets( + getHyperdriveConfig({ + hyperdriveChainId: hyperdrive.chainId, + hyperdriveAddress: hyperdrive.address, + appConfig, + }), + ); + + const isZapToken = !depositAssets.some( + (asset) => asset.address === activeWithdrawToken.address, + ); + const { fiatPrice: activeWithdrawTokenPrice } = useTokenFiatPrice({ tokenAddress: activeWithdrawToken.address, chainId: activeWithdrawToken.chainId, @@ -130,14 +158,30 @@ export function CloseLongForm({ }, }); + // TODO: Implement zapping in upcoming PR + // const { closeLongZap, status: closeLongZapStatus } = useCloseLongZap({ + // hyperdriveAddress: hyperdrive.address, + // chainId: hyperdrive.chainId, + // maturityTime: long.maturity, + // tokenOut: activeWithdrawToken, + // bondAmountIn: bondAmountAsBigInt, + // minAmountOut: minAmountOutAfterSlippage, + // destination: account, + // }); + const withdrawTokenChoices: TokenChoice[] = []; if (hyperdrive.withdrawOptions.isBaseTokenWithdrawalEnabled) { withdrawTokenChoices.push({ tokenConfig: baseToken, - tokenBalance: baseTokenBalance?.value, }); } - + if (isZapsEnabled) { + tokenList?.map((tokenFromTokenList) => { + withdrawTokenChoices.push({ + tokenConfig: tokenFromTokenList, + }); + }); + } if (hyperdrive.withdrawOptions.isShareTokenWithdrawalEnabled) { withdrawTokenChoices.push({ // Safe to cast: sharesToken must be defined if its enabled for withdrawal @@ -149,6 +193,21 @@ export function CloseLongForm({ bondAmountAsBigInt && bondAmountAsBigInt > long.bondAmount ); + const { tokenAllowance: bondAllowance } = useTokenAllowance({ + account, + spender: hyperdrive.address, + tokenAddress: hyperdrive.address, + tokenChainId: hyperdrive.chainId, + enabled: !!account, + }); + // ETH doesn't require allowance + const requiresAllowance = activeWithdrawToken.address !== ETH_MAGIC_NUMBER; + const hasEnoughAllowance = getHasEnoughAllowance({ + requiresAllowance, + allowance: bondAllowance, + amount: bondAmountAsBigInt, + }); + // Plausible event props const formName = "Close Long"; const chainId = hyperdrive.chainId; @@ -194,23 +253,44 @@ export function CloseLongForm({ name={baseToken.symbol} inputLabel="You receive" token={ - { - window.plausible("formChange", { - props: { - inputName: "token", - inputValue: tokenAddress, - formName, - chainId, - poolAddress, - connectedWallet: account, - }, - }); - setActiveWithdrawToken(tokenAddress); - }} - /> + isZapsEnabled ? ( + { + window.plausible("formChange", { + props: { + inputName: "token", + inputValue: tokenAddress, + formName, + chainId, + poolAddress, + connectedWallet: account, + }, + }); + + setActiveWithdrawToken(tokenAddress); + }} + /> + ) : ( + { + window.plausible("formChange", { + props: { + inputName: "token", + inputValue: tokenAddress, + formName, + chainId, + poolAddress, + connectedWallet: account, + }, + }); + setActiveWithdrawToken(tokenAddress); + }} + /> + ) } value={ withdrawAmount @@ -295,9 +375,39 @@ export function CloseLongForm({ if (!account) { return ; } + + if (connectedChainId !== hyperdrive.chainId) { + return ( + + ); + } + + if (!!bondAmountAsBigInt && isAmountLargerThanPositionSize) { + return ( + + Insufficient balance + + ); + } + + if (isZapToken && !!bondAmountAsBigInt && !hasEnoughAllowance) { + return ( + + ); + } + if (closeLongStatus === "loading") { return ; } + return (