Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: permit2 preparation #1100

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions lib/config/config.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface ContractsConfig {
feeDistributor?: Address
veDelegationProxy?: Address
veBAL?: Address
permit2?: Address
}
export interface PoolsConfig {
issues: Partial<Record<PoolIssue, string[]>>
Expand Down
3 changes: 2 additions & 1 deletion lib/config/networks/sepolia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ const networkConfig: NetworkConfig = {
multicall2: '0xca11bde05977b3631167028862be2a173976ca11',
balancer: {
vaultV2: '0xBA12222222228d8Ba445958a75a0704d566BF2C8',
vaultV3: '0xD5584b37D1845fFeD958C2d94bC675603DdCce68',
vaultV3: '0x0EF1c156a7986F394d90eD1bEeA6483Cc435F542',
relayerV6: '0x7852fB9d0895e6e8b3EedA553c03F6e2F9124dF9',
minter: '0x1783Cd84b3d01854A96B4eD5843753C2CcbD574A',
},
veBAL: '0x150A72e4D4d81BbF045565E232c50Ed0931ad795',
permit2: '0x000000000022D473030F116dDEE9F6B43aC78BA3',
},
pools: convertHexToLowerCase({
issues: {},
Expand Down
2 changes: 2 additions & 0 deletions lib/debug-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const vaultV3Address = sepoliaNetworkConfig.contracts.balancer.vaultV3 as

export const poolId = '0x68e3266c9c8bbd44ad9dca5afbfe629022aee9fe000200000000000000000512' as const // Balancer Weighted wjAura and WETH

export const sepoliaRouter = '0x1c58cc548a23956469c7C528Bb3a846c842dfaF9'

/*
Used to pretty print objects when debugging
*/
Expand Down
15 changes: 13 additions & 2 deletions lib/modules/pool/actions/LiquidityActionHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ export class LiquidityActionHelpers {
}

public getAmountsToApprove(
humanAmountsIn: HumanTokenAmountWithAddress[]
humanAmountsIn: HumanTokenAmountWithAddress[],
isPermit2 = false
): TokenAmountToApprove[] {
return this.toInputAmounts(humanAmountsIn).map(({ address, rawAmount }) => {
return {
isPermit2,
tokenAddress: address,
requiredRawAmount: rawAmount,
requestedRawAmount: rawAmount, //This amount will be probably replaced by MAX_BIGINT depending on the approval rules
Expand Down Expand Up @@ -232,7 +234,7 @@ export function toPoolStateWithBalances(pool: Pool): PoolStateWithBalances {
* - is native and the wrapped native token is already in the array and
* - is wrapped native and the native token is already in the array
*
* @param {HumanAmoHumanTokenAmountWithAddressuntIn[]} humanAmountsIn - The array of human amounts to filter.
* @param {HumanTokenAmountWithAddress[]} humanAmountsIn - The array of human amounts to filter.
* @param {Address} tokenAddress - The token address to compare against.
* @param {GqlChain} chain - The chain type for comparison.
* @return {HumanTokenAmountWithAddress[]} The filtered array of human amounts.
Expand Down Expand Up @@ -309,3 +311,12 @@ export function injectNativeAsset(
export function hasNoLiquidity(pool: Pool): boolean {
return isZero(pool.dynamicData.totalShares)
}

// When the pool has version < v3, it adds extra buildCall params (sender and recipient) that must be present only in V1/V2
export function formatBuildCallParams<T>(buildCallParams: T, isV3Pool: boolean, account: Address) {
// sender must be undefined for v3 pools
if (isV3Pool) return buildCallParams

// sender and recipient must be defined only for v1 and v2 pools
return { ...buildCallParams, sender: account, recipient: account }
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ import {
Slippage,
} from '@balancer/sdk'
import { Pool } from '../../../PoolProvider'
import { LiquidityActionHelpers, areEmptyAmounts } from '../../LiquidityActionHelpers'
import { SdkBuildAddLiquidityInput, SdkQueryAddLiquidityOutput } from '../add-liquidity.types'
import {
LiquidityActionHelpers,
formatBuildCallParams,
areEmptyAmounts,
} from '../../LiquidityActionHelpers'
import { AddLiquidityHandler } from './AddLiquidity.handler'
import { SdkBuildAddLiquidityInput, SdkQueryAddLiquidityOutput } from '../add-liquidity.types'

/**
* UnbalancedAddLiquidityHandler is a handler that implements the
Expand Down Expand Up @@ -64,13 +68,19 @@ export class UnbalancedAddLiquidityHandler implements AddLiquidityHandler {
}: SdkBuildAddLiquidityInput): Promise<TransactionConfig> {
const addLiquidity = new AddLiquidity()

const { callData, to, value } = addLiquidity.buildCall({
const baseBuildCallParams = {
...queryOutput.sdkQueryOutput,
slippage: Slippage.fromPercentage(`${Number(slippagePercent)}`),
sender: account,
recipient: account,
wethIsEth: this.helpers.isNativeAssetIn(humanAmountsIn),
})
}

const buildCallParams = formatBuildCallParams(
baseBuildCallParams,
this.helpers.isV3Pool(),
account
)

const { callData, to, value } = addLiquidity.buildCall(buildCallParams)

return {
account,
Expand Down
10 changes: 5 additions & 5 deletions lib/modules/pool/actions/add-liquidity/useAddLiquiditySteps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ import { useShouldSignRelayerApproval } from '@/lib/modules/relayer/signRelayerA
import { useApproveRelayerStep } from '@/lib/modules/relayer/useApproveRelayerStep'
import { useRelayerMode } from '@/lib/modules/relayer/useRelayerMode'
import { useTokenApprovalSteps } from '@/lib/modules/tokens/approvals/useTokenApprovalSteps'
import { useContractAddress } from '@/lib/modules/web3/contracts/useContractAddress'
import { useMemo } from 'react'
import { usePool } from '../../PoolProvider'
import { LiquidityActionHelpers } from '../LiquidityActionHelpers'
import { AddLiquidityStepParams, useAddLiquidityStep } from './useAddLiquidityStep'
import { isV3Pool } from '../../pool.helpers'
import { useSignRelayerStep } from '@/lib/modules/transactions/transaction-steps/useSignRelayerStep'
import { Address } from 'viem'
import { isCowAmmPool } from '../../pool.helpers'
import { getTokenSpenderAddress } from '@/lib/modules/tokens/token.helpers'

type AddLiquidityStepsParams = AddLiquidityStepParams & {
helpers: LiquidityActionHelpers
Expand All @@ -21,10 +20,10 @@ export function useAddLiquiditySteps({
humanAmountsIn,
simulationQuery,
}: AddLiquidityStepsParams) {
const vaultAddress = useContractAddress('balancer.vaultV2')
const { pool, chainId, chain } = usePool()
const relayerMode = useRelayerMode(pool)
const shouldSignRelayerApproval = useShouldSignRelayerApproval(chainId, relayerMode)

const { step: approveRelayerStep, isLoading: isLoadingRelayerApproval } =
useApproveRelayerStep(chainId)
const signRelayerStep = useSignRelayerStep(chain)
Expand All @@ -36,10 +35,11 @@ export function useAddLiquiditySteps({

const { isLoading: isLoadingTokenApprovalSteps, steps: tokenApprovalSteps } =
useTokenApprovalSteps({
spenderAddress: isCowAmmPool(pool.type) ? (pool.address as Address) : vaultAddress,
spenderAddress: getTokenSpenderAddress(pool),
chain: pool.chain,
approvalAmounts: inputAmounts,
actionType: 'AddLiquidity',
isPermit2: isV3Pool(pool),
})

const addLiquidityStep = useAddLiquidityStep({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import { Address, parseEther } from 'viem'
import { BPT_DECIMALS } from '../../../pool.constants'
import { Pool } from '../../../PoolProvider'
import { LiquidityActionHelpers } from '../../LiquidityActionHelpers'
import { LiquidityActionHelpers, formatBuildCallParams } from '../../LiquidityActionHelpers'
import {
QueryRemoveLiquidityInput,
SdkBuildRemoveLiquidityInput,
Expand Down Expand Up @@ -50,13 +50,19 @@ export class ProportionalRemoveLiquidityHandler implements RemoveLiquidityHandle
}: SdkBuildRemoveLiquidityInput): Promise<TransactionConfig> {
const removeLiquidity = new RemoveLiquidity()

const { callData, to, value } = removeLiquidity.buildCall({
const baseBuildCallParams = {
...queryOutput.sdkQueryOutput,
slippage: Slippage.fromPercentage(`${Number(slippagePercent)}`),
sender: account,
recipient: account,
wethIsEth,
})
}

const buildCallParams = formatBuildCallParams(
baseBuildCallParams,
this.helpers.isV3Pool(),
account
)

const { callData, to, value } = removeLiquidity.buildCall(buildCallParams)

return {
account,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
import { Address, parseEther } from 'viem'
import { BPT_DECIMALS } from '../../../pool.constants'
import { Pool } from '../../../PoolProvider'
import { LiquidityActionHelpers, isEmptyHumanAmount } from '../../LiquidityActionHelpers'
import {
LiquidityActionHelpers,
formatBuildCallParams,
isEmptyHumanAmount,
} from '../../LiquidityActionHelpers'
import {
SdkBuildRemoveLiquidityInput,
SdkQueryRemoveLiquidityOutput,
Expand Down Expand Up @@ -72,13 +76,19 @@ export class SingleTokenRemoveLiquidityHandler implements RemoveLiquidityHandler
}: SdkBuildRemoveLiquidityInput): Promise<TransactionConfig> {
const removeLiquidity = new RemoveLiquidity()

const { callData, to, value } = removeLiquidity.buildCall({
const baseBuildCallParams = {
...queryOutput.sdkQueryOutput,
slippage: Slippage.fromPercentage(`${Number(slippagePercent)}`),
sender: account,
recipient: account,
wethIsEth,
})
}

const buildCallParams = formatBuildCallParams(
baseBuildCallParams,
this.helpers.isV3Pool(),
account
)

const { callData, to, value } = removeLiquidity.buildCall(buildCallParams)

return {
account,
Expand Down
5 changes: 5 additions & 0 deletions lib/modules/tokens/approvals/approval-rules.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ describe('getRequiredTokenApprovals', () => {
})
).toEqual([
{
isPermit2: false,
tokenAddress: wETHAddress,
requiredRawAmount: 10000000000000000000n,
requestedRawAmount: MAX_BIGINT,
},
{
isPermit2: false,
tokenAddress: wjAuraAddress,
requiredRawAmount: 20000000000000000000n,
requestedRawAmount: MAX_BIGINT,
Expand Down Expand Up @@ -87,11 +89,13 @@ describe('getRequiredTokenApprovals', () => {
requiredRawAmount: 0n,
requestedRawAmount: 0n,
tokenAddress: usdtAddress,
isPermit2: false,
},
{
tokenAddress: usdtAddress,
requiredRawAmount: 10000000000000000000n,
requestedRawAmount: MAX_BIGINT,
isPermit2: false,
},
])
})
Expand All @@ -116,6 +120,7 @@ describe('getRequiredTokenApprovals', () => {
})
).toEqual([
{
isPermit2: false,
requiredRawAmount: 10000000000000000000n,
requestedRawAmount: MAX_BIGINT,
tokenAddress: usdtAddress,
Expand Down
5 changes: 5 additions & 0 deletions lib/modules/tokens/approvals/approval-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type TokenAmountToApprove = {
tokenAddress: Address
requiredRawAmount: bigint // actual amount that the transaction requires
requestedRawAmount: bigint // amount that we are going to request (normally MAX_BIGINT)
isPermit2: boolean // whether the approval is for Permit2 or standard token approval
}

// This is a subtype of InputAmount as we only need rawAmount and address
Expand All @@ -19,6 +20,7 @@ type TokenApprovalParams = {
chainId: GqlChain | SupportedChainId | null
rawAmounts: RawAmount[]
allowanceFor: (tokenAddress: Address) => bigint
isPermit2?: boolean
approveMaxBigInt?: boolean
skipAllowanceCheck?: boolean
}
Expand All @@ -30,6 +32,7 @@ export function getRequiredTokenApprovals({
chainId,
rawAmounts,
allowanceFor,
isPermit2 = false,
approveMaxBigInt = true,
skipAllowanceCheck = false,
}: TokenApprovalParams): TokenAmountToApprove[] {
Expand All @@ -42,6 +45,7 @@ export function getRequiredTokenApprovals({
requiredRawAmount: rawAmount,
// The transaction only requires requiredRawAmount but we will normally request MAX_BIGINT
requestedRawAmount: approveMaxBigInt ? MAX_BIGINT : rawAmount,
isPermit2,
}
})

Expand All @@ -59,6 +63,7 @@ export function getRequiredTokenApprovals({
requiredRawAmount: 0n,
requestedRawAmount: 0n,
tokenAddress: t.tokenAddress,
isPermit2,
}
// Prepend approval for ZERO amount
return [zeroTokenAmountToApprove, t]
Expand Down
5 changes: 4 additions & 1 deletion lib/modules/tokens/approvals/useTokenApprovalSteps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ export type Params = {
chain: GqlChain
approvalAmounts: RawAmount[]
actionType: ApprovalAction
isPermit2?: boolean
bptSymbol?: string //Edge-case for approving
}

/*
Generic hook to creates a Token Approval Step Config for different flows defined by the actionType property
Generic hook to create a Token Approval Step Config for different flows defined by the actionType property
*/
export function useTokenApprovalSteps({
spenderAddress,
chain,
approvalAmounts,
actionType,
bptSymbol,
isPermit2 = false,
}: Params): { isLoading: boolean; steps: TransactionStep[] } {
const { userAddress } = useUserAccount()
const { getToken } = useTokens()
Expand All @@ -58,6 +60,7 @@ export function useTokenApprovalSteps({
chainId: chain,
rawAmounts: _approvalAmounts,
allowanceFor: tokenAllowances.allowanceFor,
isPermit2,
})

const steps = useMemo(() => {
Expand Down
14 changes: 14 additions & 0 deletions lib/modules/tokens/token.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Address } from 'viem'
import { HumanTokenAmountWithAddress, TokenBase } from './token.types'
import { InputAmount } from '@balancer/sdk'
import { Pool } from '../pool/PoolProvider'
import { getVaultConfig, isCowAmmPool, isV3Pool } from '../pool/pool.helpers'

export function isNativeAsset(token: TokenBase | string, chain: GqlChain | SupportedChainId) {
return nativeAssetFilter(chain)(token)
Expand Down Expand Up @@ -130,3 +131,16 @@ export function getLeafTokens(poolTokens: PoolToken[]) {

return leafTokens
}

export function getTokenSpenderAddress(pool: Pool): Address {
if (isCowAmmPool(pool.type)) return pool.address as Address
if (isV3Pool(pool)) {
const permit2Address = getNetworkConfig(pool.chain).contracts.permit2
if (!permit2Address) {
throw new Error(`Permit2 feature is not yet available for this chain (${pool.chain}) `)
}
return permit2Address
}
const { vaultAddress } = getVaultConfig(pool)
return vaultAddress
}
Loading