Skip to content

Commit

Permalink
fix: Use poolTokens over allTokens (balancer#1008)
Browse files Browse the repository at this point in the history
* fix: Use poolTokens over allTokens

* chore: Fix remove liquidity

* refactor: Nested action conditions

* fix: use leaf pool tokens for nested add liquidity (balancer#1056)

Co-authored-by: Alberto Gualis <[email protected]>

* fix: integration tests for nested pool

* chore: Cleanup token set in providers

---------

Co-authored-by: Alberto Gualis <[email protected]>
  • Loading branch information
garethfuller and agualis authored Sep 9, 2024
1 parent b6c4af5 commit 1e24317
Show file tree
Hide file tree
Showing 13 changed files with 74 additions and 43 deletions.
1 change: 1 addition & 0 deletions lib/config/config.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface ContractsConfig {
}
export interface PoolsConfig {
issues: Partial<Record<PoolIssue, string[]>>
allowNestedActions?: string[] // pool ids
}

export interface BlockExplorerConfig {
Expand Down
1 change: 1 addition & 0 deletions lib/config/networks/gnosis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const networkConfig: NetworkConfig = {
},
pools: convertHexToLowerCase({
issues: { [PoolIssue.CspPoolVulnWarning]: CSP_ISSUE_POOL_IDS[GqlChain.Gnosis] },
allowNestedActions: ['0x66888e4f35063ad8bb11506a6fde5024fb4f1db0000100000000000000000053'],
}),
}

Expand Down
3 changes: 3 additions & 0 deletions lib/config/networks/mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ const networkConfig = {
'0xad0e5e0778cac28f1ff459602b31351871b5754a0002000000000000000003cD',
],
},
allowNestedActions: [
'0x08775ccb6674d6bdceb0797c364c2653ed84f3840002000000000000000004f0', // B-80BAL-20WETH
],
}),
} as const satisfies NetworkConfig

Expand Down
6 changes: 3 additions & 3 deletions lib/modules/pool/actions/LiquidityActionHelpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { aWjAuraWethPoolElementMock } from '@/test/msw/builders/gqlPoolElement.b
import {
LiquidityActionHelpers,
areEmptyAmounts,
shouldUseNestedLiquidity,
shouldUseRecoveryRemoveLiquidity,
roundDecimals,
supportsNestedActions,
} from './LiquidityActionHelpers'
import { nestedPoolMock } from '../__mocks__/nestedPoolMock'
import {
Expand Down Expand Up @@ -39,8 +39,8 @@ describe('areEmptyAmounts', () => {
})

test('detects pools requiring nested liquidity', () => {
expect(shouldUseNestedLiquidity(aWjAuraWethPoolElementMock())).toBeFalsy()
expect(shouldUseNestedLiquidity(nestedPoolMock)).toBeTruthy()
expect(supportsNestedActions(aWjAuraWethPoolElementMock())).toBeFalsy()
expect(supportsNestedActions(nestedPoolMock)).toBeTruthy()
})

describe('detects pools requiring recovery removal', () => {
Expand Down
17 changes: 4 additions & 13 deletions lib/modules/pool/actions/LiquidityActionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ import {
Token,
} from '@balancer/sdk'
import { Hex, formatUnits, parseUnits, Address } from 'viem'

import {
hasNestedPools,
isAffectedByCspIssue,
isComposableStableV1,
isCowAmmPool,
Expand Down Expand Up @@ -173,17 +171,10 @@ It looks that you tried to call useBuildCallData before the last query finished
return queryResponse
}

export function supportsNestedLiquidity(pool: Pool) {
return pool.type === GqlPoolType.ComposableStable || pool.type === GqlPoolType.Weighted
}

export function shouldUseNestedLiquidity(pool: Pool) {
return supportsNestedLiquidity(pool) && hasNestedPools(pool)
}

export function supportsProportionalAdds(pool: Pool) {
// Nested pools do not support proportional adds (addable tokens feature)
return !shouldUseNestedLiquidity(pool)
export function supportsNestedActions(pool: Pool): boolean {
const allowNestedActions = getNetworkConfig(pool.chain).pools?.allowNestedActions ?? []
if (allowNestedActions.includes(pool.id)) return true
return false
}

export function shouldUseRecoveryRemoveLiquidity(pool: Pool): boolean {
Expand Down
13 changes: 8 additions & 5 deletions lib/modules/pool/actions/add-liquidity/AddLiquidityProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ import {
injectNativeAsset,
replaceWrappedWithNativeAsset,
requiresProportionalInput,
supportsNestedActions,
} from '../LiquidityActionHelpers'
import { isDisabledWithReason } from '@/lib/shared/utils/functions/isDisabledWithReason'
import { useUserAccount } from '@/lib/modules/web3/UserAccountProvider'
import { LABELS } from '@/lib/shared/labels'
import { selectAddLiquidityHandler } from './handlers/selectAddLiquidityHandler'
import { useTokenInputsValidation } from '@/lib/modules/tokens/TokenInputsValidationProvider'
import { isGyro, isNonComposableStable } from '../../pool.helpers'
import { useAddLiquiditySteps } from './useAddLiquiditySteps'
import { useTransactionSteps } from '@/lib/modules/transactions/transaction-steps/useTransactionSteps'
import { useTotalUsdValue } from '@/lib/modules/tokens/useTotalUsdValue'
import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types'
import { isUnhandledAddPriceImpactError } from '@/lib/modules/price-impact/price-impact.utils'
import { useModalWithPoolRedirect } from '../../useModalWithPoolRedirect'
import { getLeafTokens } from '@/lib/modules/tokens/token.helpers'

export type UseAddLiquidityResponse = ReturnType<typeof _useAddLiquidity>
export const AddLiquidityContext = createContext<UseAddLiquidityResponse | null>(null)
Expand Down Expand Up @@ -59,7 +60,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
const wNativeAsset = getWrappedNativeAssetToken(chain)

function setInitialHumanAmountsIn() {
const amountsIn = pool.allTokens.map(
const amountsIn = getPoolTokens().map(
token =>
({
tokenAddress: token.address,
Expand All @@ -81,9 +82,11 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
}

function getPoolTokens() {
if (isNonComposableStable(pool.type)) return pool.poolTokens
if (isGyro(pool.type)) return pool.allTokens
return pool.allTokens.filter(token => token.isMainToken)
if (supportsNestedActions(pool)) {
return getLeafTokens(pool.poolTokens)
}

return pool.poolTokens
}

const tokens = getPoolTokens()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { usePool } from '../../../PoolProvider'
import {
hasNoLiquidity,
requiresProportionalInput,
supportsProportionalAdds,
supportsNestedActions,
} from '../../LiquidityActionHelpers'
import { PriceImpactAccordion } from '@/lib/modules/price-impact/PriceImpactAccordion'
import { PoolActionsPriceImpactDetails } from '../../PoolActionsPriceImpactDetails'
Expand All @@ -47,7 +47,6 @@ import { cannotCalculatePriceImpactError } from '@/lib/modules/price-impact/pric
import { useUserAccount } from '@/lib/modules/web3/UserAccountProvider'
import { ConnectWallet } from '@/lib/modules/web3/ConnectWallet'
import { BalAlert } from '@/lib/shared/components/alerts/BalAlert'
import { SafeAppAlert } from '@/lib/shared/components/alerts/SafeAppAlert'

// small wrapper to prevent out of context error
export function AddLiquidityForm() {
Expand Down Expand Up @@ -97,6 +96,7 @@ function AddLiquidityMainForm() {

const weeklyYield = calcPotentialYieldFor(pool, totalUSDValue)

const nestedAddLiquidityEnabled = supportsNestedActions(pool) // TODO && !userToggledEscapeHatch
const isLoading = simulationQuery.isLoading || priceImpactQuery.isLoading
const isFetching = simulationQuery.isFetching || priceImpactQuery.isFetching

Expand Down Expand Up @@ -160,8 +160,7 @@ function AddLiquidityMainForm() {
{hasNoLiquidity(pool) && (
<BalAlert status="warning" content="You cannot add because the pool has no liquidity" />
)}
<SafeAppAlert />
{supportsProportionalAdds(pool) ? (
{!nestedAddLiquidityEnabled ? (
<TokenInputsWithAddable
tokenSelectDisclosureOpen={() => tokenSelectDisclosure.onOpen()}
requiresProportionalInput={requiresProportionalInput(pool.type)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@
import networkConfig from '@/lib/config/networks/mainnet'
import { daiAddress, usdcAddress, usdtAddress, wETHAddress } from '@/lib/debug-helpers'
import { mainnetTestPublicClient } from '@/test/utils/wagmi/wagmi-test-clients'
import { nestedPoolMock } from '../../../__mocks__/nestedPoolMock'
import { Pool } from '../../../PoolProvider'
import { NestedAddLiquidityHandler } from './NestedAddLiquidity.handler'
import { selectAddLiquidityHandler } from './selectAddLiquidityHandler'
import { defaultTestUserAccount } from '@/test/anvil/anvil-setup'
import { HumanTokenAmountWithAddress } from '@/lib/modules/tokens/token.types'
import { getPoolMock } from '../../../__mocks__/getPoolMock'
import { GqlChain } from '@/lib/shared/services/api/generated/graphql'

function selectNestedHandler(pool: Pool) {
return selectAddLiquidityHandler(pool) as NestedAddLiquidityHandler
}

// Balancer 50WETH-50-3pool
const poolId = '0x08775ccb6674d6bdceb0797c364c2653ed84f3840002000000000000000004f0'
const nestedPool = await getPoolMock(poolId, GqlChain.Mainnet)

describe('When adding nested liquidity for a weighted pool', () => {
test('has zero price impact', async () => {
const handler = selectNestedHandler(nestedPoolMock)
const handler = selectNestedHandler(nestedPool)

const humanAmountsIn: HumanTokenAmountWithAddress[] = [
{ humanAmount: '100', tokenAddress: daiAddress },
Expand All @@ -25,7 +30,7 @@ describe('When adding nested liquidity for a weighted pool', () => {
})

test('with single token input', async () => {
const handler = selectNestedHandler(nestedPoolMock)
const handler = selectNestedHandler(nestedPool)

const humanAmountsIn: HumanTokenAmountWithAddress[] = [
{ humanAmount: '1', tokenAddress: daiAddress },
Expand All @@ -37,7 +42,7 @@ describe('When adding nested liquidity for a weighted pool', () => {
})

test('with multiple token input', async () => {
const handler = selectNestedHandler(nestedPoolMock)
const handler = selectNestedHandler(nestedPool)

const humanAmountsIn: HumanTokenAmountWithAddress[] = [
{ humanAmount: '1', tokenAddress: wETHAddress },
Expand All @@ -56,7 +61,7 @@ describe('When adding nested liquidity for a weighted pool', () => {
{ humanAmount: '1', tokenAddress: daiAddress },
]

const handler = selectNestedHandler(nestedPoolMock)
const handler = selectNestedHandler(nestedPool)

// Store query response in handler instance
const queryOutput = await handler.simulate(humanAmountsIn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TwammAddLiquidityHandler } from './TwammAddLiquidity.handler'
import { UnbalancedAddLiquidityHandler } from './UnbalancedAddLiquidity.handler'
import { AddLiquidityHandler } from './AddLiquidity.handler'
import { NestedAddLiquidityHandler } from './NestedAddLiquidity.handler'
import { requiresProportionalInput, shouldUseNestedLiquidity } from '../../LiquidityActionHelpers'
import { requiresProportionalInput, supportsNestedActions } from '../../LiquidityActionHelpers'
import { ProportionalAddLiquidityHandler } from './ProportionalAddLiquidity.handler'

export function selectAddLiquidityHandler(pool: Pool): AddLiquidityHandler {
Expand All @@ -15,7 +15,10 @@ export function selectAddLiquidityHandler(pool: Pool): AddLiquidityHandler {
return new ProportionalAddLiquidityHandler(pool)
}

if (shouldUseNestedLiquidity(pool)) {
// TODO add && not toggled escape hatch to high level tokens
// 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)) {
return new NestedAddLiquidityHandler(pool)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import { selectRemoveLiquidityHandler } from './handlers/selectRemoveLiquidityHa
import { useRemoveLiquidityPriceImpactQuery } from './queries/useRemoveLiquidityPriceImpactQuery'
import { RemoveLiquidityType } from './remove-liquidity.types'
import { Address, Hash } from 'viem'
import { emptyTokenAmounts, toHumanAmount } from '../LiquidityActionHelpers'
import { emptyTokenAmounts, supportsNestedActions, toHumanAmount } from '../LiquidityActionHelpers'
import { useDisclosure } from '@chakra-ui/hooks'
import { isCowAmmPool, isGyro, isNonComposableStable } from '../../pool.helpers'
import { isWrappedNativeAsset } from '@/lib/modules/tokens/token.helpers'
import { isCowAmmPool } from '../../pool.helpers'
import { getLeafTokens, isWrappedNativeAsset } from '@/lib/modules/tokens/token.helpers'
import { useRemoveLiquiditySimulationQuery } from './queries/useRemoveLiquiditySimulationQuery'
import { useRemoveLiquiditySteps } from './useRemoveLiquiditySteps'
import { useTransactionSteps } from '@/lib/modules/transactions/transaction-steps/useTransactionSteps'
Expand Down Expand Up @@ -74,9 +74,11 @@ export function _useRemoveLiquidity(urlTxHash?: Hash) {
const isProportional = removalType === RemoveLiquidityType.Proportional

function getPoolTokens() {
if (isNonComposableStable(pool.type)) return pool.poolTokens
if (isGyro(pool.type)) return pool.allTokens
return pool.allTokens.filter(token => token.isMainToken)
// TODO add exception for composable pools where we can allow adding
// liquidity with nested tokens
if (supportsNestedActions(pool)) return getLeafTokens(pool.poolTokens)

return pool.poolTokens
}

const tokens = getPoolTokens().map(token => getToken(token.address, pool.chain))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Pool } from '../../../PoolProvider'
import {
shouldUseNestedLiquidity,
shouldUseRecoveryRemoveLiquidity,
supportsNestedActions,
} from '../../LiquidityActionHelpers'
import { RemoveLiquidityType } from '../remove-liquidity.types'
import { NestedProportionalRemoveLiquidityHandler } from './NestedProportionalRemoveLiquidity.handler'
Expand All @@ -26,10 +26,10 @@ export function selectRemoveLiquidityHandler(
return new RecoveryRemoveLiquidityHandler(pool)
}

if (shouldUseNestedLiquidity(pool) && kind === RemoveLiquidityType.Proportional) {
if (supportsNestedActions(pool) && kind === RemoveLiquidityType.Proportional) {
return new NestedProportionalRemoveLiquidityHandler(pool)
}
if (shouldUseNestedLiquidity(pool) && kind === RemoveLiquidityType.SingleToken) {
if (supportsNestedActions(pool) && kind === RemoveLiquidityType.SingleToken) {
return new NestedSingleTokenRemoveLiquidityHandler(pool)
}

Expand Down
7 changes: 5 additions & 2 deletions lib/modules/relayer/useRelayerMode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { shouldUseNestedLiquidity } from '../pool/actions/LiquidityActionHelpers'
import { supportsNestedActions } from '../pool/actions/LiquidityActionHelpers'
import { Pool } from '../pool/PoolProvider'
import { useUserSettings } from '../user/settings/UserSettingsProvider'
import { useUserAccount } from '../web3/UserAccountProvider'
Expand All @@ -9,7 +9,10 @@ export function useRelayerMode(pool?: Pool): RelayerMode {
const { connector } = useUserAccount()
const { enableSignatures } = useUserSettings()

if (pool && !shouldUseNestedLiquidity(pool)) return 'no-relayer-needed'
// TODO requires an additional && to check if the user has toggled the escape
// hatch. The escape hatch allows the user to revert to adding liquidity in the
// first level pool tokens.
if (pool && !supportsNestedActions(pool)) return 'no-relayer-needed'

if (enableSignatures === 'no') return 'approveRelayer'
if (connector?.id === 'walletConnect') return 'approveRelayer'
Expand Down
20 changes: 20 additions & 0 deletions lib/modules/tokens/token.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { includesAddress, isSameAddress } from '@/lib/shared/utils/addresses'
import { Address } from 'viem'
import { HumanTokenAmountWithAddress, TokenBase } from './token.types'
import { InputAmount } from '@balancer/sdk'
import { Pool } from '../pool/PoolProvider'

export function isNativeAsset(token: TokenBase | string, chain: GqlChain | SupportedChainId) {
return nativeAssetFilter(chain)(token)
Expand Down Expand Up @@ -110,3 +111,22 @@ export function requiresDoubleApproval(
tokenAddress
)
}

type PoolToken = Pool['poolTokens'][0]
export function getLeafTokens(poolTokens: PoolToken[]) {
const leafTokens: PoolToken[] = []

poolTokens.forEach(poolToken => {
if (poolToken.nestedPool) {
const nestedTokens = poolToken.nestedPool.tokens.filter(
// Exclude the pool token itself
t => !isSameAddress(t.address, poolToken.address)
) as PoolToken[]
leafTokens.push(...nestedTokens)
} else {
leafTokens.push(poolToken)
}
})

return leafTokens
}

0 comments on commit 1e24317

Please sign in to comment.