From 208dacd858deec372b128b282eb37360c3e01ebe Mon Sep 17 00:00:00 2001 From: Nejc Date: Wed, 15 Nov 2023 21:10:11 +0900 Subject: [PATCH] feat: Add info to the Bedeem from Backstop pool UI (#291) * feat: backstop pool withdraw info * refactor: fee * feat: token price on redeem ui --------- Co-authored-by: Gonza Montiel --- src/components/Asset/Price/index.tsx | 42 +++++++++-------- .../Backstop/WithdrawLiquidity/index.tsx | 17 +++++-- .../Backstop/WithdrawLiquidity/schema.ts | 2 + .../Pools/Backstop/WithdrawLiquidity/types.ts | 1 + .../WithdrawLiquidity/useWithdrawLiquidity.ts | 45 ++++++++++++++----- src/components/nabla/Pools/Backstop/index.tsx | 2 +- .../nabla/Pools/Swap/Redeem/index.tsx | 17 ++++++- .../Pools/Swap/WithdrawLiquidity/index.tsx | 14 +++++- .../nabla/Pools/TokenAmount/index.tsx | 44 ++++++++++++++++++ src/constants/cache.ts | 1 + src/hooks/nabla/useSharesTargetWorth.ts | 26 +++++++++++ 11 files changed, 171 insertions(+), 40 deletions(-) create mode 100644 src/components/nabla/Pools/TokenAmount/index.tsx create mode 100644 src/hooks/nabla/useSharesTargetWorth.ts diff --git a/src/components/Asset/Price/index.tsx b/src/components/Asset/Price/index.tsx index d633bb50..eb73fa4a 100644 --- a/src/components/Asset/Price/index.tsx +++ b/src/components/Asset/Price/index.tsx @@ -4,7 +4,7 @@ import { usePriceFetcher } from '../../../hooks/usePriceFetcher'; import { Skeleton } from '../../Skeleton'; export type TokenPriceProps = { - address: string; + address?: string; symbol: string; //amount?: number; prefix?: ReactNode; @@ -13,26 +13,24 @@ export type TokenPriceProps = { fallback?: ReactNode; }; -const TokenPrice = memo( - ({ address, symbol, prefix = '', loader, fallback = null }: TokenPriceProps): JSX.Element | null => { - const { pricesCache } = usePriceFetcher(); - const [price, setPrice] = useState(null); - useEffect(() => { - const run = async () => { - const p = (await pricesCache)[symbol]; - setPrice(p); - }; - run(); - }, [pricesCache, symbol]); +const TokenPrice = memo(({ symbol, prefix = null, loader, fallback = null }: TokenPriceProps): JSX.Element | null => { + const { pricesCache } = usePriceFetcher(); + const [price, setPrice] = useState(null); + useEffect(() => { + const run = async () => { + const p = (await pricesCache)[symbol]; + setPrice(p); + }; + run(); + }, [pricesCache, symbol]); - const isLoading = price === null; - if (isLoading) return <>{loader} || 10000; - if (!price) return <>{fallback}; - return ( - - {prefix}${price} - - ); - }, -); + const isLoading = price === null; + if (isLoading) return <>{loader} || 10000; + if (!price) return <>{fallback}; + return ( + + {prefix}${price} + + ); +}); export default TokenPrice; diff --git a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/index.tsx b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/index.tsx index ed323f00..304ea69f 100644 --- a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/index.tsx +++ b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/index.tsx @@ -3,10 +3,12 @@ import { ChangeEvent } from 'preact/compat'; import { Button, Range } from 'react-daisyui'; import { PoolProgress } from '../..'; import { BackstopPool } from '../../../../../../gql/graphql'; +import { backstopPoolAbi } from '../../../../../contracts/nabla/BackstopPool'; import { calcSharePercentage, minMax } from '../../../../../helpers/calc'; import { FixedU128Decimals, nativeToDecimal, roundNumber } from '../../../../../shared/parseNumbers'; import { numberLoader } from '../../../../Loader'; import TransactionProgress from '../../../../Transaction/Progress'; +import TokenAmount from '../../TokenAmount'; import { useWithdrawLiquidity } from './useWithdrawLiquidity'; export type WithdrawLiquidityProps = { @@ -22,7 +24,8 @@ const WithdrawLiquidity = ({ data }: WithdrawLiquidityProps): JSX.Element | null depositQuery, amount, form: { register, setValue }, - } = useWithdrawLiquidity(data.id, data.token.id); + selectedPool, + } = useWithdrawLiquidity(data); const deposit = depositQuery.balance || 0; const hideCss = !mutation.isIdle ? 'hidden' : ''; @@ -86,8 +89,16 @@ const WithdrawLiquidity = ({ data }: WithdrawLiquidityProps): JSX.Element | null
-
Fee
-
{'! TODO'}
+
Amount
+
+ +
Deposit
diff --git a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/schema.ts b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/schema.ts index e981907c..b3f8c325 100644 --- a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/schema.ts +++ b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/schema.ts @@ -3,6 +3,8 @@ import { transformNumber } from '../../../../../helpers/yup'; import { WithdrawLiquidityValues } from './types'; const schema = Yup.object().shape({ + address: Yup.string().nullable().min(5), + slippage: Yup.number().nullable().transform(transformNumber), amount: Yup.number().positive().required().transform(transformNumber), }); diff --git a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/types.ts b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/types.ts index 5780abd9..787113ec 100644 --- a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/types.ts +++ b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/types.ts @@ -1,3 +1,4 @@ export type WithdrawLiquidityValues = { amount: number; + address: string; }; diff --git a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/useWithdrawLiquidity.ts b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/useWithdrawLiquidity.ts index fddb7be0..e3f9b2ed 100644 --- a/src/components/nabla/Pools/Backstop/WithdrawLiquidity/useWithdrawLiquidity.ts +++ b/src/components/nabla/Pools/Backstop/WithdrawLiquidity/useWithdrawLiquidity.ts @@ -1,6 +1,8 @@ import { yupResolver } from '@hookform/resolvers/yup'; import { useQueryClient } from '@tanstack/react-query'; +import { useMemo } from 'preact/compat'; import { useForm, useWatch } from 'react-hook-form'; +import { BackstopPool, SwapPool } from '../../../../../../gql/graphql'; import { cacheKeys } from '../../../../../constants/cache'; import { backstopPoolAbi } from '../../../../../contracts/nabla/BackstopPool'; import { subtractPercentage } from '../../../../../helpers/calc'; @@ -12,7 +14,10 @@ import { useContractWrite } from '../../../../../shared/useContractWrite'; import schema from './schema'; import { WithdrawLiquidityValues } from './types'; -export const useWithdrawLiquidity = (poolAddress: string, tokenAddress: string) => { +export const useWithdrawLiquidity = (pool: BackstopPool) => { + const { id: poolAddress, token, router } = pool; + const tokenAddress = token.id; + const swapPools = router?.swapPools; const queryClient = useQueryClient(); const { indexerUrl } = useGetAppDataByTenant('nabla').data || {}; const toggle = useModalToggle(); @@ -25,6 +30,20 @@ export const useWithdrawLiquidity = (poolAddress: string, tokenAddress: string) defaultValues: {}, }); + const amount = + Number( + useWatch({ + control: form.control, + name: 'amount', + defaultValue: 0, + }), + ) || 0; + + const address = useWatch({ + control: form.control, + name: 'address', + }); + const mutation = useContractWrite({ abi: backstopPoolAbi, address: poolAddress, @@ -47,14 +66,20 @@ export const useWithdrawLiquidity = (poolAddress: string, tokenAddress: string) ]), ); - const amount = - Number( - useWatch({ - control: form.control, - name: 'amount', - defaultValue: 0, - }), - ) || 0; + const pools = useMemo( + () => + [ + { + id: '', + token: { + ...token, + id: '', + }, + } as SwapPool, + ].concat(swapPools || []), + [swapPools, token], + ); + const selectedPool = useMemo(() => pools.find((t) => t.id === address) || pools[0], [address, pools]); - return { form, amount, mutation, onSubmit, toggle, balanceQuery, depositQuery }; + return { form, amount, mutation, onSubmit, toggle, balanceQuery, depositQuery, selectedPool, pools }; }; diff --git a/src/components/nabla/Pools/Backstop/index.tsx b/src/components/nabla/Pools/Backstop/index.tsx index b33acc56..8abe8f5e 100644 --- a/src/components/nabla/Pools/Backstop/index.tsx +++ b/src/components/nabla/Pools/Backstop/index.tsx @@ -12,7 +12,7 @@ const BackstopPoolsBody = (): JSX.Element | null => { const { data, isLoading } = useBackstopPools(); if (isLoading) return ; - const pool = data?.[0]; + const pool = data?.[data.length - 1]; if (!pool) return null; // TODO: empty state UI return ( <> diff --git a/src/components/nabla/Pools/Swap/Redeem/index.tsx b/src/components/nabla/Pools/Swap/Redeem/index.tsx index 5aaa542f..d7bf2e63 100644 --- a/src/components/nabla/Pools/Swap/Redeem/index.tsx +++ b/src/components/nabla/Pools/Swap/Redeem/index.tsx @@ -2,8 +2,10 @@ import { ArrowLeftIcon } from '@heroicons/react/24/outline'; import { ChangeEvent } from 'preact/compat'; import { Button, Range } from 'react-daisyui'; import { PoolProgress } from '../..'; +import { config } from '../../../../../config'; import { calcSharePercentage, minMax } from '../../../../../helpers/calc'; import { FixedU128Decimals, nativeToDecimal, roundNumber } from '../../../../../shared/parseNumbers'; +import TokenPrice from '../../../../Asset/Price'; import { numberLoader } from '../../../../Loader'; import TransactionProgress from '../../../../Transaction/Progress'; import { TransactionSettingsDropdown } from '../../../../Transaction/Settings'; @@ -100,8 +102,19 @@ const Redeem = ({ data }: RedeemProps): JSX.Element | null => {
-
Fee
-
{'! TODO'}
+
Security fee
+
{config.backstop.securityFee * 100}%
+
+
+
Price
+
+ +
Deposit
diff --git a/src/components/nabla/Pools/Swap/WithdrawLiquidity/index.tsx b/src/components/nabla/Pools/Swap/WithdrawLiquidity/index.tsx index 60f0ad5c..edfc00cf 100644 --- a/src/components/nabla/Pools/Swap/WithdrawLiquidity/index.tsx +++ b/src/components/nabla/Pools/Swap/WithdrawLiquidity/index.tsx @@ -2,10 +2,12 @@ import { ArrowLeftIcon } from '@heroicons/react/24/outline'; import { ChangeEvent } from 'preact/compat'; import { Button, Range } from 'react-daisyui'; import { PoolProgress } from '../..'; +import { swapPoolAbi } from '../../../../../contracts/nabla/SwapPool'; import { calcSharePercentage, minMax } from '../../../../../helpers/calc'; import { FixedU128Decimals, nativeToDecimal, roundNumber } from '../../../../../shared/parseNumbers'; import { numberLoader } from '../../../../Loader'; import TransactionProgress from '../../../../Transaction/Progress'; +import TokenAmount from '../../TokenAmount'; import { SwapPoolColumn } from '../columns'; import { ModalTypes } from '../Modals/types'; import { useWithdrawLiquidity } from './useWithdrawLiquidity'; @@ -86,8 +88,16 @@ const WithdrawLiquidity = ({ data }: WithdrawLiquidityProps): JSX.Element | null
-
Fee
-
{'! TODO'}
+
Amount
+
+ +
Deposit
diff --git a/src/components/nabla/Pools/TokenAmount/index.tsx b/src/components/nabla/Pools/TokenAmount/index.tsx new file mode 100644 index 00000000..b5dfc5ed --- /dev/null +++ b/src/components/nabla/Pools/TokenAmount/index.tsx @@ -0,0 +1,44 @@ +import { Abi } from '@polkadot/api-contract'; +import { useSharesTargetWorth } from '../../../../hooks/nabla/useSharesTargetWorth'; +import { useDebouncedValue } from '../../../../hooks/useDebouncedValue'; +import { FixedU128Decimals, nativeToDecimal, prettyNumbers } from '../../../../shared/parseNumbers'; +import { Skeleton } from '../../../Skeleton'; + +export interface TokenAmountProps { + address: string; + abi?: Abi | Record; + amount?: number; + debounce?: number; + loader?: boolean; + symbol?: ReactNode; + fallback?: string | number; +} + +const TokenAmount = ({ + symbol, + abi, + fallback, + address, + amount = 0, + loader = true, + debounce = 800, +}: TokenAmountProps): JSX.Element | null => { + const debouncedAmount = useDebouncedValue(amount, debounce); + const arg = debounce ? debouncedAmount : amount; + const { isLoading, data } = useSharesTargetWorth({ + address, + abi, + amount: arg, + }); + if (isLoading || (!!debounce && amount !== debouncedAmount)) { + return loader ? 10000 : null; + } + + return ( + + {data ? prettyNumbers(nativeToDecimal(data || '0', FixedU128Decimals).toNumber()) : fallback ?? null} + {symbol ? symbol : null} + + ); +}; +export default TokenAmount; diff --git a/src/constants/cache.ts b/src/constants/cache.ts index 35d15096..108fbc90 100644 --- a/src/constants/cache.ts +++ b/src/constants/cache.ts @@ -11,6 +11,7 @@ export const cacheKeys = { walletBalance: 'walletBalance', walletBalances: 'walletBalances', tokenOutAmount: 'tokenOutAmount', + sharesTargetWorth: 'sharesTargetWorth', }; export type QueryOptions = Partial< diff --git a/src/hooks/nabla/useSharesTargetWorth.ts b/src/hooks/nabla/useSharesTargetWorth.ts new file mode 100644 index 00000000..d0225aed --- /dev/null +++ b/src/hooks/nabla/useSharesTargetWorth.ts @@ -0,0 +1,26 @@ +import { Abi } from '@polkadot/api-contract'; +import { cacheKeys, inactiveOptions } from '../../constants/cache'; +import { swapPoolAbi } from '../../contracts/nabla/SwapPool'; +import { QueryOptions } from '../../shared/helpers'; +import { useContract } from '../../shared/useContract'; + +export type UseSharesTargetWorthProps = { + address: string | undefined; + amount: number | undefined; + abi?: Abi | Dict; +}; + +export const useSharesTargetWorth = ( + { address, amount, abi = swapPoolAbi }: UseSharesTargetWorthProps, + options?: QueryOptions, +) => { + return useContract([cacheKeys.sharesTargetWorth], { + ...inactiveOptions['1m'], + ...options, + address, + abi, + method: 'sharesTargetWorth', + args: [amount], + enabled: Boolean(address && amount && options?.enabled !== false), + }); +};