Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5a690fa
WIP: Refactor core for multi-callee support
aomafarag Oct 21, 2022
86a64c9
Add exchange fee and store logic
aomafarag Oct 26, 2022
5c7534a
Merge branch 'main' into multi-callee-core
aomafarag Oct 26, 2022
2764e92
Refactor rETH
aomafarag Oct 26, 2022
ec7808d
Use suggestedMarketId in keeper
aomafarag Oct 26, 2022
e3ad2f6
Type assertion
aomafarag Oct 26, 2022
681143c
Lint
aomafarag Oct 26, 2022
5b2d9be
Validate suggestedMarketId in bot
aomafarag Oct 28, 2022
c56ecbd
Move fee calculation to fees.ts
aomafarag Oct 28, 2022
c73b2e0
Use marketId to return a single callee address
aomafarag Oct 28, 2022
9c3a3f9
Enrich auction with market data in a separate function
aomafarag Oct 28, 2022
fd2fe2d
Refactor types.ts and add new fee
aomafarag Oct 29, 2022
671e8fc
Move new fee calculation to fees.ts
aomafarag Oct 29, 2022
8830104
Remove typecasts
aomafarag Oct 29, 2022
e163276
Add marketId parameter to getCalleeData() function
aomafarag Oct 29, 2022
86fe41a
Quick fixes and adjustments
aomafarag Oct 29, 2022
6e75ecc
Validate callee name
aomafarag Oct 31, 2022
ac7a19d
Add marketId parameter to getCalleeData and getMarketPrice, and use i…
aomafarag Nov 2, 2022
41dae71
Implement suggestions
aomafarag Nov 2, 2022
0d4d1df
Refacor indexing callee.exxhanges in own const, and add exchange fee …
aomafarag Nov 2, 2022
04735cb
Implement suggested modifications
aomafarag Nov 3, 2022
2bf25ae
Skip with invalid callee
aomafarag Nov 3, 2022
7c3ad35
Refactor for loop and calculate net profit
aomafarag Nov 3, 2022
fe8b419
Refactor
aomafarag Nov 4, 2022
630dc07
Add marketId to index records
aomafarag Nov 4, 2022
7b9acc1
Remove dummy console.warn
aomafarag Nov 4, 2022
9402ef9
Pass suggestedMarketId in the frontend
aomafarag Nov 4, 2022
0708c95
Merge branch 'main' into multi-callee-core
valiafetisov Nov 4, 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
13 changes: 12 additions & 1 deletion bot/src/keepers/collateral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ const checkAndParticipateIfPossible = async function (network: string, auction:
return;
}

// check if callee is valid
if (!auctionTransaction.suggestedMarketId) {
console.info(`collateral keeper: auction "${auction.id}" has no valid market`);
return;
}

// check auction's profit
if (!auctionTransaction.transactionGrossProfit || auctionTransaction.transactionGrossProfit.isLessThan(0)) {
if (auctionTransaction.transactionGrossProfit) {
Expand Down Expand Up @@ -103,7 +109,12 @@ const checkAndParticipateIfPossible = async function (network: string, auction:

// bid on the Auction
console.info(`collateral keeper: auction "${auctionTransaction.id}": attempting swap execution`);
const bidHash = await bidWithCallee(network, auctionTransaction, walletAddress);
const bidHash = await bidWithCallee(
network,
auctionTransaction,
auctionTransaction.suggestedMarketId,
walletAddress
);
console.info(
`collateral keeper: auction "${auctionTransaction.id}" was succesfully executed via "${bidHash}" transaction`
);
Expand Down
132 changes: 106 additions & 26 deletions core/src/auctions.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import type { Auction, AuctionInitialInfo, AuctionTransaction, Notifier, TakeEvent } from './types';
import type {
Auction,
AuctionInitialInfo,
AuctionTransaction,
Notifier,
TakeEvent,
MarketData,
ExchangeFees,
} from './types';
import BigNumber from './bignumber';
import fetchAuctionsByCollateralType, {
fetchAuctionByCollateralTypeAndAuctionIndex,
fetchAuctionStatus,
fetchMinimumBidDai,
} from './fetch';
import { getCalleeData, getMarketPrice } from './calleeFunctions';
import { getBestMarketId, getCalleeData, getMarketDataRecords, getMarketPrice } from './calleeFunctions';
import { fetchCalcParametersByCollateralType } from './params';
import executeTransaction from './execute';
import { RAY_NUMBER_OF_DIGITS, WAD_NUMBER_OF_DIGITS, NULL_BYTES } from './constants/UNITS';
Expand All @@ -20,7 +28,7 @@ import {
import { getSupportedCollateralTypes } from './addresses';
import getContract, { getClipperNameByCollateralType } from './contracts';
import convertNumberTo32Bytes from './helpers/convertNumberTo32Bytes';
import { enrichAuctionWithTransactionFees, getApproximateTransactionFees } from './fees';
import { enrichAuctionWithTransactionFees, getApproximateTransactionFees, getDefaultMarketFee } from './fees';
import parseAuctionId from './helpers/parseAuctionId';
import { EventFilter } from 'ethers';
import getNetworkDate, { fetchDateByBlockNumber } from './date';
Expand Down Expand Up @@ -67,26 +75,97 @@ const calculateCollateralToCoverDebt = async function (network: string, auction:
return auction.debtDAI.dividedBy(marketPriceForAllCollateral);
};

const enrichAuctionWithMarketValues = async function (auction: Auction, network: string): Promise<Auction> {
export const enrichMarketDataRecordsWithValues = async function (
auction: AuctionTransaction,
marketDataRecords: Record<string, MarketData>,
exchangeFees: ExchangeFees,
amount: BigNumber = new BigNumber(1)
): Promise<Record<string, MarketData>> {
const exchangeFeePerUnitDAI = exchangeFees.exchangeFeeDAI.dividedBy(amount);
let enrichedMarketDataRecords = {};
for (const marketId in marketDataRecords) {
let marketData = marketDataRecords[marketId];
// enrich with values dependent on marketUnitPrice
if (marketData.marketUnitPrice.isNaN()) {
enrichedMarketDataRecords = {
...enrichedMarketDataRecords,
[marketId]: { ...marketData },
};
continue;
}
marketData = {
...marketData,
marketUnitPrice: marketData.marketUnitPrice.plus(exchangeFeePerUnitDAI),
marketUnitPriceToUnitPriceRatio: auction.approximateUnitPrice
.minus(marketData.marketUnitPrice)
.dividedBy(marketData.marketUnitPrice),
...exchangeFees,
transactionGrossProfit: calculateTransactionGrossProfit(
marketData.marketUnitPrice,
amount,
auction.approximateUnitPrice
),
};
// enrich with values dependent on fees
if (marketData.transactionGrossProfit && auction.combinedSwapFeesDAI) {
marketData = {
...marketData,
transactionNetProfit: marketData.transactionGrossProfit.minus(auction.combinedSwapFeesDAI),
};
}
// enrich with values dependent on currentDate
try {
const currentDate = await getNetworkDate(auction.network);
marketData = {
...marketData,
transactionGrossProfitDate: calculateTransactionGrossProfitDate(
auction,
marketData.marketUnitPrice,
currentDate
),
};
} catch {}
enrichedMarketDataRecords = {
...enrichedMarketDataRecords,
[marketId]: { ...marketData },
};
}
return enrichedMarketDataRecords;
};

export const enrichAuctionWithMarketDataRecords = async function (
auction: AuctionTransaction,
network: string
): Promise<AuctionTransaction> {
if (!auction.isActive || !auction.approximateUnitPrice || auction.isFinished) {
return auction;
}
try {
const collateralToCoverDebt = await calculateCollateralToCoverDebt(network, auction);
const marketUnitPrice = await getMarketPrice(network, auction.collateralSymbol, collateralToCoverDebt);
const marketUnitPriceToUnitPriceRatio = auction.approximateUnitPrice
.minus(marketUnitPrice)
.dividedBy(marketUnitPrice);
const auctionWithMarketValues = {
const exchangeFees = await getDefaultMarketFee(network);
const marketDataRecords = await getMarketDataRecords(network, auction.collateralSymbol, collateralToCoverDebt);
const enrichedMarketDataRecords = await enrichMarketDataRecordsWithValues(
auction,
marketDataRecords,
exchangeFees,
collateralToCoverDebt
);
const suggestedMarketId = await getBestMarketId(enrichedMarketDataRecords);
const marketUnitPrice = enrichedMarketDataRecords[suggestedMarketId].marketUnitPrice;
const marketUnitPriceToUnitPriceRatio =
enrichedMarketDataRecords[suggestedMarketId].marketUnitPriceToUnitPriceRatio;
const transactionGrossProfit = enrichedMarketDataRecords[suggestedMarketId].transactionGrossProfit;
const transactionGrossProfitDate = enrichedMarketDataRecords[suggestedMarketId].transactionGrossProfitDate;
const transactionNetProfit = enrichedMarketDataRecords[suggestedMarketId].transactionNetProfit;
return {
...auction,
collateralToCoverDebt,
suggestedMarketId,
marketUnitPrice,
marketUnitPriceToUnitPriceRatio,
};
const transactionGrossProfit = calculateTransactionGrossProfit(auctionWithMarketValues);
return {
...auctionWithMarketValues,
transactionGrossProfit,
transactionGrossProfitDate,
transactionNetProfit: transactionNetProfit ? transactionNetProfit : new BigNumber(NaN),
marketDataRecords,
};
} catch (error: any) {
// since it's expected that some collaterals are not tradable on some networks
Expand All @@ -111,13 +190,11 @@ export const enrichAuctionWithPriceDrop = async function (auction: Auction): Pro
const secondsTillNextPriceDrop = calculateAuctionDropTime(auctionWithParams, currentDate);
const approximateUnitPrice = calculateAuctionPrice(auctionWithParams, currentDate);
const totalPrice = auction.collateralAmount.multipliedBy(approximateUnitPrice);
const transactionGrossProfitDate = calculateTransactionGrossProfitDate(auctionWithParams, currentDate);
return {
...auctionWithParams,
secondsTillNextPriceDrop,
approximateUnitPrice,
totalPrice,
transactionGrossProfitDate,
};
} catch (error) {
return {
Expand All @@ -127,12 +204,14 @@ export const enrichAuctionWithPriceDrop = async function (auction: Auction): Pro
}
};

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

export const fetchSingleAuctionById = async function (
Expand Down Expand Up @@ -173,21 +252,21 @@ export const enrichAuction = async function (
// enrich them with price drop
const auctionWithPriceDrop = await enrichAuctionWithPriceDrop(auctionWithStatus);

// enrich them with market values
const auctionWithMarketValue = await enrichAuctionWithMarketValues(auctionWithPriceDrop, network);

// enrich with profit and fee calculation
const fees = await getApproximateTransactionFees(network);
const auctionWithFees = await enrichAuctionWithTransactionFees(auctionWithMarketValue, fees, network);
const auctionWithFees = await enrichAuctionWithTransactionFees(auctionWithPriceDrop, fees, network);

// enrich them with market data
const auctionWithMarketDataRecords = await enrichAuctionWithMarketDataRecords(auctionWithFees, network);

if (auction.debtDAI.isEqualTo(0)) {
return {
...auctionWithFees,
...auctionWithMarketDataRecords,
isFinished: true,
isActive: false,
};
}
return auctionWithFees;
return auctionWithMarketDataRecords;
};

const enrichTakeEventWithDate = async function (network: string, takeEvent: TakeEvent): Promise<TakeEvent> {
Expand Down Expand Up @@ -254,11 +333,12 @@ export const bidWithDai = async function (
export const bidWithCallee = async function (
network: string,
auction: Auction,
marketId: string,
profitAddress: string,
notifier?: Notifier
): Promise<string> {
const calleeAddress = getCalleeAddressByCollateralType(network, auction.collateralType);
const calleeData = await getCalleeData(network, auction.collateralType, profitAddress);
const calleeAddress = getCalleeAddressByCollateralType(network, auction.collateralType, marketId);
const calleeData = await getCalleeData(network, auction.collateralType, marketId, profitAddress);
const contractName = getClipperNameByCollateralType(auction.collateralType);
const contractParameters = [
convertNumberTo32Bytes(auction.index),
Expand Down
7 changes: 5 additions & 2 deletions core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ export const CHARTER_MANAGER_ADDRESS = '0x8377CD01a5834a6EaD3b7efb482f678f2092b7
const getCalleeData = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
profitAddress: string
): Promise<string> {
if (collateral.exchange.callee !== 'CurveLpTokenUniv3Callee') {
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, collateral.exchange.route);
const route = await encodeRoute(network, marketData.route);
const curveData = [CURVE_POOL_ADDRESS, CURVE_COIN_INDEX];
const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk));
const minProfit = 1;
Expand All @@ -33,6 +35,7 @@ const getCalleeData = async function (
const getMarketPrice = async function (
network: string,
_collateral: CollateralConfig,
_marketId: string,
collateralAmount: BigNumber
): Promise<BigNumber> {
// convert stETH into ETH
Expand Down
9 changes: 6 additions & 3 deletions core/src/calleeFunctions/UniswapV2CalleeDai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { getUniswapRouteAddressesBySymbol, getRegularTokenExchangeRateBySymbol }
const getCalleeData = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
profitAddress: string
): Promise<string> {
if (collateral.exchange.callee !== 'UniswapV2CalleeDai') {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee !== 'UniswapV2CalleeDai') {
throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`);
}
const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk));
Expand All @@ -19,16 +21,17 @@ const getCalleeData = async function (
profitAddress,
joinAdapterAddress,
minProfit,
await getUniswapRouteAddressesBySymbol(network, collateral.symbol),
await getUniswapRouteAddressesBySymbol(network, collateral.symbol, marketId),
]);
};

const getMarketPrice = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
amount: BigNumber
): Promise<BigNumber> {
return await getRegularTokenExchangeRateBySymbol(network, collateral.symbol, amount);
return await getRegularTokenExchangeRateBySymbol(network, collateral.symbol, marketId, amount);
};

const UniswapV2CalleeDai: CalleeFunctions = {
Expand Down
20 changes: 13 additions & 7 deletions core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import {
const getCalleeData = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
profitAddress: string
): Promise<string> {
if (collateral.exchange.callee !== 'UniswapV2LpTokenCalleeDai') {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee !== 'UniswapV2LpTokenCalleeDai') {
throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`);
}
const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk));
Expand All @@ -25,30 +27,34 @@ const getCalleeData = async function (
profitAddress,
joinAdapterAddress,
minProfit,
await getUniswapRouteAddressesBySymbol(network, collateral.exchange.token0),
await getUniswapRouteAddressesBySymbol(network, collateral.exchange.token1),
await getUniswapRouteAddressesBySymbol(network, marketData.token0, marketId),
await getUniswapRouteAddressesBySymbol(network, marketData.token1, marketId),
]);
};

const getMarketPrice = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
amount: BigNumber
): Promise<BigNumber> {
if (collateral.exchange.callee !== 'UniswapV2LpTokenCalleeDai') {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee !== 'UniswapV2LpTokenCalleeDai') {
throw new Error(`"${collateral.symbol}" is not a UniSwap LP token`);
}
const uniswapPair = await getUniswapPairBySymbols(network, collateral.exchange.token0, collateral.exchange.token1);
const uniswapPair = await getUniswapPairBySymbols(network, marketData.token0, marketData.token1);
const totalSupply = await getLpTokenTotalSupply(network, collateral.symbol);
const portionOfTheTotalSupply = amount.div(totalSupply);
const totalPriceOfToken0 = await getTotalPriceInDai(
network,
uniswapPair.reserveOf(await getUniswapTokenBySymbol(network, collateral.exchange.token0)),
uniswapPair.reserveOf(await getUniswapTokenBySymbol(network, marketData.token0)),
marketId,
portionOfTheTotalSupply
);
const totalPriceOfToken1 = await getTotalPriceInDai(
network,
uniswapPair.reserveOf(await getUniswapTokenBySymbol(network, collateral.exchange.token1)),
uniswapPair.reserveOf(await getUniswapTokenBySymbol(network, marketData.token1)),
marketId,
portionOfTheTotalSupply
);
const totalPrice = totalPriceOfToken0.plus(totalPriceOfToken1);
Expand Down
9 changes: 6 additions & 3 deletions core/src/calleeFunctions/UniswapV3Callee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import { convertCollateralToDaiUsingRoute, encodeRoute } from './helpers/uniswap
const getCalleeData = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
profitAddress: string
): Promise<string> {
if (collateral.exchange.callee !== 'UniswapV3Callee') {
const marketData = collateral.exchanges[marketId];
if (marketData?.callee !== 'UniswapV3Callee') {
throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`);
}
const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk));
const minProfit = 1;
const uniswapV3route = await encodeRoute(network, [collateral.symbol, ...collateral.exchange.route]);
const uniswapV3route = await encodeRoute(network, [collateral.symbol, ...marketData.route]);
const typesArray = ['address', 'address', 'uint256', 'bytes', 'address'];
return ethers.utils.defaultAbiCoder.encode(typesArray, [
profitAddress,
Expand All @@ -28,10 +30,11 @@ const getCalleeData = async function (
const getMarketPrice = async function (
network: string,
collateral: CollateralConfig,
marketId: string,
collateralAmount: BigNumber
): Promise<BigNumber> {
// convert collateral into DAI
const daiAmount = await convertCollateralToDaiUsingRoute(network, collateral.symbol, collateralAmount);
const daiAmount = await convertCollateralToDaiUsingRoute(network, collateral.symbol, marketId, collateralAmount);

// return price per unit
return daiAmount.dividedBy(collateralAmount);
Expand Down
Loading