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

fix: proportional slippage #424

Merged
merged 9 commits into from
Jan 15, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { useModalWithPoolRedirect } from '../../useModalWithPoolRedirect'
import { getPoolActionableTokens, isV3NotSupportingWethIsEth } from '../../pool.helpers'
import { useUserSettings } from '@repo/lib/modules/user/settings/UserSettingsProvider'
import { isUnbalancedAddErrorMessage } from '@repo/lib/shared/utils/error-filters'
import { getDefaultProportionalSlippagePercentage } from '@repo/lib/shared/utils/slippage'
import { ApiToken } from '@repo/lib/modules/tokens/token.types'

export type UseAddLiquidityResponse = ReturnType<typeof _useAddLiquidity>
Expand All @@ -45,11 +46,17 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const [acceptPoolRisks, setAcceptPoolRisks] = useState(false)
const [wethIsEth, setWethIsEth] = useState(false)
const [totalUSDValue, setTotalUSDValue] = useState('0')
// Used when the user explicitly choses proportional input mode
const [wantsProportional, setWantsProportional] = useState(false)
const [proportionalSlippage, setProportionalSlippage] = useState<string>('0')

const { pool, refetch: refetchPool, isLoading } = usePool()

/* wantsProportional is true when:
- the pool requires proportional input
- the user selected the proportional tab
*/
const [wantsProportional, setWantsProportional] = useState(requiresProportionalInput(pool))
garethfuller marked this conversation as resolved.
Show resolved Hide resolved
const [proportionalSlippage, setProportionalSlippage] = useState<string>(
getDefaultProportionalSlippagePercentage(pool)
)

const { getNativeAssetToken, getWrappedNativeAssetToken, isLoadingTokenPrices } = useTokens()
const { isConnected } = useUserAccount()
const { hasValidationErrors } = useTokenInputsValidation()
Expand All @@ -68,8 +75,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const chain = pool.chain
const nativeAsset = getNativeAssetToken(chain)
const wNativeAsset = getWrappedNativeAssetToken(chain)
const isForcedProportionalAdd = requiresProportionalInput(pool)
const slippage = isForcedProportionalAdd ? proportionalSlippage : userSlippage
const slippage = wantsProportional ? proportionalSlippage : userSlippage
agualis marked this conversation as resolved.
Show resolved Hide resolved
const tokens = getPoolActionableTokens(pool)

function setInitialHumanAmountsIn() {
Expand Down Expand Up @@ -156,7 +162,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const hasQuoteContext = !!simulationQuery.data

async function refetchQuote() {
if (isForcedProportionalAdd) {
if (wantsProportional) {
/*
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 @@ -221,8 +227,6 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
hasQuoteContext,
addLiquidityTxSuccess,
slippage,
proportionalSlippage,
isForcedProportionalAdd,
wantsProportional,
referenceAmountAddress,
setWantsProportional,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,12 @@ import { ApiToken } from '@repo/lib/modules/tokens/token.types'

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

const bufferPercentage = wantsProportional ? slippage : '0'

return (
<TokenBalancesProvider bufferPercentage={proportionalSlippage} extTokens={validTokens}>
<TokenBalancesProvider bufferPercentage={bufferPercentage} extTokens={validTokens}>
<AddLiquidityMainForm />
</TokenBalancesProvider>
)
Expand All @@ -87,7 +89,6 @@ function AddLiquidityMainForm() {
nativeAsset,
wNativeAsset,
previewModalDisclosure,
proportionalSlippage,
slippage,
setProportionalSlippage,
setWantsProportional,
Expand Down Expand Up @@ -192,11 +193,12 @@ function AddLiquidityMainForm() {
<CardHeader>
<HStack justify="space-between" w="full">
<span>Add liquidity</span>
{requiresProportionalInput(pool) || wantsProportional ? (
{wantsProportional ? (
<ProportionalTransactionSettings
pool={pool}
setSlippage={setProportionalSlippage}
size="sm"
slippage={proportionalSlippage}
slippage={slippage}
/>
) : (
<TransactionSettings size="sm" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class ProportionalBoostedAddLiquidityV3 implements AddLiquidityHandler {
...constructBaseBuildCallInput({
humanAmountsIn,
sdkQueryOutput: queryOutput.sdkQueryOutput,
slippagePercent: slippagePercent,
slippagePercent,
pool: this.helpers.pool,
}),
protocolVersion: 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TwammAddLiquidityHandler } from './TwammAddLiquidity.handler'
import { UnbalancedAddLiquidityV2Handler } from './UnbalancedAddLiquidityV2.handler'
import { AddLiquidityHandler } from './AddLiquidity.handler'
import { NestedAddLiquidityV2Handler } from './NestedAddLiquidityV2.handler'
import { requiresProportionalInput, supportsNestedActions } from '../../LiquidityActionHelpers'
import { supportsNestedActions } from '../../LiquidityActionHelpers'
import { ProportionalAddLiquidityHandler } from './ProportionalAddLiquidity.handler'
import { isBoosted, isV3Pool } from '../../../pool.helpers'
import { ProportionalAddLiquidityHandlerV3 } from './ProportionalAddLiquidityV3.handler'
Expand Down Expand Up @@ -41,7 +41,7 @@ export function selectAddLiquidityHandler(
return new BoostedUnbalancedAddLiquidityV3Handler(pool)
}

if (requiresProportionalInput(pool) || wantsProportional) {
if (wantsProportional) {
if (isV3Pool(pool)) {
return new ProportionalAddLiquidityHandlerV3(pool)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useRelayerMode } from '@repo/lib/modules/relayer/useRelayerMode'
import { useTokenApprovalSteps } from '@repo/lib/modules/tokens/approvals/useTokenApprovalSteps'
import { getSpenderForAddLiquidity } from '@repo/lib/modules/tokens/token.helpers'
import { useSignRelayerStep } from '@repo/lib/modules/transactions/transaction-steps/useSignRelayerStep'
import { useUserSettings } from '@repo/lib/modules/user/settings/UserSettingsProvider'
import { useMemo } from 'react'
import { usePool } from '../../PoolProvider'
import { requiresPermit2Approval } from '../../pool.helpers'
Expand All @@ -24,10 +23,10 @@ export function useAddLiquiditySteps({
handler,
humanAmountsIn,
simulationQuery,
slippage,
}: AddLiquidityStepsParams) {
const { pool, chainId, chain } = usePool()
const shouldBatchTransactions = useShouldBatchTransactions(pool)
const { slippage } = useUserSettings()
const relayerMode = useRelayerMode(pool)
const shouldSignRelayerApproval = useShouldSignRelayerApproval(chainId, relayerMode)

Expand Down
11 changes: 9 additions & 2 deletions packages/lib/modules/user/settings/TransactionSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { fNum } from '@repo/lib/shared/utils/numbers'
import { AlertTriangle, Settings } from 'react-feather'
import { CurrencySelect } from './CurrencySelect'
import { SlippageInput } from './UserSettings'
import { getDefaultProportionalSlippagePercentage } from '@repo/lib/shared/utils/slippage'
import { Pool } from '../../pool/PoolProvider'

export function TransactionSettings(props: ButtonProps) {
const { slippage, setSlippage } = useUserSettings()
Expand Down Expand Up @@ -62,15 +64,19 @@ export function TransactionSettings(props: ButtonProps) {
interface ProportionalTransactionSettingsProps extends ButtonProps {
slippage: string
setSlippage: (value: string) => void
pool: Pool
}

export function ProportionalTransactionSettings({
slippage,
setSlippage,
pool,
...props
}: ProportionalTransactionSettingsProps) {
const { isOpen, onOpen, onClose } = useDisclosure()

const defaultProportionalSlippagePercentage = getDefaultProportionalSlippagePercentage(pool)

return (
<Popover isLazy isOpen={isOpen} onClose={onClose} placement="bottom-end">
<PopoverTrigger>
Expand Down Expand Up @@ -104,8 +110,9 @@ export function ProportionalTransactionSettings({
<PopoverArrow />
<PopoverBody>
<Text fontSize="sm" fontWeight="500" lineHeight="18px" variant="secondary">
Slippage is set to 0 by default for forced proportional actions to reduce
dust left over. If you need to set slippage higher than 0 it will
Slippage is set to {defaultProportionalSlippagePercentage} by default for
forced proportional actions to reduce dust left over. If you need to set
slippage higher than {defaultProportionalSlippagePercentage} it will
effectively lower the amount of tokens you can add in the form below. Then,
if slippage occurs, the transaction can take the amount of tokens you
specified + slippage from your token balance.
Expand Down
15 changes: 15 additions & 0 deletions packages/lib/shared/utils/slippage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { HumanAmount } from '@balancer/sdk'
import { bn, fNum } from './numbers'
import { Pool } from '@repo/lib/modules/pool/PoolProvider'

export function getDefaultProportionalSlippagePercentage(pool: Pool) {
/*
We use this small slippage percentage by default for boosted proportional adds because SDK addLiquidityBoosted queries are not 100% precise.
The error is ~1000 wei, which is negligible on 18 decimal tokens but not as much on 6 decimals tokens (this is a SC limitation).
Using 0.01% is big enough to prevent tx simulation errors in all types of boosted proportional adds while keeping the dust amount small.
*/
const defaultBoostedProportionalSlippagePercentage = '0.01'
const defaultProportionalSlippagePercentage = '0'

return pool.hasErc4626 || pool.hasNestedErc4626
? defaultBoostedProportionalSlippagePercentage
: defaultProportionalSlippagePercentage
}

export function slippageDiffLabel(actualAmount: HumanAmount, expectedAmount: HumanAmount) {
if (!expectedAmount) return ''
Expand Down
Loading