From 089cf9ae7b6716fe91db5f8855d95bdc163bbb36 Mon Sep 17 00:00:00 2001 From: Corey Rice Date: Thu, 19 Sep 2024 12:30:41 -0300 Subject: [PATCH] refactor: move custom get trade logic to bot --- packages/cli/source/commands/convert.tsx | 10 ++++++ .../src/converter-bot/TokenConverter.test.ts | 18 ++++++++-- .../src/converter-bot/TokenConverter.ts | 29 ++++++++++++++-- .../keeper-bots/src/providers/pancake-swap.ts | 33 +++---------------- .../src/providers/swap-provider.ts | 5 ++- packages/keeper-bots/src/providers/uniswap.ts | 24 +++----------- 6 files changed, 64 insertions(+), 55 deletions(-) diff --git a/packages/cli/source/commands/convert.tsx b/packages/cli/source/commands/convert.tsx index 51ceb3e..19297c8 100644 --- a/packages/cli/source/commands/convert.tsx +++ b/packages/cli/source/commands/convert.tsx @@ -234,6 +234,16 @@ export default function Convert({ options }: Props) { } else if (trade) { await tokenConverter.arbitrage(t.tokenConverter, trade, amount, minIncome); } + } else { + dispatch({ + error: `Swap amount too low (${assetOutUsdValue} USD)`, + type: "GetBestTrade", + context: { + converter: t.tokenConverter, + tokenToReceiveFromConverter: t.assetOut.address, + tokenToSendToConverter: t.assetIn.address, + }, + }); } } } while (loop); diff --git a/packages/keeper-bots/src/converter-bot/TokenConverter.test.ts b/packages/keeper-bots/src/converter-bot/TokenConverter.test.ts index d4a6364..e3af29c 100644 --- a/packages/keeper-bots/src/converter-bot/TokenConverter.test.ts +++ b/packages/keeper-bots/src/converter-bot/TokenConverter.test.ts @@ -165,7 +165,7 @@ describe("Token Converter", () => { }); test("should call getBestTrade again if price impact is high with lower amount", async () => { - const { subscriberMock, pancakeSwapProvider } = createTokenConverterInstance(); + const { subscriberMock, pancakeSwapProvider, tokenConverter } = createTokenConverterInstance(); const pancakeSwapProviderMock = jest .spyOn(PancakeSwapProvider.prototype, "getBestTrade") @@ -180,7 +180,20 @@ describe("Token Converter", () => { return new Percent(9n, 1000n); }); - const [trade] = await pancakeSwapProvider.getBestTrade( + (publicClient.simulateContract as unknown as jest.Mock).mockImplementation( + jest.fn(() => { + if (called) { + return { + result: [1000000000000000000n, 750000000000000000n], + }; + } + return { + result: [1000000000000000000n, 1000000000000000000n], + }; + }), + ); + + const [trade] = await tokenConverter.getBestTrade( addresses.USDCPrimeConverter, addresses.WBNB, addresses.USDC, @@ -231,7 +244,6 @@ describe("Token Converter", () => { expect(pancakeSwapProviderMock).toHaveBeenNthCalledWith( 3, - addresses.USDCPrimeConverter, addresses.WBNB, addresses.USDC, 750000000000000000n, diff --git a/packages/keeper-bots/src/converter-bot/TokenConverter.ts b/packages/keeper-bots/src/converter-bot/TokenConverter.ts index 23a4633..3d431de 100644 --- a/packages/keeper-bots/src/converter-bot/TokenConverter.ts +++ b/packages/keeper-bots/src/converter-bot/TokenConverter.ts @@ -1,4 +1,4 @@ -import { Fraction } from "@pancakeswap/sdk"; +import { Fraction, Percent } from "@pancakeswap/sdk"; import { Address, BaseError, ContractFunctionRevertedError, erc20Abi, formatUnits } from "viem"; import BotBase from "../bot-base"; @@ -10,6 +10,7 @@ import { vBnbAdminAbi, venusLensAbi, } from "../config/abis/generated"; +import { tokenConverterAbi } from "../config/abis/generated"; import getAddresses from "../config/addresses"; import { SwapProvider } from "../providers"; import { TradeRoute } from "../types"; @@ -63,7 +64,31 @@ export class TokenConverter extends BotBase { amount: bigint, fixedPairs?: boolean, ): Promise<[TradeRoute, bigint]> { - return this.swapProvider.getBestTrade(tokenConverter, swapFrom, swapTo, amount, fixedPairs); + // [amount transferred out of converter, amount transferred in] + const { result: updatedAmountIn } = await this.publicClient.simulateContract({ + address: tokenConverter, + abi: tokenConverterAbi, + functionName: "getUpdatedAmountIn", + args: [amount, swapTo, swapFrom], + }); + + const [trade, priceImpact] = await this.swapProvider.getBestTrade(swapFrom, swapTo, updatedAmountIn[1], fixedPairs); + + if (priceImpact && priceImpact.greaterThan(new Percent(5n, 1000n))) { + this.sendMessage({ + type: "GetBestTrade", + error: "High price impact", + context: { + converter: tokenConverter, + tokenToReceiveFromConverter: swapFrom, + tokenToSendToConverter: swapTo, + priceImpact: priceImpact.toFixed(), + }, + }); + + return this.getBestTrade(tokenConverter, swapFrom, swapTo, (updatedAmountIn[1] * 75n) / 100n, fixedPairs); + } + return [trade, updatedAmountIn[0]]; } /** diff --git a/packages/keeper-bots/src/providers/pancake-swap.ts b/packages/keeper-bots/src/providers/pancake-swap.ts index c6b9afd..804ad61 100644 --- a/packages/keeper-bots/src/providers/pancake-swap.ts +++ b/packages/keeper-bots/src/providers/pancake-swap.ts @@ -4,7 +4,6 @@ import { Client as UrqlClient, createClient } from "urql/core"; import { Address, Hex, encodePacked, erc20Abi } from "viem"; import getConfig from "../config"; -import { tokenConverterAbi } from "../config/abis/generated"; import type { SUPPORTED_CHAINS } from "../config/chains"; import { chains } from "../config/chains"; import { ConverterBotMessage } from "../converter-bot/types"; @@ -107,12 +106,11 @@ class PancakeSwapProvider extends SwapProvider { }; async getBestTrade( - tokenConverter: Address, swapFrom: Address, swapTo: Address, amount: bigint, fixedPairs: boolean = false, - ): Promise<[TradeRoute, bigint]> { + ): Promise<[TradeRoute, Percent | null]> { const swapFromToken = await this.getToken(swapFrom); const swapToToken = await this.getToken(swapTo); @@ -120,19 +118,11 @@ class PancakeSwapProvider extends SwapProvider { let error; let priceImpact; - // [amount transferred out of converter, amount transferred in] - const { result: updatedAmountIn } = await this.publicClient.simulateContract({ - address: tokenConverter, - abi: tokenConverterAbi, - functionName: "getUpdatedAmountIn", - args: [amount, swapTo, swapFrom], - }); - try { const candidatePools = await this.getCandidatePools(swapFromToken, swapToToken, fixedPairs); const response = await SmartRouter.getBestTrade( - CurrencyAmount.fromRawAmount(swapToToken, updatedAmountIn[1]), + CurrencyAmount.fromRawAmount(swapToToken, amount), swapFromToken, TradeType.EXACT_OUTPUT, { @@ -145,6 +135,8 @@ class PancakeSwapProvider extends SwapProvider { }, ); + priceImpact = SmartRouter.getPriceImpact(response); + if (response) { trade = { inputToken: { @@ -158,7 +150,6 @@ class PancakeSwapProvider extends SwapProvider { path: this.encodeExactInputPath(response.routes[0]), }; } - priceImpact = SmartRouter.getPriceImpact(response); } catch (e) { const message = (e as Error).message; error = `Error getting best trade - ${message}`; @@ -174,21 +165,7 @@ class PancakeSwapProvider extends SwapProvider { throw new Error("No trade found"); } - if (priceImpact.greaterThan(new Percent(5n, 1000n))) { - this.sendMessage({ - type: "GetBestTrade", - error: "High price impact", - context: { - converter: tokenConverter, - tokenToReceiveFromConverter: swapFromToken.address!, - tokenToSendToConverter: swapToToken.address!, - priceImpact: priceImpact.toFixed(), - }, - }); - - return this.getBestTrade(tokenConverter, swapFrom, swapTo, (updatedAmountIn[1] * 75n) / 100n, fixedPairs); - } - return [trade, updatedAmountIn[0]]; + return [trade, priceImpact]; } /** diff --git a/packages/keeper-bots/src/providers/swap-provider.ts b/packages/keeper-bots/src/providers/swap-provider.ts index 75664cc..8ce7d74 100644 --- a/packages/keeper-bots/src/providers/swap-provider.ts +++ b/packages/keeper-bots/src/providers/swap-provider.ts @@ -1,3 +1,4 @@ +import { Percent } from "@pancakeswap/sdk"; import { Token } from "@uniswap/sdk-core"; import { Pool } from "@uniswap/v3-sdk"; import { Address } from "viem"; @@ -45,8 +46,6 @@ class SwapProvider { } async getBestTrade( - // eslint-disable-next-line - tokenConverter: Address, // eslint-disable-next-line swapFrom: Address, // eslint-disable-next-line @@ -55,7 +54,7 @@ class SwapProvider { amount: bigint, // eslint-disable-next-line fixedPairs?: boolean, - ): Promise<[TradeRoute, bigint]> { + ): Promise<[TradeRoute, Percent | null]> { throw new Error("Not Implemented Error"); } } diff --git a/packages/keeper-bots/src/providers/uniswap.ts b/packages/keeper-bots/src/providers/uniswap.ts index f413b60..d5f58ee 100644 --- a/packages/keeper-bots/src/providers/uniswap.ts +++ b/packages/keeper-bots/src/providers/uniswap.ts @@ -1,4 +1,4 @@ -import { Fraction } from "@pancakeswap/sdk"; +import { Fraction, Percent as PancakePercent } from "@pancakeswap/sdk"; import { IRoute } from "@uniswap/router-sdk"; import { ChainId, Currency, CurrencyAmount, Percent, Token, TradeType } from "@uniswap/sdk-core"; import { AlphaRouter, SwapOptionsSwapRouter02, SwapType } from "@uniswap/smart-order-router"; @@ -8,7 +8,6 @@ import JSBI from "jsbi"; import { Address, Hex, encodePacked, erc20Abi } from "viem"; import getConfig from "../config"; -import { tokenConverterAbi } from "../config/abis/generated"; import type { SUPPORTED_CHAINS } from "../config/chains"; import { chains } from "../config/chains"; import { ConverterBotMessage } from "../converter-bot/types"; @@ -61,23 +60,10 @@ class UniswapProvider extends SwapProvider { throw new Error(`Unable to fetch token details for ${address}`); } - async getBestTrade( - tokenConverter: Address, - swapFrom: Address, - swapTo: Address, - amount: bigint, - ): Promise<[TradeRoute, bigint]> { + async getBestTrade(swapFrom: Address, swapTo: Address, amount: bigint): Promise<[TradeRoute, PancakePercent | null]> { const swapFromToken = await this.getToken(swapFrom); const swapToToken = await this.getToken(swapTo); - // [amount transferred out of converter, amount transferred in] - const { result: updatedAmountIn } = await this.publicClient.simulateContract({ - address: tokenConverter, - abi: tokenConverterAbi, - functionName: "getUpdatedAmountIn", - args: [amount, swapTo, swapFrom], - }); - const provider = new ethers.providers.JsonRpcProvider(config.rpcUrl); const router = new AlphaRouter({ chainId: config.network.id as ChainId, @@ -91,14 +77,14 @@ class UniswapProvider extends SwapProvider { }; let error; let trade; + try { const response = await router.route( - CurrencyAmount.fromRawAmount(swapToToken, JSBI.BigInt(updatedAmountIn[1].toString())), + CurrencyAmount.fromRawAmount(swapToToken, JSBI.BigInt(amount.toString())), swapFromToken, TradeType.EXACT_OUTPUT, options, ); - if (response) { const inputCurrency = response.trade.swaps[0].inputAmount; const outputCurrency = response.trade.swaps[response.trade.swaps.length - 1].outputAmount; @@ -134,7 +120,7 @@ class UniswapProvider extends SwapProvider { throw new Error("No trade found"); } - return [trade, updatedAmountIn[0]]; + return [trade, null]; } /**