From 2c6629931b403fa6e78d67a1209de94b7c8b3d35 Mon Sep 17 00:00:00 2001 From: groninge Date: Fri, 29 Nov 2024 13:22:01 +0100 Subject: [PATCH 01/30] add tabs --- .../add-liquidity/form/AddLiquidityForm.tsx | 16 ++-- .../form/AddLiquidityFormTabs.tsx | 95 +++++++++++++++++++ .../form/TokenInputsWithAddable.tsx | 26 ++--- 3 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx index ef2b6ba5e..79777abc9 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx @@ -55,6 +55,7 @@ import { ConnectWallet } from '@repo/lib/modules/web3/ConnectWallet' import { BalAlert } from '@repo/lib/shared/components/alerts/BalAlert' import { SafeAppAlert } from '@repo/lib/shared/components/alerts/SafeAppAlert' import { useTokens } from '@repo/lib/modules/tokens/TokensProvider' +import { AddLiquidityFormTabs } from './AddLiquidityFormTabs' // small wrapper to prevent out of context error export function AddLiquidityForm() { @@ -188,15 +189,12 @@ function AddLiquidityMainForm() { )} - {!nestedAddLiquidityEnabled ? ( - tokenSelectDisclosure.onOpen()} - totalUSDValue={totalUSDValue} - /> - ) : ( - tokenSelectDisclosure.onOpen()} /> - )} + {!simulationQuery.isError && ( tokenSelectDisclosure.onOpen()} /> + } else { + return ( + tokenSelectDisclosure.onOpen()} + totalUSDValue={totalUSDValue} + /> + ) + } +} + +export function AddLiquidityFormTabs({ + pool, + tokenSelectDisclosure, + totalUSDValue, + nestedAddLiquidityEnabled, +}: { + pool: Pool + tokenSelectDisclosure: UseDisclosureReturn + totalUSDValue: string + nestedAddLiquidityEnabled: boolean +}) { + const isDisabledUnbalancedTab = requiresProportionalInput(pool) + const isDisabledBalancedTab = nestedAddLiquidityEnabled + + return ( + + + + Unbalanced + + + Balanced + + + + + + + + + + + + ) +} diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/TokenInputsWithAddable.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/TokenInputsWithAddable.tsx index 80740d016..62054e402 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/TokenInputsWithAddable.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/TokenInputsWithAddable.tsx @@ -9,19 +9,16 @@ import { useAddLiquidity } from '../AddLiquidityProvider' import { TokenInputs } from './TokenInputs' import { useProportionalInputs } from './useProportionalInputs' import { useMaximumInputs } from './useMaximumInputs' -import { BalAlert } from '@repo/lib/shared/components/alerts/BalAlert' -import { hasNoLiquidity } from '../../LiquidityActionHelpers' -import { usePool } from '../../../PoolProvider' type Props = { tokenSelectDisclosureOpen: () => void - requiresProportionalInput: boolean + isProportionalInput: boolean totalUSDValue: string } export function TokenInputsWithAddable({ tokenSelectDisclosureOpen, - requiresProportionalInput, + isProportionalInput, totalUSDValue, }: Props) { const { isConnected } = useUserAccount() @@ -38,7 +35,6 @@ export function TokenInputsWithAddable({ setIsMaximized: setIsMaximizedForProportionalInput, clearAmountsIn, } = useProportionalInputs() - const { pool } = usePool() const { canMaximize: canMaximizeForMaximumInput, @@ -48,7 +44,7 @@ export function TokenInputsWithAddable({ setIsMaximized: setIsMaximizedForMaximumInput, } = useMaximumInputs() - const isProportional = requiresProportionalInput || wantsProportional + const isProportional = isProportionalInput || wantsProportional const handleMaximizeUserAmounts = isProportional ? handleMaximizeUserAmountsForProportionalInput @@ -84,9 +80,6 @@ export function TokenInputsWithAddable({ return ( - {requiresProportionalInput && !hasNoLiquidity(pool) && ( - - )} {isConnected && ( @@ -94,15 +87,10 @@ export function TokenInputsWithAddable({ - {!requiresProportionalInput && canMaximizeForProportionalInput ? ( + {canMaximizeForProportionalInput ? ( - - {`${wantsProportional ? 'Proportional ' : 'Total '}`} + + {`${isProportionalInput ? 'Proportional ' : 'Total '}`} addable pool tokens @@ -138,7 +126,7 @@ export function TokenInputsWithAddable({ {!canMaximize && ( Date: Fri, 29 Nov 2024 14:01:15 +0100 Subject: [PATCH 02/30] set default tab --- .../actions/add-liquidity/form/AddLiquidityFormTabs.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index 95c5d2394..bd4724840 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -51,7 +51,12 @@ export function AddLiquidityFormTabs({ const isDisabledBalancedTab = nestedAddLiquidityEnabled return ( - + Date: Fri, 29 Nov 2024 14:02:02 +0100 Subject: [PATCH 03/30] add tvl threshold --- .../form/AddLiquidityFormTabs.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index bd4724840..afe15982d 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -11,6 +11,9 @@ import { requiresProportionalInput } from '../../LiquidityActionHelpers' import { TokenInputs } from './TokenInputs' import { TokenInputsWithAddable } from './TokenInputsWithAddable' import { Pool } from '../../../PoolProvider' +import { bn } from '@repo/lib/shared/utils/numbers' + +const MIN_LIQUIDITY_FOR_BALANCED_ADD = 50000 export function TokenInputsBase({ nestedAddLiquidityEnabled, @@ -47,9 +50,17 @@ export function AddLiquidityFormTabs({ totalUSDValue: string nestedAddLiquidityEnabled: boolean }) { - const isDisabledUnbalancedTab = requiresProportionalInput(pool) + const isBelowMinTvlThreshold = bn(pool.dynamicData.totalLiquidity).lt( + bn(MIN_LIQUIDITY_FOR_BALANCED_ADD) + ) + + const isDisabledUnbalancedTab = requiresProportionalInput(pool) || isBelowMinTvlThreshold const isDisabledBalancedTab = nestedAddLiquidityEnabled + const UnbalancedTabTooltipLabel = isBelowMinTvlThreshold + ? 'Liquidity must be added proportionally until the pool TVL is greater than $50000' + : 'This pool requires liquidity to be added proportionally' + return ( - + Unbalanced Date: Fri, 29 Nov 2024 14:39:08 +0100 Subject: [PATCH 04/30] set tab index --- .../add-liquidity/form/AddLiquidityForm.tsx | 5 ++++- .../form/AddLiquidityFormTabs.tsx | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx index 79777abc9..1bea0325b 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx @@ -19,7 +19,7 @@ import { VStack, useDisclosure, } from '@chakra-ui/react' -import { useEffect, useRef } from 'react' +import { useEffect, useRef, useState } from 'react' import { Address } from 'viem' import { AddLiquidityModal } from '../modal/AddLiquidityModal' import { useAddLiquidity } from '../AddLiquidityProvider' @@ -99,6 +99,7 @@ function AddLiquidityMainForm() { const { balanceFor, isBalancesLoading } = useTokenBalances() const { isConnected } = useUserAccount() const { startTokenPricePolling } = useTokens() + const [tabIndex, setTabIndex] = useState(0) useEffect(() => { setPriceImpact(priceImpactQuery.data) @@ -192,6 +193,8 @@ function AddLiquidityMainForm() { diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index afe15982d..5cbe5f110 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -12,6 +12,7 @@ import { TokenInputs } from './TokenInputs' import { TokenInputsWithAddable } from './TokenInputsWithAddable' import { Pool } from '../../../PoolProvider' import { bn } from '@repo/lib/shared/utils/numbers' +import { useEffect } from 'react' const MIN_LIQUIDITY_FOR_BALANCED_ADD = 50000 @@ -44,11 +45,15 @@ export function AddLiquidityFormTabs({ tokenSelectDisclosure, totalUSDValue, nestedAddLiquidityEnabled, + tabIndex, + setTabIndex, }: { pool: Pool tokenSelectDisclosure: UseDisclosureReturn totalUSDValue: string nestedAddLiquidityEnabled: boolean + tabIndex: number + setTabIndex: (index: number) => void }) { const isBelowMinTvlThreshold = bn(pool.dynamicData.totalLiquidity).lt( bn(MIN_LIQUIDITY_FOR_BALANCED_ADD) @@ -61,11 +66,23 @@ export function AddLiquidityFormTabs({ ? 'Liquidity must be added proportionally until the pool TVL is greater than $50000' : 'This pool requires liquidity to be added proportionally' + const handleTabsChange = (index: number) => { + setTabIndex(index) + } + + useEffect(() => { + if (isDisabledUnbalancedTab) { + setTabIndex(1) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isDisabledUnbalancedTab]) + return ( From 8e96a7dfd9bf135712bf3b8c65f4d34a83865d1f Mon Sep 17 00:00:00 2001 From: groninge Date: Fri, 29 Nov 2024 14:53:23 +0100 Subject: [PATCH 05/30] proportional check logic has moved --- .../form/AddLiquidityFormTabs.tsx | 14 ++++++++----- .../form/TokenInputsWithAddable.tsx | 21 ++++++------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index 5cbe5f110..bbeb90def 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -13,6 +13,7 @@ import { TokenInputsWithAddable } from './TokenInputsWithAddable' import { Pool } from '../../../PoolProvider' import { bn } from '@repo/lib/shared/utils/numbers' import { useEffect } from 'react' +import { useProportionalInputs } from './useProportionalInputs' const MIN_LIQUIDITY_FOR_BALANCED_ADD = 50000 @@ -20,19 +21,19 @@ export function TokenInputsBase({ nestedAddLiquidityEnabled, tokenSelectDisclosure, totalUSDValue, - isProportionalInput, + isProportional, }: { nestedAddLiquidityEnabled: boolean tokenSelectDisclosure: UseDisclosureReturn totalUSDValue: string - isProportionalInput: boolean + isProportional: boolean }) { if (nestedAddLiquidityEnabled) { return tokenSelectDisclosure.onOpen()} /> } else { return ( tokenSelectDisclosure.onOpen()} totalUSDValue={totalUSDValue} /> @@ -55,6 +56,8 @@ export function AddLiquidityFormTabs({ tabIndex: number setTabIndex: (index: number) => void }) { + const { clearAmountsIn } = useProportionalInputs() + const isBelowMinTvlThreshold = bn(pool.dynamicData.totalLiquidity).lt( bn(MIN_LIQUIDITY_FOR_BALANCED_ADD) ) @@ -67,6 +70,7 @@ export function AddLiquidityFormTabs({ : 'This pool requires liquidity to be added proportionally' const handleTabsChange = (index: number) => { + clearAmountsIn() setTabIndex(index) } @@ -102,7 +106,7 @@ export function AddLiquidityFormTabs({ void - isProportionalInput: boolean + isProportional: boolean totalUSDValue: string } export function TokenInputsWithAddable({ tokenSelectDisclosureOpen, - isProportionalInput, + isProportional, totalUSDValue, }: Props) { const { isConnected } = useUserAccount() const { toCurrency } = useCurrency() const { setHumanAmountIn } = useAddLiquidity() - const [wantsProportional, setWantsProportional] = useState(false) const { handleProportionalHumanInputChange, @@ -33,7 +32,6 @@ export function TokenInputsWithAddable({ maximizedUsdValue: maximizedUsdValueForProportionalInput, canMaximize: canMaximizeForProportionalInput, setIsMaximized: setIsMaximizedForProportionalInput, - clearAmountsIn, } = useProportionalInputs() const { @@ -44,8 +42,6 @@ export function TokenInputsWithAddable({ setIsMaximized: setIsMaximizedForMaximumInput, } = useMaximumInputs() - const isProportional = isProportionalInput || wantsProportional - const handleMaximizeUserAmounts = isProportional ? handleMaximizeUserAmountsForProportionalInput : handleMaximizeUserAmountsForMaximumInput @@ -71,12 +67,7 @@ export function TokenInputsWithAddable({ setIsMaximized(true) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [totalUSDValue, wantsProportional]) - - function handleWantsProportional() { - setWantsProportional(!wantsProportional) - clearAmountsIn() - } + }, [totalUSDValue]) return ( @@ -90,7 +81,7 @@ export function TokenInputsWithAddable({ {canMaximizeForProportionalInput ? ( - {`${isProportionalInput ? 'Proportional ' : 'Total '}`} + {`${isProportional ? 'Proportional ' : 'Total '}`} addable pool tokens @@ -126,7 +117,7 @@ export function TokenInputsWithAddable({ {!canMaximize && ( Date: Sat, 30 Nov 2024 12:33:54 +0100 Subject: [PATCH 06/30] reafactor: proportional tabs and handlers --- .../pool/PoolDetail/PoolMyLiquidity.tsx | 2 +- .../add-liquidity/AddLiquidityProvider.tsx | 16 +- .../add-liquidity/form/AddLiquidityForm.tsx | 33 ++-- .../form/AddLiquidityFormTabs.tsx | 152 ++++++++--------- .../form/TokenInputsMaybeProportional.tsx | 29 ++++ .../form/TokenInputsWithAddable.tsx | 145 ---------------- .../form/useProportionalInputs.tsx | 155 +++--------------- .../BaseProportionalAddLiquidity.handler.ts | 2 + ...oportionalBoostedAddLiquidityV3.handler.ts | 95 +++++++++++ ...BoostedUnbalancedAddLiquidityV3.handler.ts | 40 ----- .../handlers/add-liquidity.utils.ts | 19 +++ .../handlers/selectAddLiquidityHandler.ts | 12 +- .../price-impact/PriceImpactAccordion.tsx | 40 +++-- .../btns/button-group/ButtonGroup.tsx | 119 +++++++++----- .../components/errors/UnbalancedAddError.tsx | 47 +++++- packages/lib/shared/utils/error-filters.ts | 8 +- 16 files changed, 440 insertions(+), 474 deletions(-) create mode 100644 packages/lib/modules/pool/actions/add-liquidity/form/TokenInputsMaybeProportional.tsx delete mode 100644 packages/lib/modules/pool/actions/add-liquidity/form/TokenInputsWithAddable.tsx create mode 100644 packages/lib/modules/pool/actions/add-liquidity/handlers/ProportionalBoostedAddLiquidityV3.handler.ts delete mode 100644 packages/lib/modules/pool/actions/add-liquidity/handlers/ProportionalBoostedUnbalancedAddLiquidityV3.handler.ts diff --git a/packages/lib/modules/pool/PoolDetail/PoolMyLiquidity.tsx b/packages/lib/modules/pool/PoolDetail/PoolMyLiquidity.tsx index b3082bc27..4602b474e 100644 --- a/packages/lib/modules/pool/PoolDetail/PoolMyLiquidity.tsx +++ b/packages/lib/modules/pool/PoolDetail/PoolMyLiquidity.tsx @@ -17,7 +17,7 @@ import { Tooltip, useDisclosure, } from '@chakra-ui/react' -import React, { useMemo, useState, useLayoutEffect } from 'react' +import { useMemo, useState, useLayoutEffect } from 'react' import { usePool } from '../PoolProvider' import { Address } from 'viem' import { usePathname, useRouter } from 'next/navigation' diff --git a/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx b/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx index c251f2266..3efe4af88 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx @@ -3,7 +3,7 @@ import { useTokens } from '@repo/lib/modules/tokens/TokensProvider' import { useMandatoryContext } from '@repo/lib/shared/utils/contexts' -import { HumanAmount } from '@balancer/sdk' +import { HumanAmount, isSameAddress } from '@balancer/sdk' import { PropsWithChildren, createContext, useEffect, useMemo, useState } from 'react' import { Address, Hash } from 'viem' import { usePool } from '../../PoolProvider' @@ -91,6 +91,19 @@ export function _useAddLiquidity(urlTxHash?: Hash) { ]) } + function clearAmountsIn(changedAmount?: HumanTokenAmountWithAddress) { + setHumanAmountsIn( + humanAmountsIn.map(({ tokenAddress }) => { + // Keeps user inputs like '0' or '0.' instead of replacing them with '' + if (changedAmount && isSameAddress(changedAmount.tokenAddress, tokenAddress)) { + return changedAmount + } + + return { tokenAddress, humanAmount: '' } + }) + ) + } + const tokensWithNativeAsset = replaceWrappedWithNativeAsset(tokens, nativeAsset) const validTokens = injectNativeAsset(tokens, nativeAsset, pool) @@ -207,6 +220,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) { refetchQuote, setHumanAmountIn, setHumanAmountsIn, + clearAmountsIn, setNeedsToAcceptHighPI, setAcceptPoolRisks, setWethIsEth, diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx index 9a9085d6b..9ec9985bc 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx @@ -28,8 +28,6 @@ import { ProportionalTransactionSettings, TransactionSettings, } from '@repo/lib/modules/user/settings/TransactionSettings' -import { TokenInputs } from './TokenInputs' -import { TokenInputsWithAddable } from './TokenInputsWithAddable' import { usePool } from '../../../PoolProvider' import { hasNoLiquidity, @@ -57,6 +55,7 @@ import { SafeAppAlert } from '@repo/lib/shared/components/alerts/SafeAppAlert' import { useTokens } from '@repo/lib/modules/tokens/TokensProvider' import { AddLiquidityFormTabs } from './AddLiquidityFormTabs' import { UnbalancedAddError } from '@repo/lib/shared/components/errors/UnbalancedAddError' +import { isUnbalancedAddError } from '@repo/lib/shared/utils/error-filters' // small wrapper to prevent out of context error export function AddLiquidityForm() { @@ -89,6 +88,7 @@ function AddLiquidityMainForm() { proportionalSlippage, slippage, setProportionalSlippage, + setWantsProportional, wantsProportional, } = useAddLiquidity() @@ -103,6 +103,15 @@ function AddLiquidityMainForm() { const { startTokenPricePolling } = useTokens() const [tabIndex, setTabIndex] = useState(0) + const setUnbalancedTab = () => { + setTabIndex(0) + setWantsProportional(false) + } + const setProportionalTab = () => { + setTabIndex(1) + setWantsProportional(true) + } + useEffect(() => { setPriceImpact(priceImpactQuery.data) }, [priceImpactQuery.data]) @@ -110,6 +119,8 @@ function AddLiquidityMainForm() { const hasPriceImpact = priceImpact !== undefined && priceImpact !== null const priceImpactLabel = hasPriceImpact ? fNum('priceImpact', priceImpact) : '-' + const isUnbalancedError = isUnbalancedAddError(simulationQuery.error || priceImpactQuery.error) + const weeklyYield = calcPotentialYieldFor(pool, totalUSDValue) const nestedAddLiquidityEnabled = supportsNestedActions(pool) // TODO && !userToggledEscapeHatch @@ -194,12 +205,18 @@ function AddLiquidityMainForm() { + {!wantsProportional && isUnbalancedError && ( + + )} {!simulationQuery.isError && ( } + avoidPriceImpactAlert={isUnbalancedError} cannotCalculatePriceImpact={cannotCalculatePriceImpactError(priceImpactQuery.error)} isDisabled={!priceImpactQuery.data} setNeedsToAcceptPIRisk={setNeedsToAcceptHighPI} @@ -266,12 +284,7 @@ function AddLiquidityMainForm() { error={simulationQuery.error} /> )} - {(simulationQuery.isError || priceImpactQuery.isError) && ( - setTabIndex(1)} - /> - )} + {isConnected ? ( + + ) : ( + ) })} ) } + +function GroupOptionButton({ + option, + isActive, + size, + width, + groupId, + onChange, +}: { option: ButtonGroupOption; isActive: boolean } & Props) { + return ( + + ) +} diff --git a/packages/lib/shared/components/errors/UnbalancedAddError.tsx b/packages/lib/shared/components/errors/UnbalancedAddError.tsx index 915fe1512..dfed82ded 100644 --- a/packages/lib/shared/components/errors/UnbalancedAddError.tsx +++ b/packages/lib/shared/components/errors/UnbalancedAddError.tsx @@ -1,9 +1,15 @@ 'use client' import { AlertProps, Text } from '@chakra-ui/react' -import { isUnbalancedAddError } from '../../utils/error-filters' +import { + isInvariantRatioPIErrorMessage, + isInvariantRatioSimulationErrorMessage, + isUnbalancedAddError, + isUnbalancedAddErrorMessage, +} from '../../utils/error-filters' import { BalAlertLink } from '../alerts/BalAlertLink' import { ErrorAlert } from './ErrorAlert' +import { useAddLiquidity } from '@repo/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider' type Props = AlertProps & { error?: Error | null @@ -11,12 +17,20 @@ type Props = AlertProps & { } export function UnbalancedAddError({ error, goToProportionalAdds }: Props) { + const { clearAmountsIn } = useAddLiquidity() + const goToProportionalMode = () => { + clearAmountsIn() + goToProportionalAdds() + } + if (isUnbalancedAddError(error)) { return ( - + - Your input(s) would excessively unbalance the pool.{' '} - Please use proportional mode. + {getErrorMessage(error)}{' '} + + {getUseProportionalLabel(error)} + ) @@ -24,3 +38,28 @@ export function UnbalancedAddError({ error, goToProportionalAdds }: Props) { return null } + +// TODO: Improve these error messages +function getErrorTitle(error?: Error | null) { + if (isInvariantRatioPIErrorMessage(error?.message)) return 'Unknown price impact' + return 'Token amounts error' +} + +function getErrorMessage(error?: Error | null) { + if (!error) return 'Unexpected error.' + if (isUnbalancedAddErrorMessage(error)) { + return 'Your input(s) would excessively unbalance the pool.' + } + if (isInvariantRatioSimulationErrorMessage(error?.message)) { + return 'Your input(s) would cause an invariant error in the vault' + } + if (isInvariantRatioPIErrorMessage(error?.message)) { + return 'The price impact cannot be calculated. Proceed if you know exactly what you are doing or' + } + return 'Unexpected error. Please ask for support' +} + +function getUseProportionalLabel(error?: Error | null) { + if (isInvariantRatioPIErrorMessage(error?.message)) return 'try proportional mode.' + return 'Please use proportional mode.' +} diff --git a/packages/lib/shared/utils/error-filters.ts b/packages/lib/shared/utils/error-filters.ts index 6db7f6bfd..1666cd98c 100644 --- a/packages/lib/shared/utils/error-filters.ts +++ b/packages/lib/shared/utils/error-filters.ts @@ -57,7 +57,7 @@ export function isUnbalancedAddError(error?: Error | null): boolean { return false } -export function isUnbalancedAddErrorMessage(error: Error | null): boolean { +export function isUnbalancedAddErrorMessage(error?: Error | null): boolean { const errorStrings = ['BAL#304', 'queryAddLiquidityUnbalanced'] // [v2 error, v3 error] const hasErrors = (errorString: string) => error?.message.includes(errorString) @@ -72,10 +72,12 @@ export function isInvariantRatioSimulationErrorMessage(errorMessage?: string): b } export function isInvariantRatioPIErrorMessage(errorMessage?: string): boolean { + if (!errorMessage) return false if ( - errorMessage?.includes( + errorMessage.includes( 'addLiquidityUnbalanced operation will fail at SC level with user defined input.' - ) + ) || + errorMessage.includes('Arithmetic operation resulted in underflow or overflow.') ) { return true } From f6b76627098ad080106b5c51988adea15e811ca1 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 12:57:46 +0100 Subject: [PATCH 07/30] fix: do not show unbalanced error in nested adds --- .../pool/actions/add-liquidity/form/AddLiquidityForm.tsx | 8 ++++++-- packages/lib/shared/components/errors/GenericError.tsx | 7 +++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx index 9ec9985bc..d5cf614ee 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx @@ -119,11 +119,14 @@ function AddLiquidityMainForm() { const hasPriceImpact = priceImpact !== undefined && priceImpact !== null const priceImpactLabel = hasPriceImpact ? fNum('priceImpact', priceImpact) : '-' - const isUnbalancedError = isUnbalancedAddError(simulationQuery.error || priceImpactQuery.error) + const nestedAddLiquidityEnabled = supportsNestedActions(pool) // TODO && !userToggledEscapeHatch + + const isUnbalancedError = + !nestedAddLiquidityEnabled && //Nested adds to not support proportional for now + isUnbalancedAddError(simulationQuery.error || priceImpactQuery.error) const weeklyYield = calcPotentialYieldFor(pool, totalUSDValue) - const nestedAddLiquidityEnabled = supportsNestedActions(pool) // TODO && !userToggledEscapeHatch const isLoading = simulationQuery.isLoading || priceImpactQuery.isLoading const isFetching = simulationQuery.isFetching || priceImpactQuery.isFetching @@ -282,6 +285,7 @@ function AddLiquidityMainForm() { )} diff --git a/packages/lib/shared/components/errors/GenericError.tsx b/packages/lib/shared/components/errors/GenericError.tsx index 25e51691d..96517593c 100644 --- a/packages/lib/shared/components/errors/GenericError.tsx +++ b/packages/lib/shared/components/errors/GenericError.tsx @@ -6,7 +6,6 @@ import { isNotEnoughGasError, isPausedError, isTooManyRequestsError, - isUnbalancedAddError, isUserRejectedError, isViemHttpFetchError, } from '../../utils/error-filters' @@ -17,15 +16,15 @@ type ErrorWithOptionalShortMessage = Error & { shortMessage?: string } type Props = AlertProps & { error: ErrorWithOptionalShortMessage customErrorName?: string + skipError?: boolean } -export function GenericError({ error: _error, customErrorName, ...rest }: Props) { +export function GenericError({ error: _error, customErrorName, skipError, ...rest }: Props) { + if (skipError) return const error = ensureError(_error) if (isUserRejectedError(error)) return null const errorName = customErrorName ? `${customErrorName} (${error.name})` : error.name - if (isUnbalancedAddError(_error)) return null //We handle this specific error in UnbalancedAddError component - if (isViemHttpFetchError(_error)) { return ( From a6d6743200529dc819c9d81004407e5914d60a0b Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 13:43:00 +0100 Subject: [PATCH 08/30] fix: nested with top level with underlyings --- packages/lib/modules/tokens/token.helpers.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/lib/modules/tokens/token.helpers.ts b/packages/lib/modules/tokens/token.helpers.ts index bbe770a20..bd2c37da2 100644 --- a/packages/lib/modules/tokens/token.helpers.ts +++ b/packages/lib/modules/tokens/token.helpers.ts @@ -125,20 +125,27 @@ export function getLeafTokens(poolTokens: PoolToken[]) { t => !isSameAddress(t.address, poolToken.address) ) as PoolToken[] - const nestedLeafTokens = nestedTokens.map(t => - t.isErc4626 && t.underlyingToken - ? { ...t.underlyingToken, address: t.underlyingToken.address as Address, index: t.index } - : { ...t, address: t.address as Address } - ) + const nestedLeafTokens = nestedTokens.map(t => getTokenOrUnderlying(t)) leafTokens.push(...nestedLeafTokens) } else { - leafTokens.push(poolToken as TokenCore) + // TODO: add unit test for this case: pol id 0x42de4fa875126fdbaf590b2fc3802adbca58acee + leafTokens.push(getTokenOrUnderlying(poolToken)) } }) return leafTokens } +function getTokenOrUnderlying(token: PoolToken): TokenCore { + return token.isErc4626 && token.underlyingToken + ? { + ...token.underlyingToken, + address: token.underlyingToken.address as Address, + index: token.index, + } + : { ...token, address: token.address as Address } +} + export function getSpenderForAddLiquidity(pool: Pool): Address { if (isCowAmmPool(pool.type)) return pool.address as Address if (isV3Pool(pool)) { From 7d58595a2d304334cad72fdc9bdf31354b6f22e5 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 18:02:44 +0100 Subject: [PATCH 09/30] chore: simplify AddLiquidityFormTabs --- .../form/AddLiquidityFormTabs.tsx | 53 +++++-------------- .../handlers/selectAddLiquidityHandler.ts | 4 ++ 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index 1eb8b5e5f..b7e719f9c 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -10,34 +10,9 @@ import { supportsProportionalAddLiquidityKind, } from '../../LiquidityActionHelpers' import { useAddLiquidity } from '../AddLiquidityProvider' -import { TokenInputs } from './TokenInputs' import { TokenInputsMaybeProportional } from './TokenInputsMaybeProportional' const MIN_LIQUIDITY_FOR_BALANCED_ADD = 10000 -export function TokenInputsBase({ - nestedAddLiquidityEnabled, - tokenSelectDisclosure, - totalUSDValue, - isProportional, -}: { - nestedAddLiquidityEnabled: boolean - tokenSelectDisclosure: UseDisclosureReturn - totalUSDValue: string - isProportional: boolean -}) { - if (nestedAddLiquidityEnabled) { - return tokenSelectDisclosure.onOpen()} /> - } else { - return ( - tokenSelectDisclosure.onOpen()} - totalUSDValue={totalUSDValue} - /> - ) - } -} - export function AddLiquidityFormTabs({ tokenSelectDisclosure, totalUSDValue, @@ -58,6 +33,12 @@ export function AddLiquidityFormTabs({ const isDisabledProportionalTab = nestedAddLiquidityEnabled || !supportsProportionalAddLiquidityKind(pool) + /* TODO: test nested unbalanced + proportional calculations + it does not work now due to "'Reference amount must be relative to a token in the pool or its BPT'," + cause tokensWithBpt does not contain leaf tokens inside BPT: + http://localhost:3000/pools/sepolia/v3/0xc9233cc69435591b193b50f702ac31e404a08b10 + */ + // const isDisabledProportionalTab = !supportsProportionalAddLiquidityKind(pool) const isBelowMinTvlThreshold = !isDisabledProportionalTab && @@ -100,6 +81,8 @@ export function AddLiquidityFormTabs({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [isDisabledUnbalancedTab, isLoading]) + const isProportional = tabIndex === 1 + return ( - {tabIndex === 0 ? ( - - ) : ( - - )} + tokenSelectDisclosure.onOpen()} + totalUSDValue={totalUSDValue} + /> ) } diff --git a/packages/lib/modules/pool/actions/add-liquidity/handlers/selectAddLiquidityHandler.ts b/packages/lib/modules/pool/actions/add-liquidity/handlers/selectAddLiquidityHandler.ts index b0bce5406..ac46c1080 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/handlers/selectAddLiquidityHandler.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/handlers/selectAddLiquidityHandler.ts @@ -25,6 +25,10 @@ export function selectAddLiquidityHandler( // We should add a toggle to the form which allows the user to revert to // adding liquidity in the first level pool tokens. if (supportsNestedActions(pool)) { + // TODO: console.log( + // 'NestedAddLiquidityV3Handler should work with unbalanced + calculate proportional', + // { wantsProportional } + // ) return isV3Pool(pool) ? new NestedAddLiquidityV3Handler(pool) : new NestedAddLiquidityV2Handler(pool) From 98057ea1f12d11f0571a4adac6cbf41dd182f933 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 18:24:58 +0100 Subject: [PATCH 10/30] fix: disable wethIsEth for v3 boosted --- packages/lib/modules/pool/actions/LiquidityActionHelpers.ts | 4 ++-- .../pool/actions/add-liquidity/AddLiquidityProvider.tsx | 4 ++-- packages/lib/modules/pool/pool.helpers.ts | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts index 761d37902..461a42414 100644 --- a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts +++ b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts @@ -43,7 +43,7 @@ import { isUnbalancedLiquidityDisabled, isV2Pool, isV3Pool, - isV3WithNestedActionsPool, + isV3NotSupportingWethIsEth, } from '../pool.helpers' import { TokenAmountIn } from '../../tokens/approvals/permit2/useSignPermit2' @@ -359,7 +359,7 @@ export function emptyTokenAmounts(pool: Pool): TokenAmount[] { export function shouldShowNativeWrappedSelector(token: GqlToken, pool: Pool) { return ( - !isV3WithNestedActionsPool(pool) && // V3 nested actions don't support wethIsEth currently + !isV3NotSupportingWethIsEth(pool) && // V3 boosted/nested actions don't support wethIsEth currently !isCowAmmPool(pool.type) && // Cow AMM pools don't support wethIsEth isNativeOrWrappedNative(token.address as Address, token.chain) ) diff --git a/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx b/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx index 3efe4af88..3e01d10a5 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx @@ -28,7 +28,7 @@ import { useTotalUsdValue } from '@repo/lib/modules/tokens/useTotalUsdValue' import { HumanTokenAmountWithAddress } from '@repo/lib/modules/tokens/token.types' import { isUnhandledAddPriceImpactError } from '@repo/lib/modules/price-impact/price-impact.utils' import { useModalWithPoolRedirect } from '../../useModalWithPoolRedirect' -import { getPoolActionableTokens, isV3WithNestedActionsPool } from '../../pool.helpers' +import { getPoolActionableTokens, isV3NotSupportingWethIsEth } from '../../pool.helpers' import { useUserSettings } from '@repo/lib/modules/user/settings/UserSettingsProvider' import { isUnbalancedAddErrorMessage } from '@repo/lib/shared/utils/error-filters' @@ -192,7 +192,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) { return { transactionSteps, humanAmountsIn, - tokens: wethIsEth && !isV3WithNestedActionsPool(pool) ? tokensWithNativeAsset : tokens, + tokens: wethIsEth && !isV3NotSupportingWethIsEth(pool) ? tokensWithNativeAsset : tokens, validTokens, totalUSDValue, simulationQuery, diff --git a/packages/lib/modules/pool/pool.helpers.ts b/packages/lib/modules/pool/pool.helpers.ts index e935da85d..8cfe55b6a 100644 --- a/packages/lib/modules/pool/pool.helpers.ts +++ b/packages/lib/modules/pool/pool.helpers.ts @@ -341,6 +341,10 @@ export function isV3WithNestedActionsPool(pool: Pool): boolean { return supportsNestedActions(pool) && isV3Pool(pool) } +export function isV3NotSupportingWethIsEth(pool: Pool): boolean { + return (supportsNestedActions(pool) || isBoosted(pool)) && isV3Pool(pool) +} + export function requiresPermit2Approval(pool: Pool): boolean { return isV3Pool(pool) } From 64ea9fdb00b5aa4472f15dd80060131192da2210 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 19:01:41 +0100 Subject: [PATCH 11/30] fix: boostedPoolState when underlying token has undefined address --- .../modules/pool/actions/LiquidityActionHelpers.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts index 461a42414..8172eac53 100644 --- a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts +++ b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts @@ -88,12 +88,14 @@ export class LiquidityActionHelpers { ...token, address: token.address as Address, balance: token.balance as HumanAmount, - underlyingToken: { - ...token.underlyingToken, - address: token.underlyingToken?.address as Address, - decimals: token.underlyingToken?.decimals as number, - index, //TODO: review that this index is always the expected one - }, + underlyingToken: token.underlyingToken?.address + ? { + ...token.underlyingToken, + address: token.underlyingToken?.address as Address, + decimals: token.underlyingToken?.decimals as number, + index, //TODO: review that this index is always the expected one + } + : null, // TODO: unit test this case with http://localhost:3000/pools/sepolia/v3/0xbb83ba331c3254c8c44645430126797dceda89c0/add-liquidity }) ) const state: PoolStateWithUnderlyings & { totalShares: HumanAmount } = { From b305468f4c2959168d8b06b57c8c208fdda9732a Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 19:02:46 +0100 Subject: [PATCH 12/30] fix: avoid using ETH instead of WETH when pool does not support wethIsEth --- .../pool/actions/add-liquidity/form/AddLiquidityForm.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx index d5cf614ee..2ec69e0a7 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx @@ -56,6 +56,7 @@ import { useTokens } from '@repo/lib/modules/tokens/TokensProvider' import { AddLiquidityFormTabs } from './AddLiquidityFormTabs' import { UnbalancedAddError } from '@repo/lib/shared/components/errors/UnbalancedAddError' import { isUnbalancedAddError } from '@repo/lib/shared/utils/error-filters' +import { isV3NotSupportingWethIsEth } from '../../../pool.helpers' // small wrapper to prevent out of context error export function AddLiquidityForm() { @@ -144,7 +145,7 @@ function AddLiquidityMainForm() { // if native asset balance is higher set that asset as the 'default' useEffect(() => { - if (!isBalancesLoading && nativeAsset && wNativeAsset) { + if (!isBalancesLoading && nativeAsset && wNativeAsset && !isV3NotSupportingWethIsEth(pool)) { const nativeAssetBalance = balanceFor(nativeAsset.address) const wNativeAssetBalance = balanceFor(wNativeAsset.address) if ( From 8bfb117f4fc65c53f573ee1a0e5883bb85c3b347 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 19:05:24 +0100 Subject: [PATCH 13/30] fix: use token index instead of for index in boostedPoolState --- packages/lib/modules/pool/actions/LiquidityActionHelpers.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts index 8172eac53..bb99100bb 100644 --- a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts +++ b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts @@ -84,7 +84,7 @@ export class LiquidityActionHelpers { /* Used by V3 boosted SDK handlers */ public get boostedPoolState(): PoolStateWithUnderlyings & { totalShares: HumanAmount } { const poolTokensWithUnderlyings: PoolTokenWithUnderlying[] = this.pool.poolTokens.map( - (token, index) => ({ + token => ({ ...token, address: token.address as Address, balance: token.balance as HumanAmount, @@ -93,7 +93,7 @@ export class LiquidityActionHelpers { ...token.underlyingToken, address: token.underlyingToken?.address as Address, decimals: token.underlyingToken?.decimals as number, - index, //TODO: review that this index is always the expected one + index: token.index, //TODO: review that this index is always the expected one } : null, // TODO: unit test this case with http://localhost:3000/pools/sepolia/v3/0xbb83ba331c3254c8c44645430126797dceda89c0/add-liquidity }) From 81fb155a911442f4fca52b010a6dd66c2b2ec177 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sat, 30 Nov 2024 19:52:53 +0100 Subject: [PATCH 14/30] fix: use explicit referenceAmount by address in Proportional adds --- .../actions/add-liquidity/AddLiquidityProvider.tsx | 5 +++++ .../add-liquidity/form/useProportionalInputs.tsx | 5 ++++- .../add-liquidity/handlers/AddLiquidity.handler.ts | 3 ++- .../ProportionalBoostedAddLiquidityV3.handler.ts | 13 +++++++++++-- .../queries/useAddLiquiditySimulationQuery.ts | 12 ++++++++++-- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx b/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx index 3e01d10a5..63eee9949 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx @@ -37,6 +37,8 @@ export const AddLiquidityContext = createContext export function _useAddLiquidity(urlTxHash?: Hash) { const [humanAmountsIn, setHumanAmountsIn] = useState([]) + // only used by Proportional handlers that require a referenceAmount + const [referenceAmountAddress, setReferenceAmountAddress] = useState
() const [needsToAcceptHighPI, setNeedsToAcceptHighPI] = useState(false) const [acceptPoolRisks, setAcceptPoolRisks] = useState(false) const [wethIsEth, setWethIsEth] = useState(false) @@ -123,6 +125,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) { handler, humanAmountsIn, enabled: !urlTxHash, + referenceAmountAddress, }) const priceImpactQuery = useAddLiquidityPriceImpactQuery({ handler, @@ -215,12 +218,14 @@ export function _useAddLiquidity(urlTxHash?: Hash) { proportionalSlippage, isForcedProportionalAdd, wantsProportional, + referenceAmountAddress, setWantsProportional, setProportionalSlippage, refetchQuote, setHumanAmountIn, setHumanAmountsIn, clearAmountsIn, + setReferenceAmountAddress, setNeedsToAcceptHighPI, setAcceptPoolRisks, setWethIsEth, diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/useProportionalInputs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/useProportionalInputs.tsx index c41b3c84e..9cd461945 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/useProportionalInputs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/useProportionalInputs.tsx @@ -14,13 +14,16 @@ import { useAddLiquidity } from '../AddLiquidityProvider' export function useProportionalInputs() { const { isConnected } = useUserAccount() - const { helpers, setHumanAmountsIn, clearAmountsIn, wethIsEth } = useAddLiquidity() + const { helpers, setHumanAmountsIn, clearAmountsIn, wethIsEth, setReferenceAmountAddress } = + useAddLiquidity() const { balances, isBalancesLoading } = useTokenBalances() const { isLoading: isPoolLoading } = usePool() function handleProportionalHumanInputChange(tokenAddress: Address, humanAmount: HumanAmount) { if (isEmptyHumanAmount(humanAmount)) return clearAmountsIn({ tokenAddress, humanAmount }) + setReferenceAmountAddress(tokenAddress) + const proportionalHumanAmountsIn = _calculateProportionalHumanAmountsIn({ tokenAddress, humanAmount, diff --git a/packages/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts b/packages/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts index 611a51ce2..783967b4e 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/handlers/AddLiquidity.handler.ts @@ -21,7 +21,8 @@ export interface AddLiquidityHandler { // Also returns bptOut to be used by the UI simulate( humanAmountsIn: HumanTokenAmountWithAddress[], - sender: Address + sender: Address, + referenceAmountAddress?: Address // only used by Proportional handlers that require a referenceAmount ): Promise // Calculate the price impact of adding liquidity diff --git a/packages/lib/modules/pool/actions/add-liquidity/handlers/ProportionalBoostedAddLiquidityV3.handler.ts b/packages/lib/modules/pool/actions/add-liquidity/handlers/ProportionalBoostedAddLiquidityV3.handler.ts index 0ba4c7106..578b106a5 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/handlers/ProportionalBoostedAddLiquidityV3.handler.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/handlers/ProportionalBoostedAddLiquidityV3.handler.ts @@ -6,6 +6,7 @@ import { Address, Hex, InputAmount, + isSameAddress, } from '@balancer/sdk' import { HumanTokenAmountWithAddress } from '@repo/lib/modules/tokens/token.types' import { TransactionConfig } from '@repo/lib/modules/web3/contracts/contract.types' @@ -29,9 +30,17 @@ export class ProportionalBoostedAddLiquidityV3 implements AddLiquidityHandler { public async simulate( humanAmountsIn: HumanTokenAmountWithAddress[], - userAddress: Address + userAddress: Address, + referenceAmountAddress?: Address ): Promise { - const referenceAmount = this.helpers.toSdkInputAmounts(humanAmountsIn)[0] + // TODO: generalize to other handlers with referenceAmountAddress + const inputAmounts = this.helpers.toSdkInputAmounts(humanAmountsIn) + const foundReferenceAmount = + referenceAmountAddress && + inputAmounts.find(item => isSameAddress(item.address, referenceAmountAddress)) + const referenceAmount = foundReferenceAmount || inputAmounts[0] + + console.log({ referenceAmount }) const addLiquidity = new AddLiquidityBoostedV3() diff --git a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts index 76bec5fdd..3c7050c4e 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts @@ -13,6 +13,7 @@ import { sentryMetaForAddLiquidityHandler } from '@repo/lib/shared/utils/query-e import { HumanTokenAmountWithAddress } from '@repo/lib/modules/tokens/token.types' import { useBlockNumber } from 'wagmi' import { isInvariantRatioSimulationErrorMessage } from '@repo/lib/shared/utils/error-filters' +import { Address } from 'viem' export type AddLiquiditySimulationQueryResult = ReturnType @@ -20,9 +21,15 @@ type Params = { handler: AddLiquidityHandler humanAmountsIn: HumanTokenAmountWithAddress[] enabled: boolean + referenceAmountAddress?: Address // only used by Proportional handlers that require a referenceAmount } -export function useAddLiquiditySimulationQuery({ handler, humanAmountsIn, enabled }: Params) { +export function useAddLiquiditySimulationQuery({ + handler, + humanAmountsIn, + enabled, + referenceAmountAddress, +}: Params) { const { userAddress } = useUserAccount() const { pool, chainId } = usePool() const { data: blockNumber } = useBlockNumber({ chainId }) @@ -39,7 +46,8 @@ export function useAddLiquiditySimulationQuery({ handler, humanAmountsIn, enable const queryKey = addLiquidityKeys.preview(params) - const queryFn = async () => handler.simulate(debouncedHumanAmountsIn, userAddress) + const queryFn = async () => + handler.simulate(debouncedHumanAmountsIn, userAddress, referenceAmountAddress) return useQuery({ queryKey, From 9e0e9c7be831f8fd2d2e059dbec9f20226dac0fe Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sun, 1 Dec 2024 08:05:10 +0100 Subject: [PATCH 15/30] debug: log silenced PI query error --- .../add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts index a42053a75..6f33e6015 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts @@ -45,6 +45,7 @@ export function useAddLiquidityPriceImpactQuery({ handler, humanAmountsIn, enabl enabled: enabled && !areEmptyAmounts(debouncedHumanAmountsIn), retry(failureCount, error) { if (isInvariantRatioPIErrorMessage(error?.message)) { + console.log('Silenced PI error: ', error) // Avoid more retries return false } From 11f487d97413488feac80b46d7a87ef3efcb2843 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sun, 1 Dec 2024 08:31:39 +0100 Subject: [PATCH 16/30] debug: log nested PI inputs --- .../add-liquidity/handlers/NestedAddLiquidityV3.handler.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts b/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts index 4c14e3076..7b1a26432 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts @@ -31,6 +31,9 @@ export class NestedAddLiquidityV3Handler implements AddLiquidityHandler { return 0 } const input = this.constructSdkInput(humanAmountsIn) + + console.log('NESTED PRICE IMPACT: ', { input, nestedPoolState: this.helpers.nestedPoolStateV3 }) + const priceImpactABA = await PriceImpact.addLiquidityNested( input, this.helpers.nestedPoolStateV3 From b4ef6c2e82eb3a263ea28fc30cd8d01696072273 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sun, 1 Dec 2024 09:00:45 +0100 Subject: [PATCH 17/30] debug: avoid retry limit on PI error --- .../add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts index 6f33e6015..237f6851c 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts @@ -45,9 +45,10 @@ export function useAddLiquidityPriceImpactQuery({ handler, humanAmountsIn, enabl enabled: enabled && !areEmptyAmounts(debouncedHumanAmountsIn), retry(failureCount, error) { if (isInvariantRatioPIErrorMessage(error?.message)) { - console.log('Silenced PI error: ', error) + console.log('Silenced PI error: ', { error, failureCount }) // Avoid more retries - return false + // return false + return failureCount < 3 } // 3 retries by default return failureCount < 3 From 8067449a94a300c60a6eaf91059774a174a0d5b8 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sun, 1 Dec 2024 09:11:38 +0100 Subject: [PATCH 18/30] debug: alchemy route --- apps/frontend-v3/app/api/rpc/[chain]/route.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/frontend-v3/app/api/rpc/[chain]/route.ts b/apps/frontend-v3/app/api/rpc/[chain]/route.ts index cbd60b37a..986fb0fb2 100644 --- a/apps/frontend-v3/app/api/rpc/[chain]/route.ts +++ b/apps/frontend-v3/app/api/rpc/[chain]/route.ts @@ -18,7 +18,8 @@ const chainToRpcMap: Record = { [GqlChain.Polygon]: dRpcUrl('polygon'), [GqlChain.Avalanche]: dRpcUrl('avalanche'), [GqlChain.Fantom]: dRpcUrl('fantom'), - [GqlChain.Sepolia]: dRpcUrl('sepolia'), + // [GqlChain.Sepolia]: dRpcUrl('sepolia'), + [GqlChain.Sepolia]: 'https://eth-sepolia.g.alchemy.com/v2/0fWVFS_mrORIl5Ge3y9NFX1zZU1LbvDl', [GqlChain.Fraxtal]: dRpcUrl('fraxtal'), [GqlChain.Gnosis]: dRpcUrl('gnosis'), [GqlChain.Mode]: dRpcUrl('mode'), From cd0ef87a55c1487a1e71cbe4570788e0df6780e3 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Sun, 1 Dec 2024 09:21:06 +0100 Subject: [PATCH 19/30] debug: alchemy route 2 --- apps/frontend-v3/app/api/rpc/[chain]/route.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/frontend-v3/app/api/rpc/[chain]/route.ts b/apps/frontend-v3/app/api/rpc/[chain]/route.ts index 986fb0fb2..204f256b3 100644 --- a/apps/frontend-v3/app/api/rpc/[chain]/route.ts +++ b/apps/frontend-v3/app/api/rpc/[chain]/route.ts @@ -18,8 +18,9 @@ const chainToRpcMap: Record = { [GqlChain.Polygon]: dRpcUrl('polygon'), [GqlChain.Avalanche]: dRpcUrl('avalanche'), [GqlChain.Fantom]: dRpcUrl('fantom'), - // [GqlChain.Sepolia]: dRpcUrl('sepolia'), - [GqlChain.Sepolia]: 'https://eth-sepolia.g.alchemy.com/v2/0fWVFS_mrORIl5Ge3y9NFX1zZU1LbvDl', + [GqlChain.Sepolia]: dRpcUrl('sepolia'), + // DEBUG: + // [GqlChain.Sepolia]: 'https://eth-sepolia.g.alchemy.com/v2/', [GqlChain.Fraxtal]: dRpcUrl('fraxtal'), [GqlChain.Gnosis]: dRpcUrl('gnosis'), [GqlChain.Mode]: dRpcUrl('mode'), From 720141ba0bfb1f5cd7fd4526941f0a4f9fc07b09 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Mon, 2 Dec 2024 08:13:00 +0100 Subject: [PATCH 20/30] chore: clean console logs --- ...LiquidityActionHelpers.integration.spec.ts | 79 +++++++++++++++++++ .../handlers/NestedAddLiquidityV3.handler.ts | 2 - .../useAddLiquidityPriceImpactQuery.ts | 2 +- .../queries/useAddLiquiditySimulationQuery.ts | 1 + 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts b/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts index eef8b9171..31162907e 100644 --- a/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts +++ b/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts @@ -14,6 +14,7 @@ import { GqlChain, GqlPoolElement } from '@repo/lib/shared/services/api/generate import { getPoolMock } from '../__mocks__/getPoolMock' import { allPoolTokens } from '../pool.helpers' import { LiquidityActionHelpers } from './LiquidityActionHelpers' +import { Pool } from '../PoolProvider' describe('Calculates toInputAmounts from allPoolTokens', () => { it('for v2 weighted pool with no nested tokens', async () => { @@ -293,3 +294,81 @@ describe.skip('Liquidity helpers for V3 NESTED pool', async () => { ]) }) }) + +// Unskip when sepolia V3 pools are available in production api +test.skip('Nested pool state for V3 NESTED POOL', async () => { + // const poolId = '0xc9233cc69435591b193b50f702ac31e404a08b10' // Sepolia Balancer 50 WETH 50 USD + + const usdcSepoliaAddress = '0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8' + const daiSepoliaAddress = '0xff34b3d4aee8ddcd6f9afffb6fe49bd371b8a357' + const wethSepoliaAddress = '0x7b79995e5f793a07bc00c21412e50ecae098e7f9' + // const v3Pool = await getPoolMock(poolId, GqlChain.Sepolia) + const v3Pool = {} as Pool + + const helpers = new LiquidityActionHelpers(v3Pool) + + const state = helpers.nestedPoolStateV3 + + expect(state).toEqual({ + mainTokens: [ + { + address: wethSepoliaAddress, + decimals: 18, + index: 0, + }, + { + address: usdcSepoliaAddress, + decimals: 6, + index: 0, + }, + { + address: daiSepoliaAddress, + decimals: 18, + index: 1, + }, + ], + pools: [ + { + address: '0xc9233cc69435591b193b50f702ac31e404a08b10', + id: '0xc9233cc69435591b193b50f702ac31e404a08b10', + level: 1, + tokens: [ + { + address: wethSepoliaAddress, + decimals: 18, + index: 0, + underlyingToken: null, + }, + { + address: '0x946e59e9637f44eb122fe64b372aaf6ed0441da1', + decimals: 18, + index: 1, + underlyingToken: null, + }, + ], + type: 'Weighted', + }, + { + address: '0x946e59e9637f44eb122fe64b372aaf6ed0441da1', + id: '0x946e59e9637f44eb122fe64b372aaf6ed0441da1', + level: 0, + tokens: [ + { + address: usdcSepoliaAddress, + decimals: 6, + index: 0, + underlyingToken: null, + }, + { + address: daiSepoliaAddress, + decimals: 18, + index: 1, + underlyingToken: null, + }, + ], + type: 'Weighted', + }, + ], + protocolVersion: 3, + }) +}) diff --git a/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts b/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts index 7b1a26432..d83da2ce1 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/handlers/NestedAddLiquidityV3.handler.ts @@ -32,8 +32,6 @@ export class NestedAddLiquidityV3Handler implements AddLiquidityHandler { } const input = this.constructSdkInput(humanAmountsIn) - console.log('NESTED PRICE IMPACT: ', { input, nestedPoolState: this.helpers.nestedPoolStateV3 }) - const priceImpactABA = await PriceImpact.addLiquidityNested( input, this.helpers.nestedPoolStateV3 diff --git a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts index 237f6851c..831e4f024 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquidityPriceImpactQuery.ts @@ -45,7 +45,7 @@ export function useAddLiquidityPriceImpactQuery({ handler, humanAmountsIn, enabl enabled: enabled && !areEmptyAmounts(debouncedHumanAmountsIn), retry(failureCount, error) { if (isInvariantRatioPIErrorMessage(error?.message)) { - console.log('Silenced PI error: ', { error, failureCount }) + if (failureCount === 1) console.log('Silenced PI error: ', { error }) // Avoid more retries // return false return failureCount < 3 diff --git a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts index 3c7050c4e..5f5191fd7 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/queries/useAddLiquiditySimulationQuery.ts @@ -56,6 +56,7 @@ export function useAddLiquiditySimulationQuery({ gcTime: 0, retry(failureCount, error) { if (isInvariantRatioSimulationErrorMessage(error?.message)) { + if (failureCount === 1) console.log('Silenced simulation error: ', { error }) // Avoid more retries return false } From a8dfe029fbda81403c5e92f9d4b2a617df6100b0 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Mon, 2 Dec 2024 09:56:29 +0100 Subject: [PATCH 21/30] chore: improve ratio error messages --- .../add-liquidity/form/AddLiquidityForm.tsx | 7 ++- .../components/errors/UnbalancedAddError.tsx | 52 ++++++++++++++----- packages/lib/shared/utils/error-filters.ts | 11 +++- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx index 2ec69e0a7..2b5d7e8eb 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityForm.tsx @@ -122,9 +122,7 @@ function AddLiquidityMainForm() { const nestedAddLiquidityEnabled = supportsNestedActions(pool) // TODO && !userToggledEscapeHatch - const isUnbalancedError = - !nestedAddLiquidityEnabled && //Nested adds to not support proportional for now - isUnbalancedAddError(simulationQuery.error || priceImpactQuery.error) + const isUnbalancedError = isUnbalancedAddError(simulationQuery.error || priceImpactQuery.error) const weeklyYield = calcPotentialYieldFor(pool, totalUSDValue) @@ -219,6 +217,7 @@ function AddLiquidityMainForm() { )} @@ -247,7 +246,7 @@ function AddLiquidityMainForm() { totalUSDValue={totalUSDValue} /> } - avoidPriceImpactAlert={isUnbalancedError} + avoidPriceImpactAlert={isUnbalancedError && !nestedAddLiquidityEnabled} cannotCalculatePriceImpact={cannotCalculatePriceImpactError(priceImpactQuery.error)} isDisabled={!priceImpactQuery.data} setNeedsToAcceptPIRisk={setNeedsToAcceptHighPI} diff --git a/packages/lib/shared/components/errors/UnbalancedAddError.tsx b/packages/lib/shared/components/errors/UnbalancedAddError.tsx index dfed82ded..298243eb3 100644 --- a/packages/lib/shared/components/errors/UnbalancedAddError.tsx +++ b/packages/lib/shared/components/errors/UnbalancedAddError.tsx @@ -2,8 +2,9 @@ import { AlertProps, Text } from '@chakra-ui/react' import { + isInvariantRatioAboveMaxSimulationErrorMessage, + isInvariantRatioAboveMinSimulationErrorMessage, isInvariantRatioPIErrorMessage, - isInvariantRatioSimulationErrorMessage, isUnbalancedAddError, isUnbalancedAddErrorMessage, } from '../../utils/error-filters' @@ -14,9 +15,14 @@ import { useAddLiquidity } from '@repo/lib/modules/pool/actions/add-liquidity/Ad type Props = AlertProps & { error?: Error | null goToProportionalAdds: () => void + isProportionalSupported?: boolean } -export function UnbalancedAddError({ error, goToProportionalAdds }: Props) { +export function UnbalancedAddError({ + error, + goToProportionalAdds, + isProportionalSupported = true, +}: Props) { const { clearAmountsIn } = useAddLiquidity() const goToProportionalMode = () => { clearAmountsIn() @@ -27,10 +33,14 @@ export function UnbalancedAddError({ error, goToProportionalAdds }: Props) { return ( - {getErrorMessage(error)}{' '} - - {getUseProportionalLabel(error)} - + {getErrorMessage(isProportionalSupported, error)}{' '} + {isProportionalSupported ? ( + + {getUseProportionalLabel(isProportionalSupported, error)} + + ) : ( + getUseProportionalLabel(isProportionalSupported, error) + )} ) @@ -41,25 +51,39 @@ export function UnbalancedAddError({ error, goToProportionalAdds }: Props) { // TODO: Improve these error messages function getErrorTitle(error?: Error | null) { + if (isInvariantRatioAboveMaxSimulationErrorMessage(error?.message)) { + return 'Amount exceeds pool limits' + } + if (isInvariantRatioAboveMinSimulationErrorMessage(error?.message)) { + return 'Amount is below pool limits' + } + if (isInvariantRatioPIErrorMessage(error?.message)) return 'Unknown price impact' return 'Token amounts error' } -function getErrorMessage(error?: Error | null) { +function getErrorMessage(isProportionalSupported: boolean, error?: Error | null) { if (!error) return 'Unexpected error.' - if (isUnbalancedAddErrorMessage(error)) { - return 'Your input(s) would excessively unbalance the pool.' - } - if (isInvariantRatioSimulationErrorMessage(error?.message)) { - return 'Your input(s) would cause an invariant error in the vault' + + if (isInvariantRatioAboveMaxSimulationErrorMessage(error?.message)) { + return 'Your input(s) would cause an invariant error in the vault.' } if (isInvariantRatioPIErrorMessage(error?.message)) { + if (!isProportionalSupported) { + return 'The price impact cannot be calculated. Proceed if you know exactly what you are doing.' + } return 'The price impact cannot be calculated. Proceed if you know exactly what you are doing or' } + if (isUnbalancedAddErrorMessage(error)) { + return 'Your input(s) would excessively unbalance the pool.' + } return 'Unexpected error. Please ask for support' } -function getUseProportionalLabel(error?: Error | null) { - if (isInvariantRatioPIErrorMessage(error?.message)) return 'try proportional mode.' +function getUseProportionalLabel(isProportionalSupported: boolean, error?: Error | null) { + if (!isProportionalSupported) return 'Please try different amounts.' + if (isInvariantRatioPIErrorMessage(error?.message)) { + return 'try proportional mode.' + } return 'Please use proportional mode.' } diff --git a/packages/lib/shared/utils/error-filters.ts b/packages/lib/shared/utils/error-filters.ts index 1666cd98c..acad457db 100644 --- a/packages/lib/shared/utils/error-filters.ts +++ b/packages/lib/shared/utils/error-filters.ts @@ -66,11 +66,18 @@ export function isUnbalancedAddErrorMessage(error?: Error | null): boolean { export function isInvariantRatioSimulationErrorMessage(errorMessage?: string): boolean { return ( - !!errorMessage?.includes('InvariantRatioAboveMax') || - !!errorMessage?.includes('InvariantRatioBelowMin') + isInvariantRatioAboveMaxSimulationErrorMessage(errorMessage) || + isInvariantRatioAboveMinSimulationErrorMessage(errorMessage) ) } +export function isInvariantRatioAboveMaxSimulationErrorMessage(errorMessage?: string): boolean { + return !!errorMessage?.includes('InvariantRatioAboveMax') +} +export function isInvariantRatioAboveMinSimulationErrorMessage(errorMessage?: string): boolean { + return !!errorMessage?.includes('InvariantRatioBelowMin') +} + export function isInvariantRatioPIErrorMessage(errorMessage?: string): boolean { if (!errorMessage) return false if ( From b4dea109ddc330644edcf146dce8b009250da941 Mon Sep 17 00:00:00 2001 From: groninge Date: Mon, 2 Dec 2024 11:05:29 +0100 Subject: [PATCH 22/30] add optional prop for full width --- .../components/btns/button-group/ButtonGroup.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx b/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx index 72fca2e8a..fed68eb2a 100644 --- a/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx +++ b/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx @@ -26,13 +26,21 @@ type Props = { groupId: string size?: ButtonProps['size'] width?: ButtonProps['width'] + isFullWidth?: boolean } export default function ButtonGroup(props: Props) { - const { groupId, options, currentOption } = props + const { groupId, options, currentOption, isFullWidth } = props return ( - + {options.map(option => { const isActive = currentOption?.value === option.value return option?.tooltipLabel ? ( From 1ac39a668d3957dbde5ce86047bf91b83a1aa6bd Mon Sep 17 00:00:00 2001 From: groninge Date: Mon, 2 Dec 2024 11:07:11 +0100 Subject: [PATCH 23/30] set to full width --- .../pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index b7e719f9c..a8bd1f332 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -88,10 +88,11 @@ export function AddLiquidityFormTabs({ Date: Mon, 2 Dec 2024 11:16:48 +0100 Subject: [PATCH 24/30] add optional prop for larger text --- .../lib/shared/components/btns/button-group/ButtonGroup.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx b/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx index fed68eb2a..83ec3b85c 100644 --- a/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx +++ b/packages/lib/shared/components/btns/button-group/ButtonGroup.tsx @@ -27,6 +27,7 @@ type Props = { size?: ButtonProps['size'] width?: ButtonProps['width'] isFullWidth?: boolean + hasLargeTextLabel?: boolean } export default function ButtonGroup(props: Props) { @@ -78,6 +79,7 @@ function GroupOptionButton({ size, width, groupId, + hasLargeTextLabel, onChange, }: { option: ButtonGroupOption; isActive: boolean } & Props) { return ( @@ -105,7 +107,7 @@ function GroupOptionButton({ shadow="md" /> )} - + {option.label} From 08d148bf564ecd12f1c27d7e3a59a66f6b460db4 Mon Sep 17 00:00:00 2001 From: groninge Date: Mon, 2 Dec 2024 11:17:39 +0100 Subject: [PATCH 25/30] set larger text for buttons --- .../pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index a8bd1f332..a8d121208 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -88,6 +88,7 @@ export function AddLiquidityFormTabs({ Date: Mon, 2 Dec 2024 11:38:32 +0100 Subject: [PATCH 26/30] chore: refactor unbalanced error label logic --- .../components/errors/UnbalancedAddError.tsx | 99 ++++++++++--------- .../errors/UnbalancedError.test.tsx | 73 ++++++++++++++ 2 files changed, 125 insertions(+), 47 deletions(-) create mode 100644 packages/lib/shared/components/errors/UnbalancedError.test.tsx diff --git a/packages/lib/shared/components/errors/UnbalancedAddError.tsx b/packages/lib/shared/components/errors/UnbalancedAddError.tsx index 298243eb3..776e51c2e 100644 --- a/packages/lib/shared/components/errors/UnbalancedAddError.tsx +++ b/packages/lib/shared/components/errors/UnbalancedAddError.tsx @@ -29,61 +29,66 @@ export function UnbalancedAddError({ goToProportionalAdds() } - if (isUnbalancedAddError(error)) { - return ( - - - {getErrorMessage(isProportionalSupported, error)}{' '} - {isProportionalSupported ? ( - - {getUseProportionalLabel(isProportionalSupported, error)} - - ) : ( - getUseProportionalLabel(isProportionalSupported, error) - )} - - - ) - } + if (!isUnbalancedAddError(error)) return null - return null -} + const errorLabels = getErrorLabels(isProportionalSupported, error) -// TODO: Improve these error messages -function getErrorTitle(error?: Error | null) { - if (isInvariantRatioAboveMaxSimulationErrorMessage(error?.message)) { - return 'Amount exceeds pool limits' - } - if (isInvariantRatioAboveMinSimulationErrorMessage(error?.message)) { - return 'Amount is below pool limits' - } + if (!errorLabels) return null - if (isInvariantRatioPIErrorMessage(error?.message)) return 'Unknown price impact' - return 'Token amounts error' + return ( + + + {errorLabels.errorMessage}{' '} + {isProportionalSupported ? ( + + {errorLabels.proportionalLabel} + + ) : ( + errorLabels.proportionalLabel + )} + + + ) } -function getErrorMessage(isProportionalSupported: boolean, error?: Error | null) { - if (!error) return 'Unexpected error.' +export type UnbalancedErrorLabels = { + errorTitle: string + errorMessage: string + proportionalLabel: string +} - if (isInvariantRatioAboveMaxSimulationErrorMessage(error?.message)) { - return 'Your input(s) would cause an invariant error in the vault.' - } - if (isInvariantRatioPIErrorMessage(error?.message)) { - if (!isProportionalSupported) { - return 'The price impact cannot be calculated. Proceed if you know exactly what you are doing.' - } - return 'The price impact cannot be calculated. Proceed if you know exactly what you are doing or' +export function getErrorLabels( + isProportionalSupported: boolean, + error?: Error | null +): UnbalancedErrorLabels | undefined { + let errorTitle = 'Token amounts error' + let errorMessage = 'Unexpected error. Please ask for support' + let proportionalLabel = 'Please use proportional mode.' + + if (!error) return + + if (isInvariantRatioAboveMaxSimulationErrorMessage(error.message)) { + errorTitle = 'Amount exceeds pool limits' + errorMessage = 'Your input(s) would cause an invariant error in the vault.' + } else if (isInvariantRatioAboveMinSimulationErrorMessage(error.message)) { + errorTitle = 'Amount is below pool limits' + } else if (isInvariantRatioPIErrorMessage(error.message)) { + errorTitle = 'Unknown price impact' + errorMessage = isProportionalSupported + ? 'The price impact cannot be calculated. Proceed if you know exactly what you are doing or' + : 'The price impact cannot be calculated. Proceed if you know exactly what you are doing.' + proportionalLabel = 'try proportional mode.' + } else if (isUnbalancedAddErrorMessage(error)) { + errorMessage = 'Your input(s) would excessively unbalance the pool.' } - if (isUnbalancedAddErrorMessage(error)) { - return 'Your input(s) would excessively unbalance the pool.' + + if (!isProportionalSupported) { + proportionalLabel = 'Please try different amounts.' } - return 'Unexpected error. Please ask for support' -} -function getUseProportionalLabel(isProportionalSupported: boolean, error?: Error | null) { - if (!isProportionalSupported) return 'Please try different amounts.' - if (isInvariantRatioPIErrorMessage(error?.message)) { - return 'try proportional mode.' + return { + errorTitle, + errorMessage, + proportionalLabel, } - return 'Please use proportional mode.' } diff --git a/packages/lib/shared/components/errors/UnbalancedError.test.tsx b/packages/lib/shared/components/errors/UnbalancedError.test.tsx new file mode 100644 index 000000000..fbd34005a --- /dev/null +++ b/packages/lib/shared/components/errors/UnbalancedError.test.tsx @@ -0,0 +1,73 @@ +import { describe, it, expect } from 'vitest' +import { getErrorLabels } from './UnbalancedAddError' + +describe('getErrorLabels', () => { + const invariantRatioAboveMaxError = new Error('InvariantRatioAboveMax') + const invariantRatioAboveMinError = new Error('InvariantRatioBelowMin') + const invariantRatioPIError = new Error( + 'addLiquidityUnbalanced operation will fail at SC level with user defined input.' + ) + const unbalancedAddErrorV2 = new Error('BAL#304') + const unbalancedAddErrorV3 = new Error('queryAddLiquidityUnbalanced') + + it('should return default error labels when no error is provided', () => { + const result = getErrorLabels(true, null) + expect(result).toBeUndefined() + }) + + it('should return correct labels for isInvariantRatioAboveMaxSimulationErrorMessage', () => { + const result = getErrorLabels(true, invariantRatioAboveMaxError) + expect(result).toEqual({ + errorTitle: 'Amount exceeds pool limits', + errorMessage: 'Your input(s) would cause an invariant error in the vault.', + proportionalLabel: 'Please use proportional mode.', + }) + }) + + it('should return correct labels for isInvariantRatioAboveMinSimulationErrorMessage', () => { + const result = getErrorLabels(true, invariantRatioAboveMinError) + expect(result).toEqual({ + errorTitle: 'Amount is below pool limits', + errorMessage: 'Unexpected error. Please ask for support', + proportionalLabel: 'Please use proportional mode.', + }) + }) + + it('should return correct labels for isInvariantRatioPIErrorMessage when proportional is supported', () => { + const result = getErrorLabels(true, invariantRatioPIError) + expect(result).toEqual({ + errorTitle: 'Unknown price impact', + errorMessage: + 'The price impact cannot be calculated. Proceed if you know exactly what you are doing or', + proportionalLabel: 'try proportional mode.', + }) + }) + + it('should return correct labels for isInvariantRatioPIErrorMessage when proportional is not supported', () => { + const result = getErrorLabels(false, invariantRatioPIError) + expect(result).toEqual({ + errorTitle: 'Unknown price impact', + errorMessage: + 'The price impact cannot be calculated. Proceed if you know exactly what you are doing.', + proportionalLabel: 'Please try different amounts.', + }) + }) + + it('should return correct labels for isUnbalancedAddErrorMessage (v2)', () => { + const result = getErrorLabels(true, unbalancedAddErrorV2) + expect(result).toEqual({ + errorTitle: 'Token amounts error', + errorMessage: 'Your input(s) would excessively unbalance the pool.', + proportionalLabel: 'Please use proportional mode.', + }) + }) + + it('should return correct labels for isUnbalancedAddErrorMessage (v3)', () => { + const result = getErrorLabels(true, unbalancedAddErrorV3) + expect(result).toEqual({ + errorTitle: 'Token amounts error', + errorMessage: 'Your input(s) would excessively unbalance the pool.', + proportionalLabel: 'Please use proportional mode.', + }) + }) +}) From 8b9c851933858af76881e0cd3f0efb2ac4087cb6 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Mon, 2 Dec 2024 11:39:22 +0100 Subject: [PATCH 27/30] chore: bump sdk to 0.33.2 --- .../handlers/BaseProportionalAddLiquidity.handler.ts | 1 - packages/lib/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/handlers/BaseProportionalAddLiquidity.handler.ts b/packages/lib/modules/pool/actions/add-liquidity/handlers/BaseProportionalAddLiquidity.handler.ts index 9dbd3e83f..8a9376a3c 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/handlers/BaseProportionalAddLiquidity.handler.ts +++ b/packages/lib/modules/pool/actions/add-liquidity/handlers/BaseProportionalAddLiquidity.handler.ts @@ -76,7 +76,6 @@ export abstract class BaseProportionalAddLiquidityHandler implements AddLiquidit referenceAmount: InputAmount, userAddress: Address ): AddLiquidityProportionalInput { - console.log({ referenceAmount }) return { chainId: this.helpers.chainId, rpcUrl: getRpcUrl(this.helpers.chainId), diff --git a/packages/lib/package.json b/packages/lib/package.json index bf936fa99..296fecaf8 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@apollo/client": "^3.11.8", - "@balancer/sdk": "0.33.1", + "@balancer/sdk": "0.33.2", "@chakra-ui/anatomy": "^2.2.2", "@chakra-ui/clickable": "^2.1.0", "@chakra-ui/hooks": "^2.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b2469eec..092c388b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -326,8 +326,8 @@ importers: specifier: ^3.11.8 version: 3.11.8(@types/react@18.2.34)(graphql-ws@5.16.0(graphql@16.9.0))(graphql@16.9.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@balancer/sdk': - specifier: 0.33.1 - version: 0.33.1(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8) + specifier: 0.33.2 + version: 0.33.2(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8) '@chakra-ui/anatomy': specifier: ^2.2.2 version: 2.2.2 @@ -1709,8 +1709,8 @@ packages: resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==} engines: {node: '>=6.9.0'} - '@balancer/sdk@0.33.1': - resolution: {integrity: sha512-JwS6wva9g2Hf8RsV3MoJIwHcctFnS77MUlrp/pC/7GfUeKwu++veWGJbVxjO0xo0PYcALSnvHGqp3YmZlPtP3g==} + '@balancer/sdk@0.33.2': + resolution: {integrity: sha512-NaM/EFFPGYiTrLDK7WoKuEWb9ntfGTyTbfsZMyE63q4ui0C5gOGNVoPlZjsHJ9S719Dk13HRW4GwV/Vn17ysJQ==} engines: {node: '>=18.x'} '@bcoe/v8-coverage@0.2.3': @@ -11719,7 +11719,7 @@ snapshots: '@babel/helper-validator-identifier': 7.25.7 to-fast-properties: 2.0.0 - '@balancer/sdk@0.33.1(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8)': + '@balancer/sdk@0.33.2(bufferutil@4.0.8)(typescript@5.4.5)(utf-8-validate@5.0.10)(zod@3.23.8)': dependencies: decimal.js-light: 2.5.1 lodash.clonedeep: 4.5.0 From b48aa41d43a59ab916f0b584cf86ebeadede3b59 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Mon, 2 Dec 2024 12:03:01 +0100 Subject: [PATCH 28/30] chore: add unit test for boosted state --- ...LiquidityActionHelpers.integration.spec.ts | 82 +++++++++++++++++++ .../pool/actions/LiquidityActionHelpers.ts | 2 +- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts b/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts index 31162907e..ed14f9c2c 100644 --- a/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts +++ b/packages/lib/modules/pool/actions/LiquidityActionHelpers.integration.spec.ts @@ -295,6 +295,88 @@ describe.skip('Liquidity helpers for V3 NESTED pool', async () => { }) }) +// Unskip when sepolia V3 pools are available in production api +test.skip('Nested pool state for V3 BOOSTED POOL', async () => { + // const poolId = '0xbb83ba331c3254c8c44645430126797dceda89c0' // Sepolia Balancer 50 WETH 50 stataUSDC + + // const v3Pool = await getPoolMock(poolId, GqlChain.Sepolia) + const v3Pool = {} as Pool + + const helpers = new LiquidityActionHelpers(v3Pool) + + const state = helpers.boostedPoolState + + expect(state).toMatchInlineSnapshot(` + { + "address": "0xbb83ba331c3254c8c44645430126797dceda89c0", + "id": "0xbb83ba331c3254c8c44645430126797dceda89c0", + "protocolVersion": 3, + "tokens": [ + { + "address": "0x7b79995e5f793a07bc00c21412e50ecae098e7f9", + "balance": "10.030375954528889", + "balanceUSD": "35947.96468719563", + "decimals": 18, + "erc4626ReviewData": null, + "hasNestedPool": false, + "id": "0xbb83ba331c3254c8c44645430126797dceda89c0-0x7b79995e5f793a07bc00c21412e50ecae098e7f9", + "index": 0, + "isAllowed": true, + "isErc4626": false, + "name": "Wrapped Ether", + "nestedPool": null, + "priceRate": "1", + "priceRateProvider": "0x0000000000000000000000000000000000000000", + "priceRateProviderData": null, + "symbol": "WETH", + "underlyingToken": null, + "weight": "0.5", + }, + { + "address": "0x8a88124522dbbf1e56352ba3de1d9f78c143751e", + "balance": "25922.046716", + "balanceUSD": "30847.23559204", + "decimals": 6, + "erc4626ReviewData": null, + "hasNestedPool": false, + "id": "0xbb83ba331c3254c8c44645430126797dceda89c0-0x8a88124522dbbf1e56352ba3de1d9f78c143751e", + "index": 1, + "isAllowed": true, + "isErc4626": true, + "name": "Static Aave Ethereum USDC", + "nestedPool": null, + "priceRate": "1.188770181245210492", + "priceRateProvider": "0x34101091673238545de8a846621823d9993c3085", + "priceRateProviderData": { + "address": "0x34101091673238545de8a846621823d9993c3085", + "factory": null, + "name": "waUSDC Rate Provider", + "reviewFile": "./StatATokenTestnetRateProvider.md", + "reviewed": true, + "summary": "safe", + "upgradeableComponents": [], + "warnings": [ + "", + ], + }, + "symbol": "stataEthUSDC", + "underlyingToken": { + "address": "0x94a9d9ac8a22534e3faca9f4e7f2e2cf85d5e4c8", + "chainId": 11155111, + "decimals": 6, + "index": 1, + "name": "USDC (AAVE Faucet)", + "symbol": "usdc-aave", + }, + "weight": "0.5", + }, + ], + "totalShares": "555.900851855167757901", + "type": "Weighted", + } + `) +}) + // Unskip when sepolia V3 pools are available in production api test.skip('Nested pool state for V3 NESTED POOL', async () => { // const poolId = '0xc9233cc69435591b193b50f702ac31e404a08b10' // Sepolia Balancer 50 WETH 50 USD diff --git a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts index bb99100bb..629d622ab 100644 --- a/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts +++ b/packages/lib/modules/pool/actions/LiquidityActionHelpers.ts @@ -95,7 +95,7 @@ export class LiquidityActionHelpers { decimals: token.underlyingToken?.decimals as number, index: token.index, //TODO: review that this index is always the expected one } - : null, // TODO: unit test this case with http://localhost:3000/pools/sepolia/v3/0xbb83ba331c3254c8c44645430126797dceda89c0/add-liquidity + : null, }) ) const state: PoolStateWithUnderlyings & { totalShares: HumanAmount } = { From 8b75d71b389a4f75bf772545235c63b768e8d9d2 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Mon, 2 Dec 2024 12:32:54 +0100 Subject: [PATCH 29/30] chore: change boosted message --- packages/lib/modules/protocols/useProtocols.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/modules/protocols/useProtocols.ts b/packages/lib/modules/protocols/useProtocols.ts index 26af1ab49..86e46ca5e 100644 --- a/packages/lib/modules/protocols/useProtocols.ts +++ b/packages/lib/modules/protocols/useProtocols.ts @@ -50,5 +50,5 @@ export type boostedProtocols = Protocol.Aave export const protocolDescriptions: Record = { [Protocol.Aave]: - "The magic of this Boosted pool lies in its composition—under the hood, it contains wrapped Aave tokens that earn interest from Aave borrowers, continuously growing the pool's worth.", + "This Boosted pool's underlying mechanics are powered by wrapped Aave tokens, which generate yield from lending activities on the Aave protocol, resulting in continuous appreciation of the pool's total value", } From c918b958c87dcf57afe2543523ee63008d779c14 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Mon, 2 Dec 2024 12:39:55 +0100 Subject: [PATCH 30/30] chore: update MIN_LIQUIDITY_FOR_BALANCED_ADD --- .../pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx index a8d121208..d1ce0c983 100644 --- a/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx +++ b/packages/lib/modules/pool/actions/add-liquidity/form/AddLiquidityFormTabs.tsx @@ -11,7 +11,7 @@ import { } from '../../LiquidityActionHelpers' import { useAddLiquidity } from '../AddLiquidityProvider' import { TokenInputsMaybeProportional } from './TokenInputsMaybeProportional' -const MIN_LIQUIDITY_FOR_BALANCED_ADD = 10000 +const MIN_LIQUIDITY_FOR_BALANCED_ADD = 50000 export function AddLiquidityFormTabs({ tokenSelectDisclosure,