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/eslint-config/next.js b/packages/eslint-config/next.js index 2cbc08c21..6748055c5 100644 --- a/packages/eslint-config/next.js +++ b/packages/eslint-config/next.js @@ -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'), @@ -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'], 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..12d35557d 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( { @@ -107,7 +115,7 @@ export function _useSwap({ isPoolSwap = false, pathParams }: SwapProviderProps) swapType: GqlSorSwapType.ExactIn, selectedChain: GqlChain.Mainnet, }, - 'swapState' + 'swapState', ) const swapState = useReactiveVar(swapStateVar) @@ -126,18 +134,18 @@ 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(() => { + 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 @@ -156,20 +164,25 @@ 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 +190,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), }) @@ -247,7 +260,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 = { @@ -274,7 +287,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 = { @@ -396,7 +409,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 } @@ -478,6 +491,7 @@ export function _useSwap({ isPoolSwap = false, 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 @@ -550,7 +564,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 { 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..b3fe5ae5b --- /dev/null +++ b/packages/lib/modules/swap/handlers/BaseDefaultSwap.handler.ts @@ -0,0 +1,102 @@ +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..c001c6838 100644 --- a/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts +++ b/packages/lib/modules/swap/handlers/DefaultSwap.handler.ts @@ -1,26 +1,18 @@ -import { getChainId } from '@repo/lib/config/app.config' -import { SwapHandler } from './Swap.handler' -import { - GetSorSwapsDocument, - GqlSorSwapType, -} 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 { Path } from '@balancer/sdk' +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' -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, variables: { ...variables, queryBatchSwap: false }, // We don't need the API to do a query because we're doing that via the SDK below. @@ -28,76 +20,21 @@ export class DefaultSwapHandler implements SwapHandler { notifyOnNetworkStatusChange: true, }) + const hopCount: number = data.swaps.routes[0]?.hops?.length || 0 const paths = data.swaps.paths.map( path => ({ ...path, inputAmountRaw: BigInt(path.inputAmountRaw), outputAmountRaw: BigInt(path.outputAmountRaw), - }) as Path + }) as Path, ) - const swap = new Swap({ - chainId: getChainId(chain), + return this.runSimulation({ + protocolVersion: data.swaps.protocolVersion as ProtocolVersion, paths, - swapKind: this.swapTypeToKind(swapType), + hopCount, + swapInputs: variables, }) - - // 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') - } } } 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..1d7a9114f --- /dev/null +++ b/packages/lib/modules/swap/handlers/PoolSwap.handler.ts @@ -0,0 +1,57 @@ +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 }