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

refactor: move custom get trade logic to bot #62

Merged
merged 1 commit into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions packages/cli/source/commands/convert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 15 additions & 3 deletions packages/keeper-bots/src/converter-bot/TokenConverter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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,
Expand Down Expand Up @@ -231,7 +244,6 @@ describe("Token Converter", () => {

expect(pancakeSwapProviderMock).toHaveBeenNthCalledWith(
3,
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
750000000000000000n,
Expand Down
29 changes: 27 additions & 2 deletions packages/keeper-bots/src/converter-bot/TokenConverter.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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]];
}

/**
Expand Down
33 changes: 5 additions & 28 deletions packages/keeper-bots/src/providers/pancake-swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -107,32 +106,23 @@ 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);

let trade;
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,
{
Expand All @@ -145,6 +135,8 @@ class PancakeSwapProvider extends SwapProvider {
},
);

priceImpact = SmartRouter.getPriceImpact(response);

if (response) {
trade = {
inputToken: {
Expand All @@ -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}`;
Expand All @@ -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];
}

/**
Expand Down
5 changes: 2 additions & 3 deletions packages/keeper-bots/src/providers/swap-provider.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -45,8 +46,6 @@ class SwapProvider {
}

async getBestTrade(
// eslint-disable-next-line
tokenConverter: Address,
// eslint-disable-next-line
swapFrom: Address,
// eslint-disable-next-line
Expand All @@ -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");
}
}
Expand Down
24 changes: 5 additions & 19 deletions packages/keeper-bots/src/providers/uniswap.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -134,7 +120,7 @@ class UniswapProvider extends SwapProvider {
throw new Error("No trade found");
}

return [trade, updatedAmountIn[0]];
return [trade, null];
}

/**
Expand Down
Loading