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

feat: use automatic routing #531

Merged
merged 68 commits into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
06c2ff7
feat: use automatic routing
KirillDogadin-std Nov 10, 2022
1a13592
improvements
KirillDogadin-std Nov 11, 2022
ca21a62
lint
KirillDogadin-std Nov 11, 2022
85d58e3
rename
KirillDogadin-std Nov 11, 2022
9820bcc
pools generated, route ignored
KirillDogadin-std Nov 11, 2022
b69041d
rm usesess block of code
KirillDogadin-std Nov 11, 2022
6c8481e
add dai to pools
KirillDogadin-std Nov 11, 2022
5d812be
lint
KirillDogadin-std Nov 11, 2022
1ee2f0e
rm useless arg
KirillDogadin-std Nov 11, 2022
6bab52a
rm useless comment
KirillDogadin-std Nov 11, 2022
a776b77
imporve
KirillDogadin-std Nov 11, 2022
6a7d437
refactor: only v3 is affected
KirillDogadin-std Nov 11, 2022
6169244
fix prev functionality
KirillDogadin-std Nov 11, 2022
81859b0
latest
KirillDogadin-std Nov 14, 2022
c354a73
explicitly defined the chain id
KirillDogadin-std Nov 15, 2022
d441c42
improve chain id arg
KirillDogadin-std Nov 15, 2022
c5c6c01
lint
KirillDogadin-std Nov 15, 2022
26c5d5e
Merge remote-tracking branch 'origin/main' into multi-callee
KirillDogadin-std Nov 15, 2022
70c954f
Use the quote if awailable
KirillDogadin-std Nov 15, 2022
87dfced
Demo of too long test
KirillDogadin-std Nov 15, 2022
0c06187
Revert "Demo of too long test"
valiafetisov Nov 17, 2022
b2decf4
Merge branch 'main' into multi-callee
valiafetisov Nov 17, 2022
2cfe37e
Merge branch 'main' into multi-callee
valiafetisov Nov 17, 2022
5ee3d39
fix autorouter info on the dashboard
valiafetisov Nov 17, 2022
c0f6ad0
adjust typing to more optimal;
KirillDogadin-std Nov 23, 2022
eb52d1b
use the preloaded pools
KirillDogadin-std Nov 23, 2022
8d87f08
seems to work
KirillDogadin-std Nov 28, 2022
f7c745e
fix frontend typing
KirillDogadin-std Nov 28, 2022
ab0f839
This line was problematic
KirillDogadin-std Nov 28, 2022
3d1d528
use fees
KirillDogadin-std Nov 28, 2022
49d6788
lint
KirillDogadin-std Nov 28, 2022
94a6e6d
fix: frontend generate fake auction
KirillDogadin-std Nov 28, 2022
86e5988
Merge branch 'main' into multi-callee
KirillDogadin-std Nov 28, 2022
40420fe
hardcode fake pools
KirillDogadin-std Nov 28, 2022
a4fd7a2
lint
KirillDogadin-std Nov 28, 2022
f75d338
as above
KirillDogadin-std Nov 28, 2022
3337a56
fix: remove autorouter during tests
KirillDogadin-std Nov 29, 2022
10fb6c0
upd: hardhat
KirillDogadin-std Nov 29, 2022
f74531a
adjust test command
KirillDogadin-std Nov 29, 2022
41a4138
quickie patch with enabling and disabling of the autorouter
KirillDogadin-std Nov 30, 2022
9cbe6a0
spin the icon if no value
KirillDogadin-std Nov 30, 2022
e921cfe
caching for the sake of performance
KirillDogadin-std Nov 30, 2022
bd5253d
refactor: use `NODE_ENV=test` to let know that test is running
KirillDogadin-std Dec 1, 2022
5689a41
Throw if preloaded pools are not present in crvlp1
KirillDogadin-std Dec 1, 2022
6b7c41a
as above for several other callees
KirillDogadin-std Dec 1, 2022
64a00ff
feat: stricter typing
KirillDogadin-std Dec 1, 2022
e958161
refactor: dont check for autorouter being enabled for curvelp callee
KirillDogadin-std Dec 1, 2022
671c0e3
as above for reth callee
KirillDogadin-std Dec 1, 2022
300bac2
refactor: extract pool generation call to the higher level
KirillDogadin-std Dec 1, 2022
33844b1
refactor: caching and minor frontend adjustments
KirillDogadin-std Dec 1, 2022
04011c9
fix: lint
KirillDogadin-std Dec 1, 2022
77bc448
refactor: i removed the wrong caching 😭
KirillDogadin-std Dec 1, 2022
011292d
feat: add autorouters everywhere
KirillDogadin-std Dec 1, 2022
44e2dc7
refactor: avoid comparing by name
KirillDogadin-std Dec 1, 2022
8383784
fix: build core
KirillDogadin-std Dec 1, 2022
344daad
fix: wait longer before vault liquidation in the test
KirillDogadin-std Dec 1, 2022
8da5335
fix: ui enable button
KirillDogadin-std Dec 1, 2022
4a46d32
fix: frontend
KirillDogadin-std Dec 1, 2022
0618459
adjust the import
KirillDogadin-std Dec 1, 2022
7a0ccfc
fix _getMarketPrice export
valiafetisov Dec 1, 2022
eed3c37
hardcode block number in the past
valiafetisov Dec 1, 2022
76f647d
Merge branch 'main' into multi-callee
KirillDogadin-std Dec 1, 2022
f2861a3
fix: route generation
KirillDogadin-std Dec 5, 2022
923522f
Merge branch 'main' into multi-callee
valiafetisov Dec 5, 2022
c250c8f
fix console error from upstream
valiafetisov Dec 5, 2022
4ff3d92
reorder simulations
valiafetisov Dec 5, 2022
1a887f5
fix MarketPriceSelection enable button
valiafetisov Dec 5, 2022
cc8dfb7
properly pass fees to the get pools
valiafetisov Dec 5, 2022
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
2,078 changes: 1,189 additions & 889 deletions core/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "tsc-watch --noClear",
"build": "tsc --build tsconfig.json",
"start": "node .",
"test": "npx hardhat test --network testnetwork --config local.hardhat.config.ts",
"test": "NODE_ENV=test npx hardhat test --network testnetwork --config local.hardhat.config.ts",
"lint": "eslint --ext \".js,.ts\" --ignore-path .gitignore . --max-warnings=0",
"hardhat:silent": "npx hardhat node &> /dev/null",
"hardhat": "npx hardhat node",
Expand Down
4 changes: 2 additions & 2 deletions core/simulations/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import vaultLiquidation from './configs/vaultLiquidation';
import onboardNewCollateral from './configs/onboardNewCollateral';

export const SIMULATIONS: Simulation[] = [
vaultLiquidation,
debtAuctionSimulation,
surplusAuctionBlockchain,
surplusAuctionSimulation,
surplusAuctionBlockchain,
wstethAuctionSimulation,
blockWithVaultsInAllStates,
vaultLiquidation,
onboardNewCollateral,
];
2 changes: 1 addition & 1 deletion core/simulations/configs/vaultLiquidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { fetchMaximumAuctionDurationInSeconds } from '../../src/fetch';
const TWO_YEARS_IN_MINUTES = 60 * 24 * 30 * 12 * 2;

const simulation: Simulation = {
title: 'Simulate collateral auction per collateral type',
title: 'Create collateral auction',
steps: [
{
title: 'Reset blockchain fork',
Expand Down
19 changes: 14 additions & 5 deletions core/src/auctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,21 @@ export const enrichMarketDataRecordsWithValues = async function (

export const enrichAuctionWithMarketDataRecords = async function (
auction: AuctionTransaction,
network: string
network: string,
useAutoRouter = false
): Promise<AuctionTransaction> {
if (!auction.isActive || !auction.approximateUnitPrice || auction.isFinished) {
return auction;
}
try {
const collateralToCoverDebt = await calculateCollateralToCoverDebt(network, auction);
const exchangeFees = await getDefaultMarketFee(network);
const marketDataRecords = await getMarketDataRecords(network, auction.collateralSymbol, collateralToCoverDebt);
const marketDataRecords = await getMarketDataRecords(
network,
auction.collateralSymbol,
collateralToCoverDebt,
useAutoRouter
);
const enrichedMarketDataRecords = await enrichMarketDataRecordsWithValues(
auction,
marketDataRecords,
Expand Down Expand Up @@ -206,12 +212,13 @@ export const enrichAuctionWithPriceDrop = async function (auction: Auction): Pro

export const enrichAuctionWithPriceDropAndMarketDataRecords = async function (
auction: Auction,
network: string
network: string,
useAutoRouter?: boolean
): Promise<Auction> {
const enrichedAuctionWithNewPriceDrop = await enrichAuctionWithPriceDrop(auction);
const fees = await getApproximateTransactionFees(network);
const auctionWithFees = await enrichAuctionWithTransactionFees(enrichedAuctionWithNewPriceDrop, fees, network);
return await enrichAuctionWithMarketDataRecords(auctionWithFees, network);
return await enrichAuctionWithMarketDataRecords(auctionWithFees, network, useAutoRouter);
};

export const fetchSingleAuctionById = async function (
Expand Down Expand Up @@ -338,7 +345,9 @@ export const bidWithCallee = async function (
notifier?: Notifier
): Promise<string> {
const calleeAddress = getCalleeAddressByCollateralType(network, auction.collateralType, marketId);
const calleeData = await getCalleeData(network, auction.collateralType, marketId, profitAddress);
const marketData = auction.marketDataRecords?.[marketId];
const preloadedPools = marketData && 'pools' in marketData ? marketData.pools : undefined;
const calleeData = await getCalleeData(network, auction.collateralType, marketId, profitAddress, preloadedPools);
const contractName = getClipperNameByCollateralType(auction.collateralType);
const contractParameters = [
convertNumberTo32Bytes(auction.index),
Expand Down
12 changes: 8 additions & 4 deletions core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import type { CalleeFunctions, CollateralConfig } from '../types';
import type { CalleeFunctions, CollateralConfig, Pool } from '../types';
import { ethers } from 'ethers';
import BigNumber from '../bignumber';
import { getContractAddressByName, getJoinNameByCollateralType } from '../contracts';
import { convertCrvethToEth, CURVE_COIN_INDEX, CURVE_POOL_ADDRESS } from './helpers/curve';
import { encodeRoute, convertCollateralToDai } from './helpers/uniswapV3';
import { convertCollateralToDai, encodePools } from './helpers/uniswapV3';

export const CHARTER_MANAGER_ADDRESS = '0x8377CD01a5834a6EaD3b7efb482f678f2092b77e';

const getCalleeData = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
profitAddress: string
profitAddress: string,
preloadedPools?: Pool[]
): Promise<string> {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee !== 'CurveLpTokenUniv3Callee') {
throw new Error(`Can not encode route for the "${collateral.ilk}"`);
}
const route = await encodeRoute(network, marketData.route);
if (!preloadedPools) {
throw new Error(`Can not encode route for the "${collateral.ilk}" without preloaded pools`);
}
const route = await encodePools(network, preloadedPools);
const curveData = [CURVE_POOL_ADDRESS, CURVE_COIN_INDEX];
const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk));
const minProfit = 1;
Expand Down
31 changes: 25 additions & 6 deletions core/src/calleeFunctions/UniswapV3Callee.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
import type { CalleeFunctions, CollateralConfig } from '../types';
import type { CalleeFunctions, CollateralConfig, Pool } from '../types';
import { ethers } from 'ethers';
import BigNumber from '../bignumber';
import { getContractAddressByName, getJoinNameByCollateralType } from '../contracts';
import { convertCollateralToDaiUsingRoute, encodeRoute } from './helpers/uniswapV3';
import { convertCollateralToDaiUsingPool, encodePools } from './helpers/uniswapV3';
import { getPools } from '.';
import { routeToPool } from './helpers/pools';
import { getRouteAndGasQuote } from './helpers/uniswapV3';

const getCalleeData = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
profitAddress: string
profitAddress: string,
preloadedPools?: Pool[]
valiafetisov marked this conversation as resolved.
Show resolved Hide resolved
): Promise<string> {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee !== 'UniswapV3Callee') {
throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`);
}
const pools = preloadedPools || (await getPools(network, collateral, marketId));
if (!pools) {
throw new Error(`getCalleeData called with invalid pools`);
}
const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk));
const minProfit = 1;
const uniswapV3route = await encodeRoute(network, [collateral.symbol, ...marketData.route]);
const uniswapV3pools = await encodePools(network, pools);
const typesArray = ['address', 'address', 'uint256', 'bytes', 'address'];
return ethers.utils.defaultAbiCoder.encode(typesArray, [
profitAddress,
joinAdapterAddress,
minProfit,
uniswapV3route,
uniswapV3pools,
ethers.constants.AddressZero,
]);
};
Expand All @@ -34,7 +42,18 @@ const getMarketPrice = async function (
collateralAmount: BigNumber
): Promise<BigNumber> {
// convert collateral into DAI
const daiAmount = await convertCollateralToDaiUsingRoute(network, collateral.symbol, marketId, collateralAmount);
const { route, fees } = await getRouteAndGasQuote(network, collateral.symbol, collateralAmount, marketId);
if (!route) {
throw new Error(`No route found for ${collateral.symbol} to DAI`);
}
const pools = await routeToPool(network, route, collateral.symbol, fees);
const daiAmount = await convertCollateralToDaiUsingPool(
network,
collateral.symbol,
marketId,
collateralAmount,
pools
);
Comment on lines +49 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better explanation would've been: previously we expected that pools (/routes) are static. Now we should receive and store pools together with prices (because re-requesting price via auto-router can change the pool) – so if the pool is generated, it have to be stored in the auction together with the price (unless the price is not an auction price, but just a reference price)


// return price per unit
return daiAmount.dividedBy(collateralAmount);
Expand Down
30 changes: 30 additions & 0 deletions core/src/calleeFunctions/helpers/pools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getTokenAddressByNetworkAndSymbol } from '../../tokens';
import { CollateralSymbol, Pool } from '../../types';

const getRouteSteps = (route: string[], fees: number[]) => {
const routeSteps = [];
for (let i = 0; i < route.length - 1; i++) {
routeSteps.push({ tokens: [route[i], route[i + 1]], fee: fees[i] });
}
return routeSteps;
};

export const routeToPool = async (
network: string,
route: string[],
collateralSymbol: CollateralSymbol,
uniswapFees?: number[]
): Promise<Pool[]> => {
const fees = uniswapFees || Array.from({ length: route.length + 2 }, () => 3000);
const fullRoute = [collateralSymbol, ...route, 'DAI'];
const routeSteps = getRouteSteps(fullRoute, fees);
return await Promise.all(
routeSteps.map(async step => ({
addresses: await Promise.all(
step.tokens.map(symbol => getTokenAddressByNetworkAndSymbol(network, symbol))
),
fee: step.fee,
routes: step.tokens,
}))
);
};
67 changes: 56 additions & 11 deletions core/src/calleeFunctions/helpers/uniswapAutoRouter.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import { AlphaRouter } from '@uniswap/smart-order-router';
import { Protocol } from '@uniswap/router-sdk';
import { Token, Percent, TradeType, CurrencyAmount } from '@uniswap/sdk-core';
import BigNumber from '../../bignumber';
import getProvider from '../../provider';
import { getDecimalChainIdByNetworkType } from '../../network';
import { getActualDecimalChainIdByNetworkType } from '../../network';
import { getTokenAddressByNetworkAndSymbol, getTokenDecimalsBySymbol } from '../../tokens';
import { getCollateralConfigBySymbol } from '../../constants/COLLATERALS';
import { DAI_NUMBER_OF_DIGITS } from '../../constants/UNITS';
import memoizee from 'memoizee';

const routers: Record<string, AlphaRouter> = {};

const getAutoRouter = async function (network: string): Promise<AlphaRouter> {
if (routers[network]) {
return routers[network];
}
routers[network] = new AlphaRouter({
chainId: getActualDecimalChainIdByNetworkType(network),
provider: await getProvider(network),
});
return routers[network];
};

const getUniswapTokenBySymbol = async function (network: string, symbol: string): Promise<Token> {
const tokenAddress = await getTokenAddressByNetworkAndSymbol(network, symbol);
const tokenDecimals = getTokenDecimalsBySymbol(symbol);
const decimalChainId = getDecimalChainIdByNetworkType(network);
const decimalChainId = getActualDecimalChainIdByNetworkType(network);
return new Token(decimalChainId, tokenAddress, tokenDecimals, symbol);
};

Expand All @@ -20,8 +36,7 @@ export const getUniswapAutoRoute = async function (
walletAddress?: string
) {
const collateralConfig = getCollateralConfigBySymbol(collateralSymbol);
const provider = await getProvider(network);
const router = new AlphaRouter({ chainId: 1, provider });
const router = await getAutoRouter(network);
const inputToken = await getUniswapTokenBySymbol(network, collateralConfig.symbol);
const outputToken = await getUniswapTokenBySymbol(network, 'DAI');

Expand All @@ -40,6 +55,7 @@ export const getUniswapAutoRoute = async function (
},
{
maxSplits: 0,
protocols: [Protocol.V3],
}
);
if (!route) {
Expand All @@ -48,27 +64,56 @@ export const getUniswapAutoRoute = async function (
return route;
};

export const fetchAutoRouteInformation = async function (
const trimRoute = (route: string[]): string[] => {
if (route.length < 2) {
throw new Error('Route array must have at least 2 elements.');
}
return route.slice(1, route.length - 1);
};

const _fetchAutoRouteInformation = async function (
network: string,
collateralSymbol: string,
inputAmount: string | number = 1,
walletAddress?: string
) {
try {
const token = await getUniswapTokenBySymbol(network, collateralSymbol);
const autoRouteData = await getUniswapAutoRoute(network, collateralSymbol, inputAmount, walletAddress);
const routes = autoRouteData.route[0].tokenPath.map(p => p.symbol);

const bestRoute = autoRouteData.route[0];
const fullRoute = bestRoute.tokenPath.map(p => {
if (!p.symbol) {
throw new Error(`Could not get symbol for token "${p.address}".`);
valiafetisov marked this conversation as resolved.
Show resolved Hide resolved
}
return p.symbol;
});
const route = trimRoute(fullRoute);
if (bestRoute.route.protocol !== Protocol.V3) {
throw new Error('Only V3 routes are supported.');
valiafetisov marked this conversation as resolved.
Show resolved Hide resolved
}
const fees = bestRoute.route.pools.map(pool => pool.fee);
const quote = new BigNumber(autoRouteData.quote.toFixed());
const quoteGasAdjusted = new BigNumber(autoRouteData.quoteGasAdjusted.toFixed());
return {
totalPrice: new BigNumber(autoRouteData.quote.toFixed(token.decimals)),
routes,
totalPrice: new BigNumber(autoRouteData.quote.toFixed(DAI_NUMBER_OF_DIGITS)),
route,
quote,
quoteGasAdjusted,
errorMessage: undefined,
fees,
};
} catch (error: any) {
return {
totalPrice: undefined,
routes: undefined,
route: undefined,
quote: undefined,
fees: undefined,
errorMessage: error.toString(),
};
}
};

export const fetchAutoRouteInformation = memoizee(_fetchAutoRouteInformation, {
promise: true,
maxAge: 1000 * 60, // 1 minute
length: 4,
});
5 changes: 4 additions & 1 deletion core/src/calleeFunctions/helpers/uniswapV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const EXCHANGE_RATE_CACHE = 20 * 1000;

const getCalleeConfig = function (collateral: CollateralConfig, marketId: string): RegularCalleeConfig {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee === 'UniswapV2CalleeDai' || marketData?.callee === 'UniswapV3Callee') {
const isUniswapTokenNonAutoRouted =
(marketData?.callee === 'UniswapV2CalleeDai' || marketData?.callee === 'UniswapV3Callee') &&
valiafetisov marked this conversation as resolved.
Show resolved Hide resolved
!('automaticRouter' in marketData);
if (isUniswapTokenNonAutoRouted) {
return marketData;
}
throw new Error(`"${collateral.symbol}" is not an UniSwap token`);
Expand Down
Loading