Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow slippage on forced proportional adds #1133

Merged
merged 8 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/modules/pool/actions/PoolActionsPriceImpactDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { NumberText } from '@/lib/shared/components/typography/NumberText'
import { fNum, bn } from '@/lib/shared/utils/numbers'
import { HStack, VStack, Text, Tooltip, Icon, Box, Skeleton } from '@chakra-ui/react'
import { usePriceImpact } from '@/lib/modules/price-impact/PriceImpactProvider'
import { useUserSettings } from '@/lib/modules/user/settings/UserSettingsProvider'
import { useCurrency } from '@/lib/shared/hooks/useCurrency'
import { usePool } from '../PoolProvider'
import { ArrowRight } from 'react-feather'
Expand All @@ -13,17 +12,18 @@ import { InfoIcon } from '@/lib/shared/components/icons/InfoIcon'
interface PoolActionsPriceImpactDetailsProps {
bptAmount: bigint | undefined
totalUSDValue: string
slippage: string
isAddLiquidity?: boolean
isLoading?: boolean
}

export function PoolActionsPriceImpactDetails({
bptAmount,
totalUSDValue,
slippage,
isAddLiquidity = false,
isLoading = false,
}: PoolActionsPriceImpactDetailsProps) {
const { slippage } = useUserSettings()
const { toCurrency } = useCurrency()
const { pool } = usePool()

Expand Down
13 changes: 11 additions & 2 deletions lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types'
import { isUnhandledAddPriceImpactError } from '@/lib/modules/price-impact/price-impact.utils'
import { useModalWithPoolRedirect } from '../../useModalWithPoolRedirect'
import { getPoolTokens } from '../../pool.helpers'
import { useUserSettings } from '@/lib/modules/user/settings/UserSettingsProvider'

export type UseAddLiquidityResponse = ReturnType<typeof _useAddLiquidity>
export const AddLiquidityContext = createContext<UseAddLiquidityResponse | null>(null)
Expand All @@ -39,12 +40,14 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const [acceptPoolRisks, setAcceptPoolRisks] = useState(false)
const [wethIsEth, setWethIsEth] = useState(false)
const [totalUSDValue, setTotalUSDValue] = useState('0')
const [proportionalSlippage, setProportionalSlippage] = useState<string>('0')

const { pool, refetch: refetchPool, isLoading } = usePool()
const { getToken, getNativeAssetToken, getWrappedNativeAssetToken, isLoadingTokenPrices } =
useTokens()
const { isConnected } = useUserAccount()
const { hasValidationErrors } = useTokenInputsValidation()
const { slippage: userSlippage } = useUserSettings()

const handler = useMemo(() => selectAddLiquidityHandler(pool), [pool.id, isLoading])

Expand All @@ -56,7 +59,8 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const chain = pool.chain
const nativeAsset = getNativeAssetToken(chain)
const wNativeAsset = getWrappedNativeAssetToken(chain)

const isForcedProportionalAdd = requiresProportionalInput(pool.type)
const slippage = isForcedProportionalAdd ? proportionalSlippage : userSlippage
const tokens = getPoolTokens(pool, getToken)

function setInitialHumanAmountsIn() {
Expand Down Expand Up @@ -115,6 +119,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
handler,
humanAmountsIn,
simulationQuery,
slippage,
})
const transactionSteps = useTransactionSteps(steps, isLoadingSteps)

Expand All @@ -126,7 +131,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const hasQuoteContext = !!simulationQuery.data

async function refetchQuote() {
if (requiresProportionalInput(pool.type)) {
if (isForcedProportionalAdd) {
/*
This is the only edge-case where the SDK needs pool onchain data from the frontend
(calculateProportionalAmounts uses pool.dynamicData.totalShares in its parameters)
Expand Down Expand Up @@ -186,6 +191,10 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
addLiquidityTxHash,
hasQuoteContext,
addLiquidityTxSuccess,
slippage,
proportionalSlippage,
isForcedProportionalAdd,
setProportionalSlippage,
refetchQuote,
setHumanAmountIn,
setHumanAmountsIn,
Expand Down
35 changes: 25 additions & 10 deletions lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import { Address } from 'viem'
import { AddLiquidityModal } from '../modal/AddLiquidityModal'
import { useAddLiquidity } from '../AddLiquidityProvider'
import { bn, fNum } from '@/lib/shared/utils/numbers'
import { TransactionSettings } from '@/lib/modules/user/settings/TransactionSettings'
import {
ProportionalTransactionSettings,
TransactionSettings,
} from '@/lib/modules/user/settings/TransactionSettings'
import { TokenInputs } from './TokenInputs'
import { TokenInputsWithAddable } from './TokenInputsWithAddable'
import { usePool } from '../../../PoolProvider'
Expand Down Expand Up @@ -52,10 +55,10 @@ import { useTokens } from '@/lib/modules/tokens/TokensProvider'

// small wrapper to prevent out of context error
export function AddLiquidityForm() {
const { validTokens } = useAddLiquidity()
const { validTokens, proportionalSlippage } = useAddLiquidity()

return (
<TokenBalancesProvider extTokens={validTokens}>
<TokenBalancesProvider extTokens={validTokens} bufferPercentage={proportionalSlippage}>
<AddLiquidityMainForm />
</TokenBalancesProvider>
)
Expand All @@ -78,6 +81,9 @@ function AddLiquidityMainForm() {
nativeAsset,
wNativeAsset,
previewModalDisclosure,
proportionalSlippage,
slippage,
setProportionalSlippage,
} = useAddLiquidity()

const nextBtn = useRef(null)
Expand Down Expand Up @@ -144,26 +150,34 @@ function AddLiquidityMainForm() {
})
}

useEffect(() => {
if (addLiquidityTxHash) {
previewModalDisclosure.onOpen()
}
}, [addLiquidityTxHash])

function onModalClose() {
// restart polling for token prices when modal is closed again
startTokenPricePolling()

previewModalDisclosure.onClose()
}

useEffect(() => {
if (addLiquidityTxHash) {
previewModalDisclosure.onOpen()
}
}, [addLiquidityTxHash])

return (
<Box w="full" maxW="lg" mx="auto" pb="2xl">
<Card>
<CardHeader>
<HStack w="full" justify="space-between">
<span>Add liquidity</span>
<TransactionSettings size="sm" />
{requiresProportionalInput(pool.type) ? (
<ProportionalTransactionSettings
slippage={proportionalSlippage}
setSlippage={setProportionalSlippage}
size="sm"
/>
) : (
<TransactionSettings size="sm" />
)}
</HStack>
</CardHeader>
<VStack spacing="md" align="start" w="full">
Expand Down Expand Up @@ -204,6 +218,7 @@ function AddLiquidityMainForm() {
<PoolActionsPriceImpactDetails
totalUSDValue={totalUSDValue}
bptAmount={simulationQuery.data?.bptOut.amount}
slippage={slippage}
isAddLiquidity
isLoading={isFetching}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ export function TokenInputsWithAddable({
<TokenInputs
tokenSelectDisclosureOpen={tokenSelectDisclosureOpen}
customSetAmountIn={setAmountIn}
></TokenInputs>
/>
</VStack>
)
}
14 changes: 9 additions & 5 deletions lib/modules/pool/actions/add-liquidity/form/useMaximumInputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useMemo, useState } from 'react'
import { usePool } from '../../../PoolProvider'
import { useAddLiquidity } from '../AddLiquidityProvider'
import { useTotalUsdValue } from '@/lib/modules/tokens/useTotalUsdValue'
import { TokenAmount } from '@/lib/modules/tokens/token.types'

export function useMaximumInputs() {
const { isConnected } = useUserAccount()
Expand All @@ -20,12 +21,15 @@ export function useMaximumInputs() {
const { isLoadingTokenPrices } = useTokens()
const [isMaximized, setIsMaximized] = useState(false)

// Depending on if the user is using WETH or ETH, we need to filter out the
// native asset or wrapped native asset.
const nativeAssetFilter = (balance: TokenAmount) =>
wethIsEth
? wNativeAsset && balance.address !== wNativeAsset.address
: nativeAsset && balance.address !== nativeAsset.address

const filteredBalances = useMemo(() => {
return balances.filter(balance =>
wethIsEth
? wNativeAsset && balance.address !== wNativeAsset.address
: nativeAsset && balance.address !== nativeAsset.address
)
return balances.filter(nativeAssetFilter)
}, [wethIsEth, isBalancesLoading])

function handleMaximizeUserAmounts() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
} from '../../LiquidityActionHelpers'
import { useAddLiquidity } from '../AddLiquidityProvider'
import { useTotalUsdValue } from '@/lib/modules/tokens/useTotalUsdValue'
import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types'
import { HumanTokenAmountWithAddress, TokenAmount } from '@/lib/modules/tokens/token.types'
import { swapWrappedWithNative } from '@/lib/modules/tokens/token.helpers'

type OptimalToken = {
Expand All @@ -42,13 +42,16 @@ export function useProportionalInputs() {
const [isMaximized, setIsMaximized] = useState(false)
const { isLoadingTokenPrices } = useTokens()

// Depending on if the user is using WETH or ETH, we need to filter out the
// native asset or wrapped native asset.
const nativeAssetFilter = (balance: TokenAmount) =>
wethIsEth
? wNativeAsset && balance.address !== wNativeAsset.address
: nativeAsset && balance.address !== nativeAsset.address

const filteredBalances = useMemo(() => {
return balances.filter(balance =>
wethIsEth
? wNativeAsset && balance.address !== wNativeAsset.address
: nativeAsset && balance.address !== nativeAsset.address
)
}, [wethIsEth, isBalancesLoading])
return balances.filter(nativeAssetFilter)
}, [wethIsEth, isBalancesLoading, balances])

function clearAmountsIn(changedAmount?: HumanTokenAmountWithAddress) {
setHumanAmountsIn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,13 @@ export class ProportionalAddLiquidityHandler implements AddLiquidityHandler {
account,
queryOutput,
humanAmountsIn,
slippagePercent,
}: SdkBuildAddLiquidityInput): Promise<TransactionConfig> {
const addLiquidity = new AddLiquidity()

const { callData, to, value } = addLiquidity.buildCall({
...queryOutput.sdkQueryOutput,
// Setting slippage to zero ensures the build call can't fail if the user
// maxes out their balance. It can result in a tx failure if the pool
// state changes significantly in the background. The assumption is that
// this should be rare. If not, we will have to re-introduce slippage here
// and limit the user input amounts to their balance - slippage.
slippage: Slippage.fromPercentage('0' as HumanAmount),
slippage: Slippage.fromPercentage(slippagePercent as HumanAmount),
sender: account,
recipient: account,
wethIsEth: this.helpers.isNativeAssetIn(humanAmountsIn),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function AddLiquiditySummary({
tokens,
addLiquidityTxHash,
addLiquidityTxSuccess,
slippage,
} = useAddLiquidity()
const { pool } = usePool()
const { isMobile } = useBreakpoints()
Expand Down Expand Up @@ -123,6 +124,7 @@ export function AddLiquiditySummary({
<PoolActionsPriceImpactDetails
totalUSDValue={totalUSDValue}
bptAmount={simulationQuery.data?.bptOut.amount}
slippage={slippage}
isAddLiquidity
/>
</VStack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useUserSettings } from '@/lib/modules/user/settings/UserSettingsProvider'
import { useUserAccount } from '@/lib/modules/web3/UserAccountProvider'
import { defaultDebounceMs, onlyExplicitRefetch } from '@/lib/shared/utils/queries'
import { useQuery } from '@tanstack/react-query'
Expand All @@ -19,19 +18,20 @@ export type AddLiquidityBuildQueryParams = {
handler: AddLiquidityHandler
humanAmountsIn: HumanTokenAmountWithAddress[]
simulationQuery: AddLiquiditySimulationQueryResult
slippage: string
}

// Uses the SDK to build a transaction config to be used by wagmi's useManagedSendTransaction
export function useAddLiquidityBuildCallDataQuery({
handler,
humanAmountsIn,
simulationQuery,
slippage,
enabled,
}: AddLiquidityBuildQueryParams & {
enabled: boolean
}) {
const { userAddress, isConnected } = useUserAccount()
const { slippage } = useUserSettings()
const { pool, chainId } = usePool()
const { data: blockNumber } = useBlockNumber({ chainId })
const { relayerApprovalSignature } = useRelayerSignature()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function useAddLiquiditySteps({
handler,
humanAmountsIn,
simulationQuery,
slippage,
}: AddLiquidityStepsParams) {
const { pool, chainId, chain } = usePool()
const relayerMode = useRelayerMode(pool)
Expand Down Expand Up @@ -46,6 +47,7 @@ export function useAddLiquiditySteps({
handler,
humanAmountsIn,
simulationQuery,
slippage,
})

const steps = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { SimulationError } from '@/lib/shared/components/errors/SimulationError'
import { InfoIcon } from '@/lib/shared/components/icons/InfoIcon'
import { SafeAppAlert } from '@/lib/shared/components/alerts/SafeAppAlert'
import { useTokens } from '@/lib/modules/tokens/TokensProvider'
import { useUserSettings } from '@/lib/modules/user/settings/UserSettingsProvider'
const TABS: ButtonGroupOption[] = [
{
value: 'proportional',
Expand Down Expand Up @@ -71,6 +72,7 @@ export function RemoveLiquidityForm() {
const nextBtn = useRef(null)
const [activeTab, setActiveTab] = useState(TABS[0])
const { startTokenPricePolling } = useTokens()
const { slippage } = useUserSettings()

useEffect(() => {
setPriceImpact(priceImpactQuery.data)
Expand Down Expand Up @@ -177,6 +179,7 @@ export function RemoveLiquidityForm() {
<PoolActionsPriceImpactDetails
totalUSDValue={totalUSDValue}
bptAmount={BigInt(parseUnits(quoteBptIn, 18))}
slippage={slippage}
isLoading={isFetching}
/>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { RemoveLiquidityReceiptResult } from '@/lib/modules/transactions/transac
import { BalAlert } from '@/lib/shared/components/alerts/BalAlert'
import { useTokens } from '@/lib/modules/tokens/TokensProvider'
import { CardPopAnim } from '@/lib/shared/components/animations/CardPopAnim'
import { useUserSettings } from '@/lib/modules/user/settings/UserSettingsProvider'

export function RemoveLiquiditySummary({
isLoading: isLoadingReceipt,
Expand All @@ -34,6 +35,7 @@ export function RemoveLiquiditySummary({
const { getTokensByChain } = useTokens()
const { pool } = usePool()
const { userAddress, isLoading: isUserAddressLoading } = useUserAccount()
const { slippage } = useUserSettings()

const _amountsOut = amountsOut.filter(amount => bn(amount.humanAmount).gt(0))

Expand Down Expand Up @@ -87,6 +89,7 @@ export function RemoveLiquiditySummary({
<VStack align="start" spacing="sm">
<PoolActionsPriceImpactDetails
totalUSDValue={totalUSDValue}
slippage={slippage}
bptAmount={BigInt(parseUnits(quoteBptIn, 18))}
/>
</VStack>
Expand Down
Loading
Loading