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: add single pool handler #94

Merged
merged 9 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import { PoolActionsLayout } from '@repo/lib/modules/pool/actions/PoolActionsLayout'
import { getPoolTokens } from '@repo/lib/modules/pool/pool.helpers'
import { usePoolRedirect } from '@repo/lib/modules/pool/pool.hooks'
import { ProtocolVersion } from '@repo/lib/modules/pool/pool.types'
import { chainToSlugMap } from '@repo/lib/modules/pool/pool.utils'
import { usePool } from '@repo/lib/modules/pool/PoolProvider'
import { SwapForm } from '@repo/lib/modules/swap/SwapForm'
import SwapLayout from '@repo/lib/modules/swap/SwapLayout'
import { PathParams, SwapProviderProps } from '@repo/lib/modules/swap/SwapProvider'
import { useTokens } from '@repo/lib/modules/tokens/TokensProvider'
import { Hash } from 'viem'
import { Hash, Hex } from 'viem'

type Props = {
params: { txHash?: string[] }
Expand All @@ -30,7 +31,12 @@ export default function PoolSwapPage({ params: { txHash } }: Props) {
tokenOut: poolTokens[1].address,
urlTxHash: maybeTxHash,
}
const props: SwapProviderProps = { pathParams, isPoolSwap: true, poolTokens }
const props: SwapProviderProps = {
pathParams,
poolTokens,
poolId: pool.id as Hex,
poolVersion: pool.protocolVersion as ProtocolVersion,
}

return (
<PoolActionsLayout>
Expand Down
8 changes: 1 addition & 7 deletions packages/eslint-config/next.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const project = resolve(process.cwd(), 'tsconfig.json')
module.exports = {
extends: [
'eslint:recommended',

'plugin:@typescript-eslint/recommended',
require.resolve('@vercel/style-guide/eslint/next'),
require.resolve('@vercel/style-guide/eslint/react'),
Expand Down Expand Up @@ -53,13 +54,6 @@ module.exports = {
group: ['wagmi/dist'],
message: 'Invalid import: remove dist from import path',
},
{
group: ['@apollo/client'],
importNames: ['useQuery'],
message:
// eslint-disable-next-line max-len
'Import useQuery from @apollo/experimental-nextjs-app-support/ssr to avoid u.inFlightLinkObservables errors',
},
{
group: ['act'],
importNames: ['react-dom/test-utils'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '@repo/lib/shared/components/modals/PartnerRedirectModal'
import { useState } from 'react'
import { getXavePoolLink } from '../../pool.utils'
// import { PoolAdvancedOptions } from './PoolAdvancedOptions'
import { PoolAdvancedOptions } from './PoolAdvancedOptions'

export function PoolHeader() {
const pathname = usePathname()
Expand Down Expand Up @@ -67,10 +67,7 @@ export function PoolHeader() {
Add liquidity
</Button>

{/*
Will be enabled when pool swaps handler is implemented:
<PoolAdvancedOptions />
*/}
</HStack>
<PartnerRedirectModal
isOpen={partnerRedirectDisclosure.isOpen}
Expand Down
2 changes: 2 additions & 0 deletions packages/lib/modules/pool/pool.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export enum BaseVariant {
v3 = 'v3',
}

export type ProtocolVersion = 1 | 2 | 3

// these variants support extra features in project config
export enum PartnerVariant {
cow = 'cow',
Expand Down
12 changes: 6 additions & 6 deletions packages/lib/modules/swap/SwapDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ import { GqlSorSwapType } from '@repo/lib/shared/services/api/generated/graphql'
import { useUserSettings } from '../user/settings/UserSettingsProvider'
import { usePriceImpact } from '@repo/lib/modules/price-impact/PriceImpactProvider'
import { SdkSimulateSwapResponse } from './swap.types'
import { DefaultSwapHandler } from './handlers/DefaultSwap.handler'
import { useTokens } from '../tokens/TokensProvider'
import { NativeWrapHandler } from './handlers/NativeWrap.handler'
import { InfoIcon } from '@repo/lib/shared/components/icons/InfoIcon'
import pluralize from 'pluralize'
import { BaseDefaultSwapHandler } from './handlers/BaseDefaultSwap.handler'

export function OrderRoute() {
const { simulationQuery } = useSwap()

const queryData = simulationQuery.data as SdkSimulateSwapResponse
const orderRouteVersion = queryData ? queryData.protocolVersion : 2
const hopCount = queryData ? queryData.routes[0]?.hops?.length : 0
const hopCount = queryData ? queryData.hopCount : 0

return (
<HStack justify="space-between" w="full">
Expand Down Expand Up @@ -65,7 +65,7 @@ export function SwapDetails() {

const { priceImpactLevel, priceImpactColor, PriceImpactIcon, priceImpact } = usePriceImpact()

const isDefaultSwap = handler instanceof DefaultSwapHandler
const isDefaultSwap = handler instanceof BaseDefaultSwapHandler
const isNativeWrapOrUnwrap = handler instanceof NativeWrapHandler

const _slippage = isNativeWrapOrUnwrap ? 0 : slippage
Expand Down Expand Up @@ -94,10 +94,10 @@ export function SwapDetails() {
'from unfavortable market price movements before your transaction executes on-chain.'

const slippageLabel = isExactIn
? `This is the maximum slippage that the swap will allow.
? `This is the maximum slippage that the swap will allow.
It is based on the quoted amount out minus your slippage tolerance, using current market prices.
You can change your slippage tolerance in your settings.`
: `This is the maximum slippage that the swap will allow.
: `This is the maximum slippage that the swap will allow.
It is based on the quoted amount in plus your slippage tolerance, using current market prices.
You can change your slippage tolerance in your settings.`

Expand Down Expand Up @@ -187,7 +187,7 @@ export function SwapDetails() {
</HStack>
</HStack>

{isDefaultSwap && <OrderRoute />}
{isDefaultSwap ? <OrderRoute /> : null}
</VStack>
)
}
117 changes: 66 additions & 51 deletions packages/lib/modules/swap/SwapProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
'use client'
/* eslint-disable react-hooks/exhaustive-deps */

import { ApolloClient, useApolloClient, useReactiveVar } from '@apollo/client'
import { HumanAmount } from '@balancer/sdk'
import { useDisclosure } from '@chakra-ui/react'
import { getNetworkConfig } from '@repo/lib/config/app.config'
import { useNetworkConfig } from '@repo/lib/config/useNetworkConfig'
import { useMakeVarPersisted } from '@repo/lib/shared/hooks/useMakeVarPersisted'
import { useVault } from '@repo/lib/shared/hooks/useVault'
import { LABELS } from '@repo/lib/shared/labels'
import { GqlChain, GqlSorSwapType, GqlToken } from '@repo/lib/shared/services/api/generated/graphql'
import { isSameAddress, selectByAddress } from '@repo/lib/shared/utils/addresses'
import { useMandatoryContext } from '@repo/lib/shared/utils/contexts'
import { ApolloClient, useApolloClient, useReactiveVar } from '@apollo/client'
import { isDisabledWithReason } from '@repo/lib/shared/utils/functions/isDisabledWithReason'
import { bn } from '@repo/lib/shared/utils/numbers'
import { invert } from 'lodash'
import { PropsWithChildren, createContext, useEffect, useMemo, useState } from 'react'
import { Address, Hash, isAddress, parseUnits } from 'viem'
import { Address, Hash, Hex, isAddress, parseUnits } from 'viem'
import { ChainSlug, chainToSlugMap, slugToChainMap } from '../pool/pool.utils'
import { calcMarketPriceImpact } from '../price-impact/price-impact.utils'
import { usePriceImpact } from '../price-impact/PriceImpactProvider'
import { useTokenBalances } from '../tokens/TokenBalancesProvider'
import { useTokenInputsValidation } from '../tokens/TokenInputsValidationProvider'
import { useTokens } from '../tokens/TokensProvider'
import { useTransactionSteps } from '../transactions/transaction-steps/useTransactionSteps'
import { emptyAddress } from '../web3/contracts/wagmi-helpers'
import { useUserAccount } from '../web3/UserAccountProvider'
import { LABELS } from '@repo/lib/shared/labels'
import { isDisabledWithReason } from '@repo/lib/shared/utils/functions/isDisabledWithReason'
import { AuraBalSwapHandler } from './handlers/AuraBalSwap.handler'
import { DefaultSwapHandler } from './handlers/DefaultSwap.handler'
import { bn } from '@repo/lib/shared/utils/numbers'
import { NativeWrapHandler } from './handlers/NativeWrap.handler'
import { PoolSwapHandler } from './handlers/PoolSwap.handler'
import { SwapHandler } from './handlers/Swap.handler'
import { useSimulateSwapQuery } from './queries/useSimulateSwapQuery'
import { useTokens } from '../tokens/TokensProvider'
import { useDisclosure } from '@chakra-ui/react'
import { useSwapSteps } from './useSwapSteps'
import { isAuraBalSwap } from './swap.helpers'
import {
OSwapAction,
SdkSimulateSwapResponse,
SimulateSwapResponse,
SwapAction,
SwapState,
} from './swap.types'
import { SwapHandler } from './handlers/Swap.handler'
import { isSameAddress, selectByAddress } from '@repo/lib/shared/utils/addresses'
import { useVault } from '@repo/lib/shared/hooks/useVault'
import { NativeWrapHandler } from './handlers/NativeWrap.handler'
import { useIsPoolSwapUrl } from './useIsPoolSwapUrl'
import { useSwapSteps } from './useSwapSteps'
import {
getWrapHandlerClass,
getWrapType,
Expand All @@ -36,19 +50,7 @@ import {
isSupportedWrap,
isWrapOrUnwrap,
} from './wrap.helpers'
import { useTokenInputsValidation } from '../tokens/TokenInputsValidationProvider'
import { useMakeVarPersisted } from '@repo/lib/shared/hooks/useMakeVarPersisted'
import { HumanAmount } from '@balancer/sdk'
import { ChainSlug, chainToSlugMap, slugToChainMap } from '../pool/pool.utils'
import { invert } from 'lodash'
import { useTransactionSteps } from '../transactions/transaction-steps/useTransactionSteps'
import { useTokenBalances } from '../tokens/TokenBalancesProvider'
import { useNetworkConfig } from '@repo/lib/config/useNetworkConfig'
import { usePriceImpact } from '../price-impact/PriceImpactProvider'
import { calcMarketPriceImpact } from '../price-impact/price-impact.utils'
import { isAuraBalSwap } from './swap.helpers'
import { AuraBalSwapHandler } from './handlers/AuraBalSwap.handler'
import { useIsPoolSwapUrl } from './useIsPoolSwapUrl'
import { ProtocolVersion } from '../pool/pool.types'

export type UseSwapResponse = ReturnType<typeof _useSwap>
export const SwapContext = createContext<UseSwapResponse | null>(null)
Expand All @@ -69,7 +71,9 @@ function selectSwapHandler(
chain: GqlChain,
swapType: GqlSorSwapType,
apolloClient: ApolloClient<object>,
tokens: GqlToken[]
tokens: GqlToken[],
poolId?: Hex,
poolVersion?: ProtocolVersion,
): SwapHandler {
if (isNativeWrap(tokenInAddress, tokenOutAddress, chain)) {
return new NativeWrapHandler(apolloClient)
Expand All @@ -80,17 +84,21 @@ function selectSwapHandler(
return new AuraBalSwapHandler(tokens)
}

if (poolId && poolVersion) return new PoolSwapHandler(poolId, tokens, poolVersion)

return new DefaultSwapHandler(apolloClient)
}

export type SwapProviderProps = {
pathParams: PathParams
isPoolSwap?: boolean
// Only used by pool swap
poolId?: Hex
poolTokens?: GqlToken[]
poolVersion?: ProtocolVersion
}
export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps) {
export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) {
const urlTxHash = pathParams.urlTxHash
const isPoolSwap = !!poolId
const isPoolSwapUrl = useIsPoolSwapUrl()
const swapStateVar = useMakeVarPersisted<SwapState>(
{
Expand All @@ -107,7 +115,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)
swapType: GqlSorSwapType.ExactIn,
selectedChain: GqlChain.Mainnet,
},
'swapState'
'swapState',
)

const swapState = useReactiveVar(swapStateVar)
Expand All @@ -126,18 +134,20 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)
const previewModalDisclosure = useDisclosure()

const client = useApolloClient()
const handler = useMemo(
() =>
selectSwapHandler(
swapState.tokenIn.address,
swapState.tokenOut.address,
swapState.selectedChain,
swapState.swapType,
client,
tokens
),
[swapState.tokenIn.address, swapState.tokenOut.address, swapState.selectedChain]
)
const handler = useMemo(() => {
if (isPoolSwap) resetSwapAmounts()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be here. The useMemo is about setting the swap handler and shouldn't contain other logic. If this side-effect is needed I guess it will have to go in a useEffect.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, why is it needed only in the case of it being a pool swap?


return selectSwapHandler(
swapState.tokenIn.address,
swapState.tokenOut.address,
swapState.selectedChain,
swapState.swapType,
client,
tokens,
poolId,
poolVersion,
)
}, [swapState.tokenIn.address, swapState.tokenOut.address, swapState.selectedChain])

const isTokenInSet = swapState.tokenIn.address !== emptyAddress
const isTokenOutSet = swapState.tokenOut.address !== emptyAddress
Expand All @@ -156,28 +166,33 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)
const tokenInUsd = usdValueForToken(tokenInInfo, swapState.tokenIn.amount)
const tokenOutUsd = usdValueForToken(tokenOutInfo, swapState.tokenOut.amount)

const getSwapAmount = () => {
const swapState = swapStateVar()
return (
(swapState.swapType === GqlSorSwapType.ExactIn
? swapState.tokenIn.amount
: swapState.tokenOut.amount) || '0'
)
}

const shouldFetchSwap = (state: SwapState, urlTxHash?: Hash) => {
if (urlTxHash) return false
return (
isAddress(state.tokenIn.address) &&
isAddress(state.tokenOut.address) &&
!!state.swapType &&
bn(getSwapAmount(swapState)).gt(0)
bn(getSwapAmount()).gt(0)
)
}

const getSwapAmount = (state: SwapState) =>
(state.swapType === GqlSorSwapType.ExactIn ? state.tokenIn.amount : state.tokenOut.amount) ||
'0'

const simulationQuery = useSimulateSwapQuery({
handler,
swapInputs: {
chain: swapState.selectedChain,
tokenIn: swapState.tokenIn.address,
tokenOut: swapState.tokenOut.address,
swapType: swapState.swapType,
swapAmount: getSwapAmount(swapState),
swapAmount: getSwapAmount(),
},
enabled: shouldFetchSwap(swapState, urlTxHash),
})
Expand Down Expand Up @@ -247,7 +262,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)

function setTokenInAmount(
amount: string,
{ userTriggered = true }: { userTriggered?: boolean } = {}
{ userTriggered = true }: { userTriggered?: boolean } = {},
) {
const state = swapStateVar()
const newState = {
Expand All @@ -274,7 +289,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)

function setTokenOutAmount(
amount: string,
{ userTriggered = true }: { userTriggered?: boolean } = {}
{ userTriggered = true }: { userTriggered?: boolean } = {},
) {
const state = swapStateVar()
const newState = {
Expand Down Expand Up @@ -396,7 +411,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)
const wrapType = getWrapType(
swapState.tokenIn.address,
swapState.tokenOut.address,
swapState.selectedChain
swapState.selectedChain,
)
return wrapType ? wrapType : OSwapAction.SWAP
}
Expand Down Expand Up @@ -550,7 +565,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps)
[needsToAcceptHighPI, 'Accept high price impact first'],
[hasValidationErrors, 'Invalid input'],
[simulationQuery.isError, 'Error fetching swap'],
[simulationQuery.isLoading, 'Fetching swap...']
[simulationQuery.isLoading, 'Fetching swap...'],
)

return {
Expand Down
Loading
Loading