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

Publish release funds updates #65

Merged
merged 10 commits into from
Sep 30, 2024
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@venusprotocol/cli",
"version": "1.6.0",
"version": "1.6.1-dev.2",
"license": "MIT",
"bin": {
"venus": "dist/venus.js"
Expand Down
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
2 changes: 1 addition & 1 deletion packages/cli/source/commands/releaseFunds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function ReleaseFunds({ options = {} }: Props) {
const withBalances = allPools.reduce((acc, curr) => {
const tokensWithBalance = curr[1].filter(t => tokensWithBalances.has(t.underlyingAddress));
if (tokensWithBalance.length) {
acc[curr[0]] = tokensWithBalance.map(t => t.vTokenAddress);
acc[curr[0]] = tokensWithBalance.map(t => t.underlyingAddress);
}
return acc;
}, {} as Record<Address, readonly Address[]>);
Expand Down
2 changes: 1 addition & 1 deletion packages/keeper-bots/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@venusprotocol/keeper-bots",
"version": "1.1.0",
"version": "1.1.1-dev.2",
"description": "",
"scripts": {
"dev": "tsc --watch",
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Address } from "viem";

import getConfig from "../../config";
import { coreComptrollerAbi, vBep20InterfaceAbi } from "../../config/abis/generated";
import { coreComptrollerAbi, coreVTokenAbi } from "../../config/abis/generated";
import getAddresses from "../../config/addresses";
import publicClient from "../../config/clients/publicClient";
import type { PoolAddressArray } from "../types";
Expand All @@ -16,10 +16,11 @@ export const getCoreMarkets = async (): Promise<PoolAddressArray[]> => {
functionName: "getAllMarkets",
});

// @ts-expect-error not infinte
const underlyingAddresses = await publicClient.multicall({
contracts: markets.map(m => ({
address: m,
abi: vBep20InterfaceAbi,
abi: coreVTokenAbi,
functionName: "underlying",
args: [],
})),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Address } from "viem";

import { vBep20InterfaceAbi } from "../../config/abis/generated";
import { poolLensAbi } from "../../config/abis/generated";
import { ilVTokenAbi, poolLensAbi } from "../../config/abis/generated";
import getAddresses from "../../config/addresses";
import publicClient from "../../config/clients/publicClient";
import type { PoolAddressArray } from "../types";
Expand All @@ -16,11 +15,12 @@ export const getIsolatedMarkets = async (): Promise<PoolAddressArray[]> => {
});

const underlyingAddressesByPool = await Promise.all(
// @ts-expect-error not infinte
pools.map(async p => {
return await publicClient.multicall({
contracts: p.vTokens.map(m => ({
address: m.vToken,
abi: vBep20InterfaceAbi,
abi: ilVTokenAbi,
functionName: "underlying",
args: [],
})),
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
20 changes: 15 additions & 5 deletions packages/keeper-bots/wagmi.config.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import ilComptrollerArtifact from "@venusprotocol/isolated-pools/artifacts/contracts/Comptroller.sol/Comptroller.json" assert { type: "json" };
import poolLensArtifact from "@venusprotocol/isolated-pools/artifacts/contracts/Lens/PoolLens.sol/PoolLens.json" assert { type: "json" };
import ilVTokenArtifact from "@venusprotocol/isolated-pools/artifacts/contracts/VToken.sol/VToken.json" assert { type: "json" };
import liquidationOperatorArtifact from "@venusprotocol/keeper-bot-contracts/artifacts/contracts/operators/LiquidationOperator.sol/LiquidationOperator.json" assert { type: "json" };
import tokenConverterOperatorArtifact from "@venusprotocol/keeper-bot-contracts/artifacts/contracts/operators/TokenConverterOperator.sol/TokenConverterOperator.json" assert { type: "json" };
import resilientOracleArtifact from "@venusprotocol/oracle/artifacts/contracts/ResilientOracle.sol/ResilientOracle.json" assert { type: "json" };
import protocolShareReserveArtifact from "@venusprotocol/protocol-reserve/artifacts/contracts/ProtocolReserve/ProtocolShareReserve.sol/ProtocolShareReserve.json" assert { type: "json" };
import tokenConverterArtifact from "@venusprotocol/protocol-reserve/artifacts/contracts/TokenConverter/AbstractTokenConverter.sol/AbstractTokenConverter.json" assert { type: "json" };
import vBnbAdminArtifact from "@venusprotocol/venus-protocol/artifacts/contracts/Admin/VBNBAdmin.sol/VBNBAdmin.json" assert { type: "json" };
import diamondComptrollerArtifact from "@venusprotocol/venus-protocol/artifacts/contracts/Comptroller/Diamond/DiamondConsolidated.sol/DiamondConsolidated.json" assert { type: "json" };
import venusLensArtifact from "@venusprotocol/venus-protocol/artifacts/contracts/Lens/VenusLens.sol/VenusLens.json" assert { type: "json" };
import coreVTokenArtifact from "@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VTokens/VBep20Delegate.sol/VBep20Delegate.json" assert { type: "json" };
import vBep20InterfaceArtifact from "@venusprotocol/venus-protocol/artifacts/contracts/Tokens/VTokens/VTokenInterfaces.sol/VBep20Interface.json" assert { type: "json" };
import { defineConfig } from "@wagmi/cli";
import { Abi } from "abitype";

const getExternalContracts = async (): Promise<{ name: string; abi: Abi }[]> => [
{
abi: vBep20InterfaceArtifact.abi as Abi,
name: "VBep20Interface",
},
{
abi: poolLensArtifact.abi as Abi,
name: "PoolLens",
Expand All @@ -28,6 +26,18 @@ const getExternalContracts = async (): Promise<{ name: string; abi: Abi }[]> =>
abi: ilComptrollerArtifact.abi as Abi,
name: "IlComptroller",
},
{
abi: ilVTokenArtifact.abi as Abi,
name: "ilVToken",
},
{
abi: resilientOracleArtifact.abi as Abi,
name: "resilientOracle",
},
{
abi: liquidationOperatorArtifact.abi as Abi,
name: "liquidationOperator",
},
{
abi: coreVTokenArtifact.abi as Abi,
name: "CoreVToken",
Expand Down
Loading