Skip to content

Commit

Permalink
Merge pull request #62 from VenusProtocol/refactor-custom-get-trade-l…
Browse files Browse the repository at this point in the history
…ogic-to-bot

refactor: move custom get trade logic to bot
  • Loading branch information
coreyar authored Sep 19, 2024
2 parents 1f82efa + 089cf9a commit 368091d
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 55 deletions.
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

0 comments on commit 368091d

Please sign in to comment.