import _ from 'lodash' import { ETH, WETHE, WETH } from '@constants/tokens/bridgeable' import { AVWETH } from '@/constants/tokens/auxilliary' import { stringToBigInt, formatBigIntToString } from '@/utils/bigint/format' import { getTokenAllowance } from '@/utils/actions/getTokenAllowance' import { approve, deposit, emptyPoolDeposit, } from '@/utils/actions/approveAndDeposit' import { fetchBalance, getBalance, waitForTransaction, waitForTransactionReceipt, } from '@wagmi/core' import { getSwapDepositContractFields } from '@/utils/getSwapDepositContractFields' import { calculatePriceImpact } from '@/utils/priceImpact' import { transformCalculateLiquidityInput } from '@/utils/transformCalculateLiquidityInput' import { isTransactionReceiptError } from '@/utils/isTransactionReceiptError' import { isTransactionUserRejectedError } from '@/utils/isTransactionUserRejectedError' import { useState, useEffect } from 'react' import { useDispatch } from 'react-redux' import { DepositTokenInput } from '@components/TokenInput' import { Token } from '@types' import { resetPoolDeposit, setDepositQuote, setInputValue, setIsLoading, setPool, } from '@/slices/poolDepositSlice' import { fetchPoolData } from '@/slices/poolDataSlice' import { fetchPoolUserData } from '@/slices/poolUserDataSlice' import { fetchAndStoreSingleNetworkPortfolioBalances } from '@/slices/portfolio/hooks' import { usePoolDataState, usePoolUserDataState, usePoolDepositState, } from '@/slices/pools/hooks' import { swapPoolCalculateAddLiquidity } from '@/actions/swapPoolCalculateAddLiquidity' import { txErrorHandler } from '@/utils/txErrorHandler' import LoadingTokenInput from '@components/loading/LoadingTokenInput' import PriceImpactDisplay from '../components/PriceImpactDisplay' import DepositButton from './DepositButton' import { type Address, getAddress, zeroAddress } from 'viem' import { wagmiConfig } from '@/wagmiConfig' export const DEFAULT_DEPOSIT_QUOTE = { priceImpact: 0n, allowances: {}, routerAddress: '', } const Deposit = ({ chainId, address, }: { chainId: number address: string }) => { const dispatch: any = useDispatch() const { pool, poolData } = usePoolDataState() const { poolUserData } = usePoolUserDataState() const { depositQuote, inputValue, inputSum, filteredInputValue } = usePoolDepositState() const { poolAddress } = getSwapDepositContractFields(pool, chainId) dispatch(setPool(pool)) const calculateMaxDeposits = async () => { try { if (poolUserData == null || address == null) { return } dispatch(setIsLoading(true)) const { totalLocked, virtualPrice } = poolData if (totalLocked > 0 && inputSum > 0n) { const input = transformCalculateLiquidityInput( chainId, pool, filteredInputValue.bi ) const inputs: bigint[] = [] Object.keys(input).forEach((key) => inputs.push(input[key])) const amount = await swapPoolCalculateAddLiquidity({ chainId, pool, inputs, }) let allowances: Record = {} for (const [tokenAddress, value] of Object.entries( filteredInputValue.bi )) { allowances[tokenAddress] = await getTokenAllowance( poolAddress, tokenAddress as Address, address as Address, chainId ) } const priceImpact = calculatePriceImpact( inputSum, amount as bigint, virtualPrice ) dispatch( setDepositQuote({ priceImpact: priceImpact, allowances, routerAddress: poolAddress, }) ) dispatch(setIsLoading(false)) } else { dispatch(setDepositQuote(DEFAULT_DEPOSIT_QUOTE)) dispatch(setIsLoading(false)) } } catch (e) { dispatch(setIsLoading(false)) console.log(e) } } useEffect(() => { calculateMaxDeposits() }, [inputValue, filteredInputValue, pool, chainId, address]) const onChangeInputValue = (token: Token, value: string) => { const bigInt = stringToBigInt(value, token.decimals[chainId]) ?? 0n if (chainId && token) { const tokenAddr = getAddress(token.addresses[chainId]) dispatch( setInputValue({ bi: { ...inputValue.bi, [tokenAddr]: bigInt, }, str: { ...inputValue.str, [tokenAddr]: value, }, }) ) } } useEffect(() => { if (poolData && poolUserData && pool && chainId && address) { resetInputs() } }, [poolUserData]) const resetInputs = () => { dispatch(resetPoolDeposit()) } const approveTxn = async () => { try { const tx = approve(pool, depositQuote, inputValue.bi, chainId) try { await tx calculateMaxDeposits() } catch (error) { return txErrorHandler } } catch (error) { return txErrorHandler(error) } } const onResetDeposit = () => { dispatch(fetchPoolData({ poolName: String(pool.routerIndex) })) dispatch(fetchPoolUserData({ pool, address: address as Address })) dispatch(fetchAndStoreSingleNetworkPortfolioBalances({ address, chainId })) dispatch(resetPoolDeposit()) } const depositTxn = async () => { try { let tx if (poolData.totalLocked === 0) { tx = emptyPoolDeposit(pool, filteredInputValue.bi, chainId) } else { tx = deposit(pool, 'ONE_TENTH', null, filteredInputValue.bi, chainId) } const resolvedTx = await tx if (isTransactionUserRejectedError(resolvedTx)) { throw Error(resolvedTx) } await waitForTransactionReceipt(wagmiConfig, { hash: resolvedTx?.transactionHash as Address, timeout: 60_000, }) onResetDeposit() } catch (error) { /** * Assume transaction success if transaction receipt error * Likely to be rpc related issue */ if (isTransactionReceiptError(error)) { onResetDeposit() } txErrorHandler(error) } finally { dispatch(setIsLoading(false)) } } return (
{pool && poolUserData.tokens && poolData ? ( poolUserData.tokens.map((tokenObj, i) => { return (
) }) ) : ( <> )}
{depositQuote.priceImpact && depositQuote.priceImpact !== 0n && inputSum !== 0n && ( )}
) } const SerializedDepositInput = ({ pool, tokenObj, address, chainId, inputValue, onChangeInputValue, }) => { const [serializedToken, setSerializedToken] = useState(undefined) const balanceToken = getBalanceToken(tokenObj.token, pool) useEffect(() => { const fetchSerializedData = async () => { try { const token = await serializeToken( address, chainId, balanceToken, tokenObj ) setSerializedToken(token) } catch (error) { console.log(`error`, error) } } fetchSerializedData() }, []) return ( serializedToken && ( onChangeInputValue(serializedToken, value)} chainId={chainId} address={address} /> ) ) } const getBalanceToken = (token: Token, pool: Token) => { let balanceToken: Token | undefined if (token.symbol == WETH.symbol && !pool.nativeTokens.includes(WETH)) { balanceToken = ETH } else if (token.symbol == AVWETH.symbol) { balanceToken = WETHE } else { balanceToken = token } return balanceToken } const serializeToken = async ( address: string, chainId: number, balanceToken: Token, tokenObj: any ) => { let fetchedBalance if (balanceToken.addresses[chainId] === zeroAddress) { fetchedBalance = await getBalance(wagmiConfig, { address: address as Address, chainId, }) return { ...balanceToken, rawBalance: fetchedBalance.value, balanceStr: formatBigIntToString( fetchedBalance.value, balanceToken.decimals[chainId], 5 ), } } else if (balanceToken === WETHE) { fetchedBalance = await getBalance(wagmiConfig, { address: address as Address, chainId, token: balanceToken.addresses[chainId] as Address, }) return { ...balanceToken, rawBalance: fetchedBalance.value, balanceStr: fetchedBalance.formatted, } } else { return { ...balanceToken, rawBalance: tokenObj.rawBalance, balanceStr: tokenObj.balanceStr, } } } export default Deposit