Skip to content

Commit

Permalink
Merge pull request #51 from VenusProtocol/refactor-token-amounts
Browse files Browse the repository at this point in the history
refactor: create trade with token amounts as fractions
  • Loading branch information
coreyar authored Aug 15, 2024
2 parents 86f884f + d508e42 commit 9591c12
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 69 deletions.
6 changes: 3 additions & 3 deletions packages/keeper-bots/src/__mocks__/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CurrencyAmount, Percent, Token } from "@pancakeswap/sdk";
import { CurrencyAmount, Fraction, Percent, Token } from "@pancakeswap/sdk";
import { RouteV3 } from "@uniswap/router-sdk";
import { Token as UniswapToken } from "@uniswap/sdk-core";
import { FeeAmount, Pool, Route as V3RouteSDK } from "@uniswap/v3-sdk";
Expand Down Expand Up @@ -113,11 +113,11 @@ export const mockTrade = {
path: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c000cf6bb5389c92bdda8a3747ddb454cb7a64626c63" as const,
inputToken: {
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c" as const,
amount: 8904975230019520420n,
amount: new Fraction(8904975230019520420n, 1),
},
outputToken: {
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63" as const,
amount: 517926942058379677423n,
amount: new Fraction(517926942058379677423n, 1),
},
};

Expand Down
173 changes: 125 additions & 48 deletions packages/keeper-bots/src/converter-bot/TokenConverter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,28 +121,53 @@ describe("Token Converter", () => {
(tokenConverter.publicClient.simulateContract as jest.Mock).mockImplementation(() => ({
result: [1000000000000000000n, 1000000000000000000n],
}));
const [trade, updatedAmountIn] = await tokenConverter.getBestTrade(
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
1000000000000000000000n,
);

expect(
await tokenConverter.getBestTrade(
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
1000000000000000000000n,
),
).toEqual([
{
inputToken: {
expect(trade.inputToken).toEqual({
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
amount: {
currency: {
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
amount: "8",
chainId: 56,
decimals: 18,
isNative: false,
isToken: true,
name: undefined,
projectLink: undefined,
symbol: "WBNB",
},
outputToken: {
decimalScale: 1000000000000000000n,
denominator: 1n,
numerator: 8904975230019520420n,
},
});
expect(trade.outputToken).toEqual({
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63",
amount: {
currency: {
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63",
amount: "517",
chainId: 56,
decimals: 18,
isNative: false,
isToken: true,
name: undefined,
projectLink: undefined,
symbol: "XVS",
},
path: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c630009c4bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
decimalScale: 1000000000000000000n,
denominator: 1n,
numerator: 517926942058379677423n,
},
[1000000000000000000n, 1000000000000000000n],
]);
});
expect(trade.path).toEqual(
"0xcf6bb5389c92bdda8a3747ddb454cb7a64626c630009c4bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
);
expect(updatedAmountIn).toEqual([1000000000000000000n, 1000000000000000000n]);
});

test("should call getBestTrade again if price impact is high with lower amount", async () => {
Expand All @@ -168,27 +193,53 @@ describe("Token Converter", () => {
return new Percent(9n, 1000n);
});

expect(
await pancakeSwapProvider.getBestTrade(
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
1000000000000000000000n,
),
).toEqual([
{
inputToken: {
const [trade, updatedAmountIn] = await pancakeSwapProvider.getBestTrade(
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
1000000000000000000000n,
);

expect(trade.inputToken).toEqual({
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
amount: {
currency: {
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
amount: "8",
chainId: 56,
decimals: 18,
isNative: false,
isToken: true,
name: undefined,
projectLink: undefined,
symbol: "WBNB",
},
outputToken: {
decimalScale: 1000000000000000000n,
denominator: 1n,
numerator: 8904975230019520420n,
},
});
expect(trade.outputToken).toEqual({
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63",
amount: {
currency: {
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63",
amount: "517",
chainId: 56,
decimals: 18,
isNative: false,
isToken: true,
name: undefined,
projectLink: undefined,
symbol: "XVS",
},
path: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c630009c4bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
decimalScale: 1000000000000000000n,
denominator: 1n,
numerator: 517926942058379677423n,
},
[1000000000000000000n, 1000000000000000000n],
]);
});
expect(trade.path).toEqual(
"0xcf6bb5389c92bdda8a3747ddb454cb7a64626c630009c4bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
);
expect(updatedAmountIn).toEqual([1000000000000000000n, 1000000000000000000n]);

expect(pancakeSwapProviderMock).toHaveBeenNthCalledWith(
3,
Expand Down Expand Up @@ -840,28 +891,54 @@ describe("Token Converter", () => {
result: [1000000000000000000n, 1000000000000000000n],
}));

expect(
await tokenConverter.prepareConversion(
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
1000000000000000000000n,
),
).toEqual({
trade: {
inputToken: {
amount: "8",
const arbitrageArgs = await tokenConverter.prepareConversion(
addresses.USDCPrimeConverter,
addresses.WBNB,
addresses.USDC,
1000000000000000000000n,
);

expect(arbitrageArgs?.amount).toEqual(1000000000000000000n);
expect(arbitrageArgs?.trade.inputToken).toEqual({
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
amount: {
currency: {
address: "0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
chainId: 56,
decimals: 18,
isNative: false,
isToken: true,
name: undefined,
projectLink: undefined,
symbol: "WBNB",
},
outputToken: {
amount: "517",
decimalScale: 1000000000000000000n,
denominator: 1n,
numerator: 8904975230019520420n,
},
});
expect(arbitrageArgs?.trade.outputToken).toEqual({
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63",
amount: {
currency: {
address: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c63",
chainId: 56,
decimals: 18,
isNative: false,
isToken: true,
name: undefined,
projectLink: undefined,
symbol: "XVS",
},
path: "0xcf6bb5389c92bdda8a3747ddb454cb7a64626c630009c4bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
decimalScale: 1000000000000000000n,
denominator: 1n,
numerator: 517926942058379677423n,
},
amount: 1000000000000000000n,
minIncome: 999999999999999992n,
});
expect(arbitrageArgs?.trade.path).toEqual(
"0xcf6bb5389c92bdda8a3747ddb454cb7a64626c630009c4bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c",
);
expect(arbitrageArgs?.minIncome).toEqual(-7904975230019520420n);

expect(subscriberMock).toHaveBeenCalledWith({
type: "GetBestTrade",
Expand Down
15 changes: 7 additions & 8 deletions packages/keeper-bots/src/converter-bot/TokenConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,14 @@ export class TokenConverter {
* @param amount Amount to check/ request if an allowance has been granted
*/
async checkAndRequestAllowance(token: Address, owner: Address, spender: Address, amount: bigint) {
const approvalAmount = await this.publicClient.readContract({
const allowance = await this.publicClient.readContract({
address: token,
abi: erc20Abi,
functionName: "allowance",
args: [owner, spender],
});

if (approvalAmount < amount) {
if (allowance < amount) {
const trx = await this.walletClient.writeContract({
address: token,
abi: erc20Abi,
Expand Down Expand Up @@ -444,16 +444,17 @@ export class TokenConverter {
tradeAmount: { amountOut: tradeAmount && tradeAmount[0], amountIn: tradeAmount && tradeAmount[1] },
swap: {
inputToken: {
amount: trade.inputToken.amount.toString(),
amount: trade.inputToken.amount.toFixed(0),
token: trade.inputToken.address,
},
outputToken: {
amount: trade.outputToken.amount.toString(),
amount: trade.outputToken.amount.toFixed(0),
token: trade.outputToken.address,
},
},
};
}

this.sendMessage({
type: "GetBestTrade",
error,
Expand All @@ -468,13 +469,11 @@ export class TokenConverter {

if (trade && tradeAmount) {
// the difference between the token you get from TokenConverter and the token you pay to the MM
const minIncome = BigInt(
new Fraction(tradeAmount[0], 1).subtract(trade.inputToken.amount).toFixed(0, { groupSeparator: "" }),
);
const minIncome = new Fraction(tradeAmount[0], 1).subtract(trade.inputToken.amount);
return {
trade,
amount: tradeAmount[0],
minIncome,
minIncome: BigInt(minIncome.toFixed(0, { groupSeparator: "" })),
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class PancakeSwapProvider extends SwapProvider {
functionName: "getUpdatedAmountIn",
args: [amount, swapToToken.address, swapFromToken.address],
});

try {
const response = await SmartRouter.getBestTrade(
CurrencyAmount.fromRawAmount(swapToToken, updatedAmountIn[1]),
Expand All @@ -107,14 +108,15 @@ class PancakeSwapProvider extends SwapProvider {
quoterOptimization: true,
},
);

if (response) {
trade = {
inputToken: {
amount: response.inputAmount.toFixed(0, { groupSeparator: "" }),
amount: response.inputAmount,
address: response.inputAmount.currency.address,
},
outputToken: {
amount: response.outputAmount.toFixed(0, { groupSeparator: "" }),
amount: response.outputAmount,
address: response.outputAmount.currency.address,
},
path: this.encodeExactInputPath(response.routes[0]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class SwapProvider {
getOutputCurrency = <P = Pool, T = Token>(pool: P, inputToken: T): T => {
// @ts-expect-error library types don't match
const { token0, token1 } = pool;
console.log("token0.equals(inputToken)", token0.equals(inputToken));
return token0.equals(inputToken) ? token1 : token0;
};

Expand Down
13 changes: 8 additions & 5 deletions packages/keeper-bots/src/converter-bot/providers/uniswap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Fraction } from "@pancakeswap/sdk";
import { IRoute } from "@uniswap/router-sdk";
import { ChainId, Currency, CurrencyAmount, Fraction, Percent, Token, TradeType } from "@uniswap/sdk-core";
import { ChainId, Currency, CurrencyAmount, Percent, Token, TradeType } from "@uniswap/sdk-core";
import { AlphaRouter, SwapOptionsSwapRouter02, SwapType } from "@uniswap/smart-order-router";
import { Pool, Route } from "@uniswap/v3-sdk";
import { ethers } from "ethers";
Expand Down Expand Up @@ -103,14 +104,16 @@ class UniswapProvider extends SwapProvider {

trade = {
inputToken: {
amount: BigInt(
new Fraction(inputCurrency.numerator, inputCurrency.denominator).toFixed(0, { groupSeparator: "" }),
amount: new Fraction(
BigInt(inputCurrency.numerator.toString()),
BigInt(inputCurrency.denominator.toString()),
),
address: (inputCurrency?.currency as Token).address as Address,
},
outputToken: {
amount: BigInt(
new Fraction(outputCurrency.numerator, outputCurrency.denominator).toFixed(0, { groupSeparator: "" }),
amount: new Fraction(
BigInt(outputCurrency.numerator.toString()),
BigInt(outputCurrency.denominator.toString()),
),
address: (outputCurrency?.currency as Token).address as Address,
},
Expand Down
5 changes: 3 additions & 2 deletions packages/keeper-bots/src/converter-bot/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Fraction } from "@pancakeswap/sdk";
import { Address } from "viem";

import { BalanceResult } from "./queries/getTokenConvertersTokenBalances";
Expand Down Expand Up @@ -74,11 +75,11 @@ export type Message =

export interface TradeRoute {
inputToken: {
amount: bigint;
amount: Fraction;
address: Address;
};
outputToken: {
amount: bigint;
amount: Fraction;
address: Address;
};
path: Address;
Expand Down

0 comments on commit 9591c12

Please sign in to comment.