diff --git a/bot/src/keepers/collateral.ts b/bot/src/keepers/collateral.ts index d5335f543..5e7a9ead3 100644 --- a/bot/src/keepers/collateral.ts +++ b/bot/src/keepers/collateral.ts @@ -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) { @@ -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` ); diff --git a/core/src/auctions.ts b/core/src/auctions.ts index 3a3b13ce4..934359d97 100644 --- a/core/src/auctions.ts +++ b/core/src/auctions.ts @@ -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'; @@ -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'; @@ -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 { +export const enrichMarketDataRecordsWithValues = async function ( + auction: AuctionTransaction, + marketDataRecords: Record, + exchangeFees: ExchangeFees, + amount: BigNumber = new BigNumber(1) +): Promise> { + 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 { 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 @@ -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 { @@ -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 { 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 ( @@ -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 { @@ -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 { - 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), diff --git a/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts b/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts index 2aa220ead..f80a2a741 100644 --- a/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts +++ b/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts @@ -10,12 +10,14 @@ export const CHARTER_MANAGER_ADDRESS = '0x8377CD01a5834a6EaD3b7efb482f678f2092b7 const getCalleeData = async function ( network: string, collateral: CollateralConfig, + marketId: string, profitAddress: string ): Promise { - 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; @@ -33,6 +35,7 @@ const getCalleeData = async function ( const getMarketPrice = async function ( network: string, _collateral: CollateralConfig, + _marketId: string, collateralAmount: BigNumber ): Promise { // convert stETH into ETH diff --git a/core/src/calleeFunctions/UniswapV2CalleeDai.ts b/core/src/calleeFunctions/UniswapV2CalleeDai.ts index 55b4ce7bb..2e3970ecb 100644 --- a/core/src/calleeFunctions/UniswapV2CalleeDai.ts +++ b/core/src/calleeFunctions/UniswapV2CalleeDai.ts @@ -7,9 +7,11 @@ import { getUniswapRouteAddressesBySymbol, getRegularTokenExchangeRateBySymbol } const getCalleeData = async function ( network: string, collateral: CollateralConfig, + marketId: string, profitAddress: string ): Promise { - 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)); @@ -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 { - return await getRegularTokenExchangeRateBySymbol(network, collateral.symbol, amount); + return await getRegularTokenExchangeRateBySymbol(network, collateral.symbol, marketId, amount); }; const UniswapV2CalleeDai: CalleeFunctions = { diff --git a/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts b/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts index fc5693fe8..b4b6bb720 100644 --- a/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts +++ b/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts @@ -13,9 +13,11 @@ import { const getCalleeData = async function ( network: string, collateral: CollateralConfig, + marketId: string, profitAddress: string ): Promise { - 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)); @@ -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 { - 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); diff --git a/core/src/calleeFunctions/UniswapV3Callee.ts b/core/src/calleeFunctions/UniswapV3Callee.ts index 27ca72f97..75d405ae1 100644 --- a/core/src/calleeFunctions/UniswapV3Callee.ts +++ b/core/src/calleeFunctions/UniswapV3Callee.ts @@ -7,14 +7,16 @@ import { convertCollateralToDaiUsingRoute, encodeRoute } from './helpers/uniswap const getCalleeData = async function ( network: string, collateral: CollateralConfig, + marketId: string, profitAddress: string ): Promise { - 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, @@ -28,10 +30,11 @@ const getCalleeData = async function ( const getMarketPrice = async function ( network: string, collateral: CollateralConfig, + marketId: string, collateralAmount: BigNumber ): Promise { // 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); diff --git a/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts b/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts index e082298a9..019a0caa5 100644 --- a/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts +++ b/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts @@ -9,6 +9,7 @@ import { convertWstethToSteth } from './helpers/wsteth'; const getCalleeData = async function ( network: string, collateral: CollateralConfig, + _marketId: string, profitAddress: string ): Promise { const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); @@ -26,9 +27,11 @@ const getCalleeData = async function ( const getMarketPrice = async function ( network: string, collateral: CollateralConfig, + marketId: string, collateralAmount: BigNumber ): Promise { - if (collateral.exchange.callee !== 'WstETHCurveUniv3Callee') { + const marketData = collateral.exchanges[marketId]; + if (marketData?.callee !== 'WstETHCurveUniv3Callee') { throw new Error(`Invalid callee used to get market price for ${collateral.ilk}`); } // convert wstETH into stETH diff --git a/core/src/calleeFunctions/helpers/uniswapV2.ts b/core/src/calleeFunctions/helpers/uniswapV2.ts index 239224572..170539ffa 100644 --- a/core/src/calleeFunctions/helpers/uniswapV2.ts +++ b/core/src/calleeFunctions/helpers/uniswapV2.ts @@ -17,24 +17,29 @@ import { getCollateralConfigBySymbol } from '../../constants/COLLATERALS'; const EXCHANGE_RATE_CACHE = 20 * 1000; -const getColleeConfig = function (collateral: CollateralConfig): RegularCalleeConfig { - if (collateral.exchange.callee === 'UniswapV2CalleeDai' || collateral.exchange.callee === 'UniswapV3Callee') { - return collateral.exchange; +const getCalleeConfig = function (collateral: CollateralConfig, marketId: string): RegularCalleeConfig { + const marketData = collateral.exchanges[marketId]; + if (marketData?.callee === 'UniswapV2CalleeDai' || marketData?.callee === 'UniswapV3Callee') { + return marketData; } throw new Error(`"${collateral.symbol}" is not an UniSwap token`); }; -export const getCompleteExchangePathBySymbol = function (symbol: string, useExchangeRoute = true) { +export const getCompleteExchangePathBySymbol = function (symbol: string, marketId: string, useExchangeRoute = true) { if (symbol === 'DAI') { // no exchange is needed for DAI -> DAI return ['DAI']; } const collateral = getCollateralConfigBySymbol(symbol); - return !useExchangeRoute ? [symbol, 'DAI'] : [symbol, ...getColleeConfig(collateral).route, 'DAI']; + return !useExchangeRoute ? [symbol, 'DAI'] : [symbol, ...getCalleeConfig(collateral, marketId).route, 'DAI']; }; -export const getUniswapRouteAddressesBySymbol = async function (network: string, symbol: string): Promise { - const completeExchangePath = getCompleteExchangePathBySymbol(symbol); +export const getUniswapRouteAddressesBySymbol = async function ( + network: string, + symbol: string, + marketId: string +): Promise { + const completeExchangePath = getCompleteExchangePathBySymbol(symbol, marketId); return await Promise.all( completeExchangePath.map(exchangePathSymbol => getTokenAddressByNetworkAndSymbol(network, exchangePathSymbol)) ); @@ -105,11 +110,12 @@ export const getLpTokenTotalSupply = async function (network: string, symbol: st export const getRegularTokenExchangeRateBySymbol = async function ( network: string, symbol: string, + marketId: string, amount: BigNumber ): Promise { const collateral = getCollateralConfigBySymbol(symbol); - getColleeConfig(collateral); // to check that the callee is supported - const completeExchangePath = getCompleteExchangePathBySymbol(symbol); + getCalleeConfig(collateral, marketId); // to check that the callee is supported + const completeExchangePath = getCompleteExchangePathBySymbol(symbol, marketId); const pairs = splitArrayIntoPairs(completeExchangePath); const uniswapPairs = await Promise.all(pairs.map(pair => getUniswapPairBySymbols(network, pair[0], pair[1]))); const exchangeToken = await getUniswapTokenBySymbol(network, symbol); @@ -125,6 +131,7 @@ export const getRegularTokenExchangeRateBySymbol = async function ( export const getTotalPriceInDai = async function ( network: string, reserve: TokenAmount, + marketId: string, portionOfTheTotalSupply: BigNumber ): Promise { if (!reserve.token.symbol) { @@ -135,6 +142,11 @@ export const getTotalPriceInDai = async function ( if (reserve.token.symbol === 'DAI') { return amountOwned; } - const tokenExchangeRate = await getRegularTokenExchangeRateBySymbol(network, reserve.token.symbol, amountOwned); + const tokenExchangeRate = await getRegularTokenExchangeRateBySymbol( + network, + reserve.token.symbol, + marketId, + amountOwned + ); return tokenExchangeRate.multipliedBy(amountOwned); }; diff --git a/core/src/calleeFunctions/helpers/uniswapV3.ts b/core/src/calleeFunctions/helpers/uniswapV3.ts index e75ba307b..e1fc9079a 100644 --- a/core/src/calleeFunctions/helpers/uniswapV3.ts +++ b/core/src/calleeFunctions/helpers/uniswapV3.ts @@ -35,14 +35,16 @@ export const encodeRoute = async function (network: string, collateralSymbols: s export const convertCollateralToDaiUsingRoute = async function ( network: string, collateralSymbol: string, + marketId: string, collateralAmount: BigNumber ): Promise { - const collateral = await getCollateralConfigBySymbol(collateralSymbol); - if (collateral.exchange.callee !== 'UniswapV3Callee') { + const collateral = getCollateralConfigBySymbol(collateralSymbol); + const marketData = collateral.exchanges[marketId]; + if (marketData?.callee !== 'UniswapV3Callee') { throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`); } const collateralIntegerAmount = collateralAmount.shiftedBy(collateral.decimals).toFixed(0); - const route = encodeRoute(network, [collateral.symbol, ...collateral.exchange.route]); + const route = encodeRoute(network, [collateral.symbol, ...marketData.route]); const uniswapV3quoterContract = await getUniswapV3quoterContract(network); const daiIntegerAmount = await uniswapV3quoterContract.callStatic.quoteExactInput(route, collateralIntegerAmount); const daiAmount = new BigNumber(daiIntegerAmount._hex).shiftedBy(-DAI_NUMBER_OF_DIGITS); diff --git a/core/src/calleeFunctions/index.ts b/core/src/calleeFunctions/index.ts index 412a0ae78..950007ba2 100644 --- a/core/src/calleeFunctions/index.ts +++ b/core/src/calleeFunctions/index.ts @@ -1,4 +1,4 @@ -import type { CalleeNames, CalleeFunctions } from '../types'; +import type { CalleeNames, CalleeFunctions, MarketData, CollateralConfig } from '../types'; import memoizee from 'memoizee'; import BigNumber from '../bignumber'; import UniswapV2CalleeDai from './UniswapV2CalleeDai'; @@ -23,27 +23,104 @@ const allCalleeFunctions: Record = { export const getCalleeData = async function ( network: string, collateralType: string, + marketId: string, profitAddress: string ): Promise { const collateral = getCollateralConfigByType(collateralType); - const calleeFuctions = allCalleeFunctions[collateral.exchange.callee]; - if (!calleeFuctions) { + const marketData = collateral.exchanges[marketId]; + if (!marketData || !marketData.callee || !allCalleeFunctions[marketData.callee]) { throw new Error(`Unsupported collateral type "${collateralType}"`); } - return await calleeFuctions.getCalleeData(network, collateral, profitAddress); + return await allCalleeFunctions[marketData.callee].getCalleeData(network, collateral, marketId, profitAddress); }; -const _getMarketPrice = async function ( +export const getMarketDataById = async function ( + network: string, + collateral: CollateralConfig, + marketId: string, + amount: BigNumber = new BigNumber('1') +): Promise { + const calleeConfig = collateral.exchanges[marketId]; + if (!allCalleeFunctions[calleeConfig?.callee]) { + throw new Error(`Unsupported callee "${calleeConfig?.callee}" for collateral symbol "${collateral.symbol}"`); + } + let marketUnitPrice: BigNumber; + try { + marketUnitPrice = await allCalleeFunctions[calleeConfig.callee].getMarketPrice( + network, + collateral, + marketId, + amount + ); + } catch (error: any) { + const errorMessage = error?.message; + marketUnitPrice = new BigNumber(NaN); + return { + ...calleeConfig, + marketUnitPrice, + errorMessage, + }; + } + return { + ...calleeConfig, + marketUnitPrice: marketUnitPrice ? marketUnitPrice : new BigNumber(NaN), + }; +}; + +export const getMarketDataRecords = async function ( network: string, collateralSymbol: string, amount: BigNumber = new BigNumber('1') -): Promise { +): Promise> { const collateral = getCollateralConfigBySymbol(collateralSymbol); - const calleeFuctions = allCalleeFunctions[collateral.exchange.callee]; - if (!calleeFuctions) { - throw new Error(`Unsupported collateral symbol "${collateralSymbol}"`); + let marketDataRecords = {}; + for (const marketId in collateral.exchanges) { + let marketData: MarketData; + try { + marketData = await getMarketDataById(network, collateral, marketId, amount); + } catch (error: any) { + marketData = { + errorMessage: error?.message, + marketUnitPrice: new BigNumber(NaN), + route: [], // dummy value: MarketData requires either a route or tokens + }; + } + marketDataRecords = { + ...marketDataRecords, + [marketId]: { + ...marketData, + }, + }; } - return await calleeFuctions.getMarketPrice(network, collateral, amount); + return marketDataRecords; +}; + +export const getBestMarketId = async function (marketDataRecords: Record): Promise { + const marketDataRecordsSorted = Object.entries(marketDataRecords); + marketDataRecordsSorted.sort((a, b) => { + // push NaNs to the end + if (a[1].marketUnitPrice.isNaN() && b[1].marketUnitPrice.isNaN()) { + return 1; + } + if (a[1].marketUnitPrice.isNaN()) { + return 1; + } + if (b[1].marketUnitPrice.isNaN()) { + return -1; + } + return a[1].marketUnitPrice.minus(b[1].marketUnitPrice).toNumber(); + }); + return marketDataRecordsSorted[0][0]; +}; + +const _getMarketPrice = async function ( + network: string, + collateralSymbol: string, + amount: BigNumber = new BigNumber('1') +): Promise { + const marketDataRecords = await getMarketDataRecords(network, collateralSymbol, amount); + const bestMarketId = await getBestMarketId(marketDataRecords); + return marketDataRecords[bestMarketId].marketUnitPrice; }; export const getMarketPrice = memoizee(_getMarketPrice, { diff --git a/core/src/calleeFunctions/rETHCurveUniv3Callee.ts b/core/src/calleeFunctions/rETHCurveUniv3Callee.ts index 9cbed0c91..04dbdb8f5 100644 --- a/core/src/calleeFunctions/rETHCurveUniv3Callee.ts +++ b/core/src/calleeFunctions/rETHCurveUniv3Callee.ts @@ -11,12 +11,14 @@ import { NULL_ADDRESS } from '../constants/UNITS'; const getCalleeData = async function ( network: string, collateral: CollateralConfig, + marketId: string, profitAddress: string ): Promise { - if (collateral.exchange.callee !== 'rETHCurveUniv3Callee') { + const marketData = collateral.exchanges[marketId]; + if (marketData?.callee !== 'rETHCurveUniv3Callee') { 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 joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'bytes', 'address']; @@ -32,6 +34,7 @@ const getCalleeData = async function ( const getMarketPrice = async function ( network: string, _collateral: CollateralConfig, + _marketId: string, collateralAmount: BigNumber ): Promise { // convert rETH into wstETH diff --git a/core/src/constants/CALLEES.ts b/core/src/constants/CALLEES.ts index e845ee9bf..f505ffdcf 100644 --- a/core/src/constants/CALLEES.ts +++ b/core/src/constants/CALLEES.ts @@ -33,12 +33,16 @@ export const getCalleesByNetworkType = function (network: string): CalleeAddress return networkCallees; }; -export const getCalleeAddressByCollateralType = function (network: string, collateralType: string): string { +export const getCalleeAddressByCollateralType = function ( + network: string, + collateralType: string, + marketId: string +): string | undefined { const networkCallees = getCalleesByNetworkType(network); const collateral = getCollateralConfigByType(collateralType); - const calleeAddress = networkCallees[collateral.exchange.callee]; - if (!calleeAddress) { + const marketData = collateral.exchanges[marketId]; + if (!marketData || !networkCallees[marketData.callee]) { throw new Error(`No callee address found for the "${collateralType}" collateral on "${network}" network`); } - return calleeAddress; + return networkCallees[marketData.callee]; }; diff --git a/core/src/constants/COLLATERALS.ts b/core/src/constants/COLLATERALS.ts index 0ae0d9214..db9f29795 100644 --- a/core/src/constants/COLLATERALS.ts +++ b/core/src/constants/COLLATERALS.ts @@ -21,9 +21,15 @@ const COLLATERALS: Record = { ilk: 'AAVE-A', symbol: 'AAVE', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -32,9 +38,15 @@ const COLLATERALS: Record = { ilk: 'BAL-A', symbol: 'BAL', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -43,9 +55,15 @@ const COLLATERALS: Record = { ilk: 'BAT-A', symbol: 'BAT', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -54,9 +72,15 @@ const COLLATERALS: Record = { ilk: 'COMP-A', symbol: 'COMP', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -65,9 +89,15 @@ const COLLATERALS: Record = { ilk: 'ETH-A', symbol: 'ETH', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: [], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: [], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: [], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -76,9 +106,15 @@ const COLLATERALS: Record = { ilk: 'ETH-B', symbol: 'ETH', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: [], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: [], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: [], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -87,9 +123,15 @@ const COLLATERALS: Record = { ilk: 'ETH-C', symbol: 'ETH', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: [], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: [], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: [], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -98,9 +140,15 @@ const COLLATERALS: Record = { ilk: 'GUSD-A', symbol: 'GUSD', decimals: 2, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITHOUT_NEXT_PRICE, }, @@ -109,9 +157,15 @@ const COLLATERALS: Record = { ilk: 'KNC-A', symbol: 'KNC', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -120,9 +174,15 @@ const COLLATERALS: Record = { ilk: 'LINK-A', symbol: 'LINK', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -131,9 +191,15 @@ const COLLATERALS: Record = { ilk: 'LRC-A', symbol: 'LRC', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -142,9 +208,15 @@ const COLLATERALS: Record = { ilk: 'MANA-A', symbol: 'MANA', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -153,9 +225,15 @@ const COLLATERALS: Record = { ilk: 'PAXUSD-A', symbol: 'PAXUSD', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITHOUT_NEXT_PRICE, @@ -165,9 +243,15 @@ const COLLATERALS: Record = { ilk: 'RENBTC-A', symbol: 'RENBTC', decimals: 8, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -176,9 +260,15 @@ const COLLATERALS: Record = { ilk: 'TUSD-A', symbol: 'TUSD', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITHOUT_NEXT_PRICE, }, @@ -187,9 +277,15 @@ const COLLATERALS: Record = { ilk: 'UNI-A', symbol: 'UNI', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -198,9 +294,15 @@ const COLLATERALS: Record = { ilk: 'USDC-A', symbol: 'USDC', decimals: 6, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITHOUT_NEXT_PRICE, }, @@ -209,9 +311,15 @@ const COLLATERALS: Record = { ilk: 'USDC-B', symbol: 'USDC', decimals: 6, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITHOUT_NEXT_PRICE, }, @@ -220,9 +328,15 @@ const COLLATERALS: Record = { ilk: 'USDT-A', symbol: 'USDT', decimals: 6, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -231,9 +345,15 @@ const COLLATERALS: Record = { ilk: 'WBTC-A', symbol: 'WBTC', decimals: 8, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -242,9 +362,15 @@ const COLLATERALS: Record = { ilk: 'WBTC-B', symbol: 'WBTC', decimals: 8, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -253,9 +379,15 @@ const COLLATERALS: Record = { ilk: 'WBTC-C', symbol: 'WBTC', decimals: 8, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -264,9 +396,15 @@ const COLLATERALS: Record = { ilk: 'YFI-A', symbol: 'YFI', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -275,9 +413,15 @@ const COLLATERALS: Record = { ilk: 'ZRX-A', symbol: 'ZRX', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -286,9 +430,15 @@ const COLLATERALS: Record = { ilk: 'MATIC-A', symbol: 'MATIC', decimals: 18, - exchange: { - callee: 'UniswapV3Callee', - route: ['ETH'], + exchanges: { + 'Uniswap V3': { + callee: 'UniswapV3Callee', + route: ['ETH'], + }, + 'Uniswap V2': { + callee: 'UniswapV2CalleeDai', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -297,9 +447,11 @@ const COLLATERALS: Record = { ilk: 'WSTETH-A', symbol: 'WSTETH', decimals: 18, - exchange: { - callee: 'WstETHCurveUniv3Callee', - route: [], + exchanges: { + 'Curve wstETH V3': { + callee: 'WstETHCurveUniv3Callee', + route: [], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -308,9 +460,11 @@ const COLLATERALS: Record = { ilk: 'WSTETH-B', symbol: 'WSTETH', decimals: 18, - exchange: { - callee: 'WstETHCurveUniv3Callee', - route: [], + exchanges: { + 'Curve wstETH V3': { + callee: 'WstETHCurveUniv3Callee', + route: [], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -319,9 +473,11 @@ const COLLATERALS: Record = { ilk: 'CRVV1ETHSTETH-A', symbol: 'CRVV1ETHSTETH', decimals: 18, - exchange: { - callee: 'CurveLpTokenUniv3Callee', - route: ['ETH'], + exchanges: { + 'Curve Token V3': { + callee: 'CurveLpTokenUniv3Callee', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -330,10 +486,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2DAIETH-A', symbol: 'UNIV2DAIETH', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'DAI', - token1: 'ETH', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'DAI', + token1: 'ETH', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -342,10 +500,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2USDCETH-A', symbol: 'UNIV2USDCETH', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'USDC', - token1: 'ETH', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'USDC', + token1: 'ETH', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -354,10 +514,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2ETHUSDT-A', symbol: 'UNIV2ETHUSDT', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'ETH', - token1: 'USDT', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'ETH', + token1: 'USDT', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -366,10 +528,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2WBTCDAI-A', symbol: 'UNIV2WBTCDAI', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'WBTC', - token1: 'DAI', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'WBTC', + token1: 'DAI', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -378,10 +542,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2WBTCETH-A', symbol: 'UNIV2WBTCETH', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'WBTC', - token1: 'ETH', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'WBTC', + token1: 'ETH', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -390,10 +556,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2LINKETH-A', symbol: 'UNIV2LINKETH', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'LINK', - token1: 'ETH', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'LINK', + token1: 'ETH', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -402,10 +570,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2UNIETH-A', symbol: 'UNIV2UNIETH', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'UNI', - token1: 'ETH', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'UNI', + token1: 'ETH', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -414,10 +584,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2AAVEETH-A', symbol: 'UNIV2AAVEETH', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'AAVE', - token1: 'ETH', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'AAVE', + token1: 'ETH', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -426,10 +598,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2DAIUSDT-A', symbol: 'UNIV2DAIUSDT', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'DAI', - token1: 'USDT', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'DAI', + token1: 'USDT', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -438,10 +612,12 @@ const COLLATERALS: Record = { ilk: 'UNIV2DAIUSDC-A', symbol: 'UNIV2DAIUSDC', decimals: 18, - exchange: { - callee: 'UniswapV2LpTokenCalleeDai', - token0: 'DAI', - token1: 'USDC', + exchanges: { + 'Uniswap Token V2': { + callee: 'UniswapV2LpTokenCalleeDai', + token0: 'DAI', + token1: 'USDC', + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, @@ -450,9 +626,11 @@ const COLLATERALS: Record = { ilk: 'RETH-A', symbol: 'RETH', decimals: 18, - exchange: { - callee: 'rETHCurveUniv3Callee', - route: ['ETH'], + exchanges: { + 'Curve rETH V3': { + callee: 'rETHCurveUniv3Callee', + route: ['ETH'], + }, }, oracle: CONFIG_WITH_NEXT_PRICE, }, diff --git a/core/src/fees.ts b/core/src/fees.ts index 796ec2716..4654bdb86 100644 --- a/core/src/fees.ts +++ b/core/src/fees.ts @@ -1,10 +1,16 @@ -import type { Auction, AuctionTransaction, TransactionFees, VaultTransactionFees } from './types'; +import type { Auction, AuctionTransaction, TransactionFees, VaultTransactionFees, ExchangeFees } from './types'; import BigNumber from './bignumber'; import { getMarketPrice } from './calleeFunctions'; import { getGasPriceForUI } from './gas'; import getSigner from './signer'; import { getCollateralAuthorizationStatus, getWalletAuthorizationStatus } from './authorizations'; +export const BID_TRANSACTION_GAS_LIMIT = 145438; +export const SWAP_TRANSACTION_GAS_LIMIT = 722651; +export const AUTH_TRANSACTION_GAS_LIMIT = 48356; +export const RESTART_TRANSACTION_GAS_LIMIT = 209182; +export const LIQUIDATION_TRANSACTION_GAS_LIMIT = 446658; + export const convertETHtoDAI = async function (network: string, eth: BigNumber): Promise { const exchangeRate = await getMarketPrice(network, 'ETH'); return eth.multipliedBy(exchangeRate); @@ -15,10 +21,10 @@ export const getApproximateTransactionFees = async function (network: string): P // TODO: figure out a way to properly estimate gas // for each transaction when no wallet is connected - const bidTransactionFeeETH = gasPrice.multipliedBy(145438); - const swapTransactionFeeETH = gasPrice.multipliedBy(722651); - const authTransactionFeeETH = gasPrice.multipliedBy(48356); - const restartTransactionFeeETH = gasPrice.multipliedBy(209182); + const bidTransactionFeeETH = gasPrice.multipliedBy(BID_TRANSACTION_GAS_LIMIT); + const swapTransactionFeeETH = gasPrice.multipliedBy(SWAP_TRANSACTION_GAS_LIMIT); + const authTransactionFeeETH = gasPrice.multipliedBy(AUTH_TRANSACTION_GAS_LIMIT); + const restartTransactionFeeETH = gasPrice.multipliedBy(RESTART_TRANSACTION_GAS_LIMIT); return { bidTransactionFeeETH, @@ -32,9 +38,18 @@ export const getApproximateTransactionFees = async function (network: string): P }; }; +export const getDefaultMarketFee = async function (network: string): Promise { + const gasPrice = await getGasPriceForUI(network); + const exchangeFeeETH = gasPrice.multipliedBy(SWAP_TRANSACTION_GAS_LIMIT - BID_TRANSACTION_GAS_LIMIT); + return { + exchangeFeeETH, + exchangeFeeDAI: await convertETHtoDAI(network, exchangeFeeETH), + }; +}; + export const getApproximateLiquidationFees = async function (network: string): Promise { const gasPrice = await getGasPriceForUI(network); - const transactionFeeLiquidationEth = gasPrice.multipliedBy(446658); + const transactionFeeLiquidationEth = gasPrice.multipliedBy(LIQUIDATION_TRANSACTION_GAS_LIMIT); return { transactionFeeLiquidationEth, transactionFeeLiquidationDai: await convertETHtoDAI(network, transactionFeeLiquidationEth), @@ -82,8 +97,6 @@ export const enrichAuctionWithTransactionFees = async function ( combinedSwapFeesETH: combinedSwapFeesETH, combinedSwapFeesDAI: combinedSwapFeesDAI, } as AuctionTransaction; - if (auction.transactionGrossProfit && fees.swapTransactionFeeDAI) { - auctionTransaction.transactionNetProfit = auction.transactionGrossProfit.minus(combinedSwapFeesDAI); - } + return auctionTransaction; }; diff --git a/core/src/price.ts b/core/src/price.ts index e8a206681..b960e9e70 100644 --- a/core/src/price.ts +++ b/core/src/price.ts @@ -42,12 +42,16 @@ export const calculateAuctionDropTime = function (auction: Auction, currentDate: return auction.secondsBetweenPriceDrops - (elapsedTime % auction.secondsBetweenPriceDrops); }; -export const calculateTransactionGrossProfit = function (auction: Auction): BigNumber { - if (!auction.marketUnitPrice) { +export const calculateTransactionGrossProfit = function ( + marketUnitPrice: BigNumber, + collateralToCoverDebt: BigNumber, + approximateUnitPrice: BigNumber +): BigNumber { + if (!marketUnitPrice) { return new BigNumber(0); } - const totalDebtMarketPrice = auction.collateralToCoverDebt.multipliedBy(auction.marketUnitPrice); - const totalDebtPrice = auction.collateralToCoverDebt.multipliedBy(auction.approximateUnitPrice); + const totalDebtMarketPrice = collateralToCoverDebt.multipliedBy(marketUnitPrice); + const totalDebtPrice = collateralToCoverDebt.multipliedBy(approximateUnitPrice); return totalDebtMarketPrice.minus(totalDebtPrice); }; @@ -94,11 +98,15 @@ export const calculateTransactionCollateralOutcome = function ( return potentialOutcomeCollateralAmount; }; -export const calculateTransactionGrossProfitDate = function (auction: Auction, currentDate: Date): Date | undefined { +export const calculateTransactionGrossProfitDate = function ( + auction: Auction, + marketUnitPrice: BigNumber | undefined, + currentDate: Date +): Date | undefined { if ( auction.secondsBetweenPriceDrops === undefined || auction.secondsTillNextPriceDrop === undefined || - auction.marketUnitPrice === undefined || + marketUnitPrice === undefined || auction.priceDropRatio === undefined || !auction.isActive || auction.isFinished @@ -106,14 +114,14 @@ export const calculateTransactionGrossProfitDate = function (auction: Auction, c return undefined; } - const isAlreadyProfitable = auction.approximateUnitPrice.isLessThan(auction.marketUnitPrice); + const isAlreadyProfitable = auction.approximateUnitPrice.isLessThan(marketUnitPrice); if (isAlreadyProfitable) { return undefined; } const stepNumber = Math.ceil( - Math.log(auction.marketUnitPrice.dividedBy(auction.approximateUnitPrice).toNumber()) / + Math.log(marketUnitPrice.dividedBy(auction.approximateUnitPrice).toNumber()) / Math.log(auction.priceDropRatio.toNumber()) ); diff --git a/core/src/types.ts b/core/src/types.ts index 8ee9cd067..160d10df8 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -20,6 +20,8 @@ export declare interface AuctionInitialInfo { isActive: boolean; isFinished: boolean; isRestarting: boolean; + suggestedMarketId?: string; + marketDataRecords?: Record; marketUnitPrice?: BigNumber; marketUnitPriceToUnitPriceRatio?: BigNumber; transactionGrossProfit?: BigNumber; @@ -92,6 +94,26 @@ export declare interface UniswapV2LpTokenCalleeConfig { token1: string; } +export declare interface ExchangeFees { + exchangeFeeETH: BigNumber; + exchangeFeeDAI: BigNumber; +} + +declare interface MarketDataBase extends Partial { + marketUnitPrice: BigNumber; + marketUnitPriceToUnitPriceRatio?: BigNumber; + transactionGrossProfit?: BigNumber; + transactionGrossProfitDate?: Date; + transactionNetProfit?: BigNumber; + errorMessage?: any; +} + +declare interface MarketDataRegular extends MarketDataBase, Omit {} + +declare interface MarketDataUniswapV2LpToken extends MarketDataBase, Omit {} + +export type MarketData = MarketDataRegular | MarketDataUniswapV2LpToken; + export declare interface ValueSlotAddressAndOffset { slot: string; offset: number; @@ -102,7 +124,7 @@ export declare interface CollateralConfig { ilk: string; symbol: string; decimals: number; - exchange: RegularCalleeConfig | UniswapV2LpTokenCalleeConfig; + exchanges: Record; oracle: CollateralPriceSourceConfig; } @@ -146,8 +168,18 @@ export declare interface CalleeAddresses { export type CalleeNames = keyof CalleeAddresses; export declare interface CalleeFunctions { - getCalleeData: (network: string, collateral: CollateralConfig, profitAddress: string) => Promise; - getMarketPrice: (network: string, collateral: CollateralConfig, amount: BigNumber) => Promise; + getCalleeData: ( + network: string, + collateral: CollateralConfig, + marketId: string, + profitAddress: string + ) => Promise; + getMarketPrice: ( + network: string, + collateral: CollateralConfig, + marketId: string, + amount: BigNumber + ) => Promise; } export declare interface MakerParams { diff --git a/frontend/components/auction/collateral/CollateralAuctionSwapTransaction.vue b/frontend/components/auction/collateral/CollateralAuctionSwapTransaction.vue index 8d4fdaea5..eed2a786b 100644 --- a/frontend/components/auction/collateral/CollateralAuctionSwapTransaction.vue +++ b/frontend/components/auction/collateral/CollateralAuctionSwapTransaction.vue @@ -84,7 +84,13 @@ :is-collateral-authed="isWalletCollateralAuthorizationCheckPassed" :fees="fees" :transaction-gross-profit="auctionTransaction.transactionGrossProfit" - @execute="$emit('execute', { id: auctionTransaction.id, alternativeDestinationAddress: $event })" + @execute=" + $emit('execute', { + id: auctionTransaction.id, + marketId: auctionTransaction.suggestedMarketId, + alternativeDestinationAddress: $event, + }) + " /> diff --git a/frontend/containers/AuctionsContainer.vue b/frontend/containers/AuctionsContainer.vue index 68d59eec8..9772d1c2b 100644 --- a/frontend/containers/AuctionsContainer.vue +++ b/frontend/containers/AuctionsContainer.vue @@ -39,8 +39,10 @@