Skip to content

Commit

Permalink
feat: add tabs for add liquidity (#214)
Browse files Browse the repository at this point in the history
* refactor: proportional tabs and handlers

* add tvl threshold

* fix: do not show unbalanced error in nested adds

* fix: nested with top level with underlyings

* fix: disable wethIsEth for v3 boosted

* fix: boostedPoolState when underlying token has undefined address

* fix: avoid using ETH instead of WETH when pool does not support wethIsEth

* fix: use token index instead of for index in boostedPoolState

* fix: use explicit referenceAmount by address in Proportional adds

* chore: improve ratio error messages

* add optional prop for full width

* add optional prop for larger text

* set larger text for buttons

* chore: refactor unbalanced error label logic

* chore: bump sdk to 0.33.2
---------

Co-authored-by: Alberto Gualis <[email protected]>
  • Loading branch information
groninge01 and agualis authored Dec 2, 2024
1 parent 40dafb2 commit fc6d187
Show file tree
Hide file tree
Showing 30 changed files with 849 additions and 470 deletions.
2 changes: 2 additions & 0 deletions apps/frontend-v3/app/api/rpc/[chain]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const chainToRpcMap: Record<GqlChain, string | undefined> = {
[GqlChain.Avalanche]: dRpcUrl('avalanche'),
[GqlChain.Fantom]: dRpcUrl('fantom'),
[GqlChain.Sepolia]: dRpcUrl('sepolia'),
// DEBUG:
// [GqlChain.Sepolia]: 'https://eth-sepolia.g.alchemy.com/v2/<KEY>',
[GqlChain.Fraxtal]: dRpcUrl('fraxtal'),
[GqlChain.Gnosis]: dRpcUrl('gnosis'),
[GqlChain.Mode]: dRpcUrl('mode'),
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/modules/pool/PoolDetail/PoolMyLiquidity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -293,3 +294,163 @@ 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

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,
})
})
20 changes: 11 additions & 9 deletions packages/lib/modules/pool/actions/LiquidityActionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
isUnbalancedLiquidityDisabled,
isV2Pool,
isV3Pool,
isV3WithNestedActionsPool,
isV3NotSupportingWethIsEth,
} from '../pool.helpers'
import { TokenAmountIn } from '../../tokens/approvals/permit2/useSignPermit2'

Expand Down Expand Up @@ -84,16 +84,18 @@ 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,
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: token.index, //TODO: review that this index is always the expected one
}
: null,
})
)
const state: PoolStateWithUnderlyings & { totalShares: HumanAmount } = {
Expand Down Expand Up @@ -359,7 +361,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)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'

Expand All @@ -37,6 +37,8 @@ export const AddLiquidityContext = createContext<UseAddLiquidityResponse | null>

export function _useAddLiquidity(urlTxHash?: Hash) {
const [humanAmountsIn, setHumanAmountsIn] = useState<HumanTokenAmountWithAddress[]>([])
// only used by Proportional handlers that require a referenceAmount
const [referenceAmountAddress, setReferenceAmountAddress] = useState<Address | undefined>()
const [needsToAcceptHighPI, setNeedsToAcceptHighPI] = useState(false)
const [acceptPoolRisks, setAcceptPoolRisks] = useState(false)
const [wethIsEth, setWethIsEth] = useState(false)
Expand Down Expand Up @@ -91,6 +93,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)
Expand All @@ -110,6 +125,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
handler,
humanAmountsIn,
enabled: !urlTxHash,
referenceAmountAddress,
})
const priceImpactQuery = useAddLiquidityPriceImpactQuery({
handler,
Expand Down Expand Up @@ -179,7 +195,7 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
return {
transactionSteps,
humanAmountsIn,
tokens: wethIsEth && !isV3WithNestedActionsPool(pool) ? tokensWithNativeAsset : tokens,
tokens: wethIsEth && !isV3NotSupportingWethIsEth(pool) ? tokensWithNativeAsset : tokens,
validTokens,
totalUSDValue,
simulationQuery,
Expand All @@ -202,11 +218,14 @@ export function _useAddLiquidity(urlTxHash?: Hash) {
proportionalSlippage,
isForcedProportionalAdd,
wantsProportional,
referenceAmountAddress,
setWantsProportional,
setProportionalSlippage,
refetchQuote,
setHumanAmountIn,
setHumanAmountsIn,
clearAmountsIn,
setReferenceAmountAddress,
setNeedsToAcceptHighPI,
setAcceptPoolRisks,
setWethIsEth,
Expand Down
Loading

0 comments on commit fc6d187

Please sign in to comment.