From cde86019c903bcdd53aa095ec374d55256ccdd58 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Tue, 22 Oct 2024 15:29:46 +0200 Subject: [PATCH 1/6] feat: add single pool handler --- .../[id]/swap/[[...txHash]]/page.tsx | 10 +- .../pool/PoolDetail/PoolHeader/PoolHeader.tsx | 5 +- packages/lib/modules/pool/pool.types.ts | 2 + packages/lib/modules/swap/SwapDetails.tsx | 12 +- packages/lib/modules/swap/SwapProvider.tsx | 110 ++++++++++-------- .../swap/handlers/BaseDefaultSwap.handler.ts | 101 ++++++++++++++++ .../swap/handlers/DefaultSwap.handler.ts | 92 +++------------ .../modules/swap/handlers/PoolSwap.handler.ts | 58 +++++++++ packages/lib/modules/swap/swap.types.ts | 9 +- 9 files changed, 264 insertions(+), 135 deletions(-) create mode 100644 packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts create mode 100644 packages/lib/modules/swap/handlers/PoolSwap.handler.ts diff --git a/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx b/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx index adedf1f6d..d2358c4ad 100644 --- a/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx +++ b/apps/frontend-v3/app/(app)/pools/[chain]/[variant]/[id]/swap/[[...txHash]]/page.tsx @@ -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[] } @@ -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 ( diff --git a/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx b/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx index b3127caef..05e354468 100644 --- a/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx +++ b/packages/lib/modules/pool/PoolDetail/PoolHeader/PoolHeader.tsx @@ -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() @@ -67,10 +67,7 @@ export function PoolHeader() { Add liquidity - {/* - Will be enabled when pool swaps handler is implemented: - */} @@ -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 @@ -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.` @@ -187,7 +187,7 @@ export function SwapDetails() { - {isDefaultSwap && } + {isDefaultSwap ? : null} ) } diff --git a/packages/lib/modules/swap/SwapProvider.tsx b/packages/lib/modules/swap/SwapProvider.tsx index cd6c1473d..10bed97e2 100644 --- a/packages/lib/modules/swap/SwapProvider.tsx +++ b/packages/lib/modules/swap/SwapProvider.tsx @@ -1,22 +1,38 @@ '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, @@ -24,10 +40,8 @@ import { 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, @@ -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 export const SwapContext = createContext(null) @@ -69,7 +71,9 @@ function selectSwapHandler( chain: GqlChain, swapType: GqlSorSwapType, apolloClient: ApolloClient, - tokens: GqlToken[] + tokens: GqlToken[], + poolId?: Hex, + poolVersion?: ProtocolVersion ): SwapHandler { if (isNativeWrap(tokenInAddress, tokenOutAddress, chain)) { return new NativeWrapHandler(apolloClient) @@ -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( { @@ -128,14 +136,20 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps) const client = useApolloClient() const handler = useMemo( () => - selectSwapHandler( - swapState.tokenIn.address, - swapState.tokenOut.address, - swapState.selectedChain, - swapState.swapType, - client, - tokens - ), + { + if (isPoolSwap) resetSwapAmounts() + + return selectSwapHandler( + swapState.tokenIn.address, + swapState.tokenOut.address, + swapState.selectedChain, + swapState.swapType, + client, + tokens, + poolId, + poolVersion + ) + }, [swapState.tokenIn.address, swapState.tokenOut.address, swapState.selectedChain] ) @@ -156,20 +170,24 @@ 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: { @@ -177,7 +195,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps) tokenIn: swapState.tokenIn.address, tokenOut: swapState.tokenOut.address, swapType: swapState.swapType, - swapAmount: getSwapAmount(swapState), + swapAmount: getSwapAmount(), }, enabled: shouldFetchSwap(swapState, urlTxHash), }) @@ -189,11 +207,11 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps) swapType, }) - if (swapType === GqlSorSwapType.ExactIn) { + if (swapType === GqlSorSwapType.ExactIn) { setTokenOutAmount(returnAmount, { userTriggered: false }) - } else { - setTokenInAmount(returnAmount, { userTriggered: false }) - } + } else { + setTokenInAmount(returnAmount, { userTriggered: false }) + } } function setSelectedChain(_selectedChain: GqlChain) { diff --git a/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts b/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts new file mode 100644 index 000000000..c9231bab7 --- /dev/null +++ b/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts @@ -0,0 +1,101 @@ +import { Path, Slippage, Swap, SwapKind, TokenAmount } from '@balancer/sdk' +import { getChainId } from '@repo/lib/config/app.config' +import { + GqlSorSwapType +} from '@repo/lib/shared/services/api/generated/graphql' +import { TransactionConfig } from '../../web3/contracts/contract.types' +import { SdkBuildSwapInputs, SdkSimulateSwapResponse, SimulateSwapInputs } from '../swap.types' +import { SwapHandler } from './Swap.handler' +import { formatUnits } from 'viem' +import { getRpcUrl } from '../../web3/transports' +import { bn } from '@repo/lib/shared/utils/numbers' +import { ProtocolVersion } from '../../pool/pool.types' + +/** + * Base abstract class that shares common logic shared by Default standard swaps and single pool swaps. + */ +export abstract class BaseDefaultSwapHandler implements SwapHandler { + + public abstract name: string + public abstract simulate({ ...variables }: SimulateSwapInputs): Promise + + build({ + simulateResponse: { swap, queryOutput }, + slippagePercent, + account, + selectedChain, + wethIsEth, + }: SdkBuildSwapInputs): TransactionConfig { + const tx = swap.buildCall({ + slippage: Slippage.fromPercentage(slippagePercent as `${number}`), + deadline: BigInt(Number.MAX_SAFE_INTEGER), + sender: account, + recipient: account, + wethIsEth, + queryOutput, + }) + + return { + account, + chainId: getChainId(selectedChain), + data: tx.callData, + value: tx.value, + to: tx.to, + } + } + + /** + * PRIVATE METHODS + */ + protected async runSimulation({protocolVersion, swapInputs, paths, hopCount}: { + protocolVersion: ProtocolVersion + swapInputs: SimulateSwapInputs + paths: Path[], + hopCount: number + }):Promise { + + const {chain, swapType, swapAmount} = swapInputs + + // Get accurate return amount with onchain call + const rpcUrl = getRpcUrl(getChainId(chain)) + + const swap = new Swap({ + chainId: getChainId(chain), + paths, + swapKind: this.swapTypeToKind(swapType), + }) + + const queryOutput = await swap.query(rpcUrl) + let onchainReturnAmount: TokenAmount + if (queryOutput.swapKind === SwapKind.GivenIn) { + onchainReturnAmount = queryOutput.expectedAmountOut + } else { + onchainReturnAmount = queryOutput.expectedAmountIn + } + + // Format return amount to human readable + const returnAmount = formatUnits(onchainReturnAmount.amount, onchainReturnAmount.token.decimals) + + return { + protocolVersion, + hopCount, + swapType, + returnAmount, + swap, + queryOutput, + effectivePrice: bn(swapAmount).div(returnAmount).toString(), + effectivePriceReversed: bn(returnAmount).div(swapAmount).toString(), + } + } + + protected swapTypeToKind(swapType: GqlSorSwapType): SwapKind { + switch (swapType) { + case GqlSorSwapType.ExactIn: + return SwapKind.GivenIn + case GqlSorSwapType.ExactOut: + return SwapKind.GivenOut + default: + throw new Error('Invalid swap type') + } + } +} diff --git a/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts b/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts index b565616f9..c9d9732ea 100644 --- a/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts +++ b/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts @@ -1,25 +1,20 @@ -import { getChainId } from '@repo/lib/config/app.config' -import { SwapHandler } from './Swap.handler' +import { ApolloClient } from '@apollo/client' +import { Path } from '@balancer/sdk' import { - GetSorSwapsDocument, - GqlSorSwapType, + GetSorSwapsDocument } from '@repo/lib/shared/services/api/generated/graphql' -import { ApolloClient } from '@apollo/client' -import { Path, Slippage, Swap, SwapKind, TokenAmount } from '@balancer/sdk' -import { formatUnits } from 'viem' -import { TransactionConfig } from '../../web3/contracts/contract.types' -import { SdkBuildSwapInputs, SdkSimulateSwapResponse, SimulateSwapInputs } from '../swap.types' -import { getRpcUrl } from '../../web3/transports' -import { bn } from '@repo/lib/shared/utils/numbers' +import { ProtocolVersion } from '../../pool/pool.types' +import { SdkSimulateSwapResponse, SimulateSwapInputs } from '../swap.types' +import { BaseDefaultSwapHandler } from './BaseDefaultSwap.handler' -export class DefaultSwapHandler implements SwapHandler { +export class DefaultSwapHandler extends BaseDefaultSwapHandler { name = 'DefaultSwapHandler' - constructor(public apolloClient: ApolloClient) {} + constructor(public apolloClient: ApolloClient) { + super() + } async simulate({ ...variables }: SimulateSwapInputs): Promise { - const { chain, swapType } = variables - const rpcUrl = getRpcUrl(getChainId(chain)) const { data } = await this.apolloClient.query({ query: GetSorSwapsDocument, @@ -28,6 +23,7 @@ export class DefaultSwapHandler implements SwapHandler { notifyOnNetworkStatusChange: true, }) + const hopCount: number = data.swaps.routes[0]?.hops?.length || 0 const paths = data.swaps.paths.map( path => ({ @@ -37,67 +33,11 @@ export class DefaultSwapHandler implements SwapHandler { }) as Path ) - const swap = new Swap({ - chainId: getChainId(chain), + return this.runSimulation({ + protocolVersion: data.swaps.protocolVersion as ProtocolVersion, paths, - swapKind: this.swapTypeToKind(swapType), - }) - - // Get accurate return amount with onchain call - const queryOutput = await swap.query(rpcUrl) - let onchainReturnAmount: TokenAmount - if (queryOutput.swapKind === SwapKind.GivenIn) { - onchainReturnAmount = queryOutput.expectedAmountOut - } else { - onchainReturnAmount = queryOutput.expectedAmountIn - } - - // Format return amount to human readable - const returnAmount = formatUnits(onchainReturnAmount.amount, onchainReturnAmount.token.decimals) - - return { - ...data.swaps, - returnAmount, - swap, - queryOutput, - effectivePrice: bn(variables.swapAmount).div(returnAmount).toString(), - effectivePriceReversed: bn(returnAmount).div(variables.swapAmount).toString(), - } - } - - build({ - simulateResponse: { swap, queryOutput }, - slippagePercent, - account, - selectedChain, - wethIsEth, - }: SdkBuildSwapInputs): TransactionConfig { - const tx = swap.buildCall({ - slippage: Slippage.fromPercentage(slippagePercent as `${number}`), - deadline: BigInt(Number.MAX_SAFE_INTEGER), - sender: account, - recipient: account, - wethIsEth, - queryOutput, - }) - - return { - account, - chainId: getChainId(selectedChain), - data: tx.callData, - value: tx.value, - to: tx.to, - } - } - - private swapTypeToKind(swapType: GqlSorSwapType): SwapKind { - switch (swapType) { - case GqlSorSwapType.ExactIn: - return SwapKind.GivenIn - case GqlSorSwapType.ExactOut: - return SwapKind.GivenOut - default: - throw new Error('Invalid swap type') - } + hopCount, + swapInputs: variables, + }) } } diff --git a/packages/lib/modules/swap/handlers/PoolSwap.handler.ts b/packages/lib/modules/swap/handlers/PoolSwap.handler.ts new file mode 100644 index 000000000..c4bee7087 --- /dev/null +++ b/packages/lib/modules/swap/handlers/PoolSwap.handler.ts @@ -0,0 +1,58 @@ +import { Path, TokenApi } from '@balancer/sdk' +import { + GqlSorSwapType, + GqlToken, +} from '@repo/lib/shared/services/api/generated/graphql' +import { isSameAddress } from '@repo/lib/shared/utils/addresses' +import { Hex, parseUnits } from 'viem' +import { ProtocolVersion } from '../../pool/pool.types' +import { SdkSimulateSwapResponse, SimulateSwapInputs } from '../swap.types' +import { BaseDefaultSwapHandler } from './BaseDefaultSwap.handler' + +export class PoolSwapHandler extends BaseDefaultSwapHandler { + name = 'PoolSwapHandler' + + constructor(public poolId: Hex, public tokens: GqlToken[], public poolVersion: ProtocolVersion) { + super() + } + + async simulate({ ...variables }: SimulateSwapInputs): Promise { + const { swapType, swapAmount } = variables + + const tokenIn = this.tokens.find(token => isSameAddress(token.address, variables.tokenIn)) + + if (!tokenIn) { + throw new Error('TokenIn not found in pool swap handler') + } + + const tokenOut = this.tokens.find(token => isSameAddress(token.address, variables.tokenOut)) + + if (!tokenOut) { + throw new Error('TokenIn not found in pool swap handler') + } + + const inputAmountRaw: bigint = swapType === GqlSorSwapType.ExactIn + ? parseUnits(swapAmount, tokenIn.decimals) + : 0n + const outputAmountRaw: bigint = swapType === GqlSorSwapType.ExactOut + ? parseUnits(swapAmount, tokenOut.decimals) + : 0n + + const poolPath: Path = { + pools: [this.poolId], + inputAmountRaw, + outputAmountRaw, + protocolVersion: this.poolVersion, + tokens: [tokenIn as TokenApi, tokenOut as TokenApi ], + } + + const paths = [poolPath] + + return this.runSimulation({ + protocolVersion: this.poolVersion, + paths, + hopCount: 1, + swapInputs: variables, + }) + } +} diff --git a/packages/lib/modules/swap/swap.types.ts b/packages/lib/modules/swap/swap.types.ts index 52182019b..fa426c394 100644 --- a/packages/lib/modules/swap/swap.types.ts +++ b/packages/lib/modules/swap/swap.types.ts @@ -39,7 +39,14 @@ export type SimulateSwapResponse = Pick< 'effectivePrice' | 'effectivePriceReversed' | 'returnAmount' | 'swapType' > -export interface SdkSimulateSwapResponse extends SimulateSwapResponse, ApiSwapQuery { +export interface SdkSimulateSwapResponse extends SimulateSwapResponse { + swap: Swap + queryOutput: ExactInQueryOutput | ExactOutQueryOutput + protocolVersion: number + hopCount: number +} + +export interface SimulateSinglePoolSwapResponse extends SimulateSwapResponse { swap: Swap queryOutput: ExactInQueryOutput | ExactOutQueryOutput } From e8094b11cd838eb3ee13e1d23cb12b136a8f2812 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Tue, 22 Oct 2024 16:05:27 +0200 Subject: [PATCH 2/6] chore: format handlers --- packages/eslint-config/next.js | 17 ++++--------- .../swap/handlers/DefaultSwap.handler.ts | 9 +++---- .../modules/swap/handlers/PoolSwap.handler.ts | 25 +++++++++---------- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/packages/eslint-config/next.js b/packages/eslint-config/next.js index 2cbc08c21..ed46e1c36 100644 --- a/packages/eslint-config/next.js +++ b/packages/eslint-config/next.js @@ -1,6 +1,6 @@ -const { resolve } = require('node:path') +const {resolve} = require('node:path'); -const project = resolve(process.cwd(), 'tsconfig.json') +const project = resolve(process.cwd(), 'tsconfig.json'); /** @type {import('eslint').Linter.Config} */ module.exports = { @@ -33,11 +33,11 @@ module.exports = { '.*.js', 'node_modules/', ], - overrides: [{ files: ['*.js?(x)', '*.ts?(x)'] }], + overrides: [{files: ['*.js?(x)', '*.ts?(x)']}], rules: { curly: ['error', 'multi-line'], 'no-console': ['off'], - 'max-len': ['warn', { code: 120, ignoreComments: true, ignoreUrls: true }], + 'max-len': ['warn', {code: 120, ignoreComments: true, ignoreUrls: true}], '@typescript-eslint/no-explicit-any': ['off'], 'no-html-link-for-pages': ['off'], 'no-restricted-imports': [ @@ -53,13 +53,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'], @@ -70,4 +63,4 @@ module.exports = { ], 'react/jsx-no-leaked-render': 'off', }, -} +}; diff --git a/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts b/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts index c9d9732ea..c001c6838 100644 --- a/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts +++ b/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts @@ -1,8 +1,6 @@ import { ApolloClient } from '@apollo/client' import { Path } from '@balancer/sdk' -import { - GetSorSwapsDocument -} from '@repo/lib/shared/services/api/generated/graphql' +import { GetSorSwapsDocument } from '@repo/lib/shared/services/api/generated/graphql' import { ProtocolVersion } from '../../pool/pool.types' import { SdkSimulateSwapResponse, SimulateSwapInputs } from '../swap.types' import { BaseDefaultSwapHandler } from './BaseDefaultSwap.handler' @@ -15,7 +13,6 @@ export class DefaultSwapHandler extends BaseDefaultSwapHandler { } async simulate({ ...variables }: SimulateSwapInputs): Promise { - const { data } = await this.apolloClient.query({ query: GetSorSwapsDocument, variables: { ...variables, queryBatchSwap: false }, // We don't need the API to do a query because we're doing that via the SDK below. @@ -30,7 +27,7 @@ export class DefaultSwapHandler extends BaseDefaultSwapHandler { ...path, inputAmountRaw: BigInt(path.inputAmountRaw), outputAmountRaw: BigInt(path.outputAmountRaw), - }) as Path + }) as Path, ) return this.runSimulation({ @@ -38,6 +35,6 @@ export class DefaultSwapHandler extends BaseDefaultSwapHandler { paths, hopCount, swapInputs: variables, - }) + }) } } diff --git a/packages/lib/modules/swap/handlers/PoolSwap.handler.ts b/packages/lib/modules/swap/handlers/PoolSwap.handler.ts index c4bee7087..1d7a9114f 100644 --- a/packages/lib/modules/swap/handlers/PoolSwap.handler.ts +++ b/packages/lib/modules/swap/handlers/PoolSwap.handler.ts @@ -1,8 +1,5 @@ import { Path, TokenApi } from '@balancer/sdk' -import { - GqlSorSwapType, - GqlToken, -} from '@repo/lib/shared/services/api/generated/graphql' +import { GqlSorSwapType, GqlToken } from '@repo/lib/shared/services/api/generated/graphql' import { isSameAddress } from '@repo/lib/shared/utils/addresses' import { Hex, parseUnits } from 'viem' import { ProtocolVersion } from '../../pool/pool.types' @@ -12,7 +9,11 @@ import { BaseDefaultSwapHandler } from './BaseDefaultSwap.handler' export class PoolSwapHandler extends BaseDefaultSwapHandler { name = 'PoolSwapHandler' - constructor(public poolId: Hex, public tokens: GqlToken[], public poolVersion: ProtocolVersion) { + constructor( + public poolId: Hex, + public tokens: GqlToken[], + public poolVersion: ProtocolVersion, + ) { super() } @@ -31,19 +32,17 @@ export class PoolSwapHandler extends BaseDefaultSwapHandler { throw new Error('TokenIn not found in pool swap handler') } - const inputAmountRaw: bigint = swapType === GqlSorSwapType.ExactIn - ? parseUnits(swapAmount, tokenIn.decimals) - : 0n - const outputAmountRaw: bigint = swapType === GqlSorSwapType.ExactOut - ? parseUnits(swapAmount, tokenOut.decimals) - : 0n + const inputAmountRaw: bigint = + swapType === GqlSorSwapType.ExactIn ? parseUnits(swapAmount, tokenIn.decimals) : 0n + const outputAmountRaw: bigint = + swapType === GqlSorSwapType.ExactOut ? parseUnits(swapAmount, tokenOut.decimals) : 0n const poolPath: Path = { pools: [this.poolId], inputAmountRaw, outputAmountRaw, protocolVersion: this.poolVersion, - tokens: [tokenIn as TokenApi, tokenOut as TokenApi ], + tokens: [tokenIn as TokenApi, tokenOut as TokenApi], } const paths = [poolPath] @@ -53,6 +52,6 @@ export class PoolSwapHandler extends BaseDefaultSwapHandler { paths, hopCount: 1, swapInputs: variables, - }) + }) } } From 93ad3bc425787d417d487c71824a064275a18d39 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Tue, 22 Oct 2024 16:57:53 +0200 Subject: [PATCH 3/6] chore: format next.js --- packages/eslint-config/next.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/eslint-config/next.js b/packages/eslint-config/next.js index ed46e1c36..6748055c5 100644 --- a/packages/eslint-config/next.js +++ b/packages/eslint-config/next.js @@ -1,11 +1,12 @@ -const {resolve} = require('node:path'); +const { resolve } = require('node:path') -const project = resolve(process.cwd(), 'tsconfig.json'); +const project = resolve(process.cwd(), 'tsconfig.json') /** @type {import('eslint').Linter.Config} */ module.exports = { extends: [ 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', require.resolve('@vercel/style-guide/eslint/next'), require.resolve('@vercel/style-guide/eslint/react'), @@ -33,11 +34,11 @@ module.exports = { '.*.js', 'node_modules/', ], - overrides: [{files: ['*.js?(x)', '*.ts?(x)']}], + overrides: [{ files: ['*.js?(x)', '*.ts?(x)'] }], rules: { curly: ['error', 'multi-line'], 'no-console': ['off'], - 'max-len': ['warn', {code: 120, ignoreComments: true, ignoreUrls: true}], + 'max-len': ['warn', { code: 120, ignoreComments: true, ignoreUrls: true }], '@typescript-eslint/no-explicit-any': ['off'], 'no-html-link-for-pages': ['off'], 'no-restricted-imports': [ @@ -63,4 +64,4 @@ module.exports = { ], 'react/jsx-no-leaked-render': 'off', }, -}; +} From fae4cc6b4e85c9ca47e22b9ffac185cca5fd8947 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Tue, 22 Oct 2024 16:59:12 +0200 Subject: [PATCH 4/6] chore: format SwapProvider --- packages/lib/modules/swap/SwapProvider.tsx | 63 +++++++++++----------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/packages/lib/modules/swap/SwapProvider.tsx b/packages/lib/modules/swap/SwapProvider.tsx index 10bed97e2..28fc8378d 100644 --- a/packages/lib/modules/swap/SwapProvider.tsx +++ b/packages/lib/modules/swap/SwapProvider.tsx @@ -73,7 +73,7 @@ function selectSwapHandler( apolloClient: ApolloClient, tokens: GqlToken[], poolId?: Hex, - poolVersion?: ProtocolVersion + poolVersion?: ProtocolVersion, ): SwapHandler { if (isNativeWrap(tokenInAddress, tokenOutAddress, chain)) { return new NativeWrapHandler(apolloClient) @@ -115,7 +115,7 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) swapType: GqlSorSwapType.ExactIn, selectedChain: GqlChain.Mainnet, }, - 'swapState' + 'swapState', ) const swapState = useReactiveVar(swapStateVar) @@ -134,24 +134,20 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) const previewModalDisclosure = useDisclosure() const client = useApolloClient() - const handler = useMemo( - () => - { - if (isPoolSwap) resetSwapAmounts() - - 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 handler = useMemo(() => { + if (isPoolSwap) resetSwapAmounts() + + 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 @@ -170,13 +166,14 @@ export function _useSwap({ poolId, poolVersion, 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 + 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 @@ -207,11 +204,11 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) swapType, }) - if (swapType === GqlSorSwapType.ExactIn) { + if (swapType === GqlSorSwapType.ExactIn) { setTokenOutAmount(returnAmount, { userTriggered: false }) - } else { - setTokenInAmount(returnAmount, { userTriggered: false }) - } + } else { + setTokenInAmount(returnAmount, { userTriggered: false }) + } } function setSelectedChain(_selectedChain: GqlChain) { @@ -265,7 +262,7 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) function setTokenInAmount( amount: string, - { userTriggered = true }: { userTriggered?: boolean } = {} + { userTriggered = true }: { userTriggered?: boolean } = {}, ) { const state = swapStateVar() const newState = { @@ -292,7 +289,7 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) function setTokenOutAmount( amount: string, - { userTriggered = true }: { userTriggered?: boolean } = {} + { userTriggered = true }: { userTriggered?: boolean } = {}, ) { const state = swapStateVar() const newState = { @@ -414,7 +411,7 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) const wrapType = getWrapType( swapState.tokenIn.address, swapState.tokenOut.address, - swapState.selectedChain + swapState.selectedChain, ) return wrapType ? wrapType : OSwapAction.SWAP } @@ -568,7 +565,7 @@ export function _useSwap({ poolId, poolVersion, 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 { From a35d48a10b80900d5b7de449f19830855fb81acb Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Tue, 22 Oct 2024 17:01:11 +0200 Subject: [PATCH 5/6] chore: format SwapProvider --- .../swap/handlers/BaseDefaultSwap.handler.ts | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts b/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts index c9231bab7..b3fe5ae5b 100644 --- a/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts +++ b/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts @@ -1,8 +1,6 @@ import { Path, Slippage, Swap, SwapKind, TokenAmount } from '@balancer/sdk' import { getChainId } from '@repo/lib/config/app.config' -import { - GqlSorSwapType -} from '@repo/lib/shared/services/api/generated/graphql' +import { GqlSorSwapType } from '@repo/lib/shared/services/api/generated/graphql' import { TransactionConfig } from '../../web3/contracts/contract.types' import { SdkBuildSwapInputs, SdkSimulateSwapResponse, SimulateSwapInputs } from '../swap.types' import { SwapHandler } from './Swap.handler' @@ -15,7 +13,6 @@ import { ProtocolVersion } from '../../pool/pool.types' * Base abstract class that shares common logic shared by Default standard swaps and single pool swaps. */ export abstract class BaseDefaultSwapHandler implements SwapHandler { - public abstract name: string public abstract simulate({ ...variables }: SimulateSwapInputs): Promise @@ -47,45 +44,49 @@ export abstract class BaseDefaultSwapHandler implements SwapHandler { /** * PRIVATE METHODS */ - protected async runSimulation({protocolVersion, swapInputs, paths, hopCount}: { + protected async runSimulation({ + protocolVersion, + swapInputs, + paths, + hopCount, + }: { protocolVersion: ProtocolVersion - swapInputs: SimulateSwapInputs - paths: Path[], + swapInputs: SimulateSwapInputs + paths: Path[] hopCount: number - }):Promise { - - const {chain, swapType, swapAmount} = swapInputs + }): Promise { + const { chain, swapType, swapAmount } = swapInputs - // Get accurate return amount with onchain call - const rpcUrl = getRpcUrl(getChainId(chain)) + // Get accurate return amount with onchain call + const rpcUrl = getRpcUrl(getChainId(chain)) - const swap = new Swap({ - chainId: getChainId(chain), - paths, - swapKind: this.swapTypeToKind(swapType), - }) + const swap = new Swap({ + chainId: getChainId(chain), + paths, + swapKind: this.swapTypeToKind(swapType), + }) - const queryOutput = await swap.query(rpcUrl) - let onchainReturnAmount: TokenAmount - if (queryOutput.swapKind === SwapKind.GivenIn) { - onchainReturnAmount = queryOutput.expectedAmountOut - } else { - onchainReturnAmount = queryOutput.expectedAmountIn - } + const queryOutput = await swap.query(rpcUrl) + let onchainReturnAmount: TokenAmount + if (queryOutput.swapKind === SwapKind.GivenIn) { + onchainReturnAmount = queryOutput.expectedAmountOut + } else { + onchainReturnAmount = queryOutput.expectedAmountIn + } - // Format return amount to human readable - const returnAmount = formatUnits(onchainReturnAmount.amount, onchainReturnAmount.token.decimals) + // Format return amount to human readable + const returnAmount = formatUnits(onchainReturnAmount.amount, onchainReturnAmount.token.decimals) - return { - protocolVersion, - hopCount, - swapType, - returnAmount, - swap, - queryOutput, - effectivePrice: bn(swapAmount).div(returnAmount).toString(), - effectivePriceReversed: bn(returnAmount).div(swapAmount).toString(), - } + return { + protocolVersion, + hopCount, + swapType, + returnAmount, + swap, + queryOutput, + effectivePrice: bn(swapAmount).div(returnAmount).toString(), + effectivePriceReversed: bn(returnAmount).div(swapAmount).toString(), + } } protected swapTypeToKind(swapType: GqlSorSwapType): SwapKind { From e913a52fb0fd7698ae4e67af9748855dfca20619 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 25 Oct 2024 17:20:02 +0200 Subject: [PATCH 6/6] chore: move reset condition --- packages/lib/modules/swap/SwapProvider.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/lib/modules/swap/SwapProvider.tsx b/packages/lib/modules/swap/SwapProvider.tsx index 28fc8378d..12d35557d 100644 --- a/packages/lib/modules/swap/SwapProvider.tsx +++ b/packages/lib/modules/swap/SwapProvider.tsx @@ -135,8 +135,6 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) const client = useApolloClient() const handler = useMemo(() => { - if (isPoolSwap) resetSwapAmounts() - return selectSwapHandler( swapState.tokenIn.address, swapState.tokenOut.address, @@ -493,6 +491,7 @@ export function _useSwap({ poolId, poolVersion, pathParams }: SwapProviderProps) setInitialAmounts(amountIn, amountOut) if (!swapState.tokenIn.address && !swapState.tokenOut.address) setDefaultTokens() + if (isPoolSwap) resetSwapAmounts() }, []) // When wallet chain changes, update the swap form chain