From 2de8698c72bbd1606bfaea490c6ac4ef18bcfaa9 Mon Sep 17 00:00:00 2001 From: Valia Fetisov Date: Thu, 27 Oct 2022 17:32:46 +0200 Subject: [PATCH 1/3] refactoring --- core/helpers/hardhat/overwrites.ts | 2 +- .../configs/onboardNewCollateral.ts | 6 +- core/src/constants/COLLATERALS.ts | 116 ++++++------ core/src/helpers/createStructCoder.ts | 4 +- core/src/oracles.ts | 172 +++++------------- core/src/types.ts | 26 +-- core/src/vaults.ts | 11 +- core/test/vault-test.ts | 19 +- 8 files changed, 131 insertions(+), 225 deletions(-) diff --git a/core/helpers/hardhat/overwrites.ts b/core/helpers/hardhat/overwrites.ts index af47da0af..2faee78a6 100644 --- a/core/helpers/hardhat/overwrites.ts +++ b/core/helpers/hardhat/overwrites.ts @@ -11,7 +11,7 @@ export const overwriteCurrentOraclePrice = async (network: string, collateralTyp const collateralConfig = getCollateralConfigByType(collateralType); const oracleAddress = await getOracleAddressByCollateralType(network, collateralType); const amoutInteger = amount.shiftedBy(DAI_NUMBER_OF_DIGITS).toFixed(); - const valueWithValidity = createStructCoder().encode(['uint128', 'uint128'], ['1', amoutInteger]); + const valueWithValidity = createStructCoder().encode(['uint128', 'uint128'], [amoutInteger, '1']); await overwriteUintValueInAddress( oracleAddress, collateralConfig.oracle.currentPriceSlotAddress, diff --git a/core/simulations/configs/onboardNewCollateral.ts b/core/simulations/configs/onboardNewCollateral.ts index 0764b68f4..f974c5607 100644 --- a/core/simulations/configs/onboardNewCollateral.ts +++ b/core/simulations/configs/onboardNewCollateral.ts @@ -10,7 +10,7 @@ import createVaultWithCollateral, { } from '../helpers/createVaultWithCollateral'; import deploySpell, { getAllSpellNames } from '../helpers/deploySpell'; import executeSpell from '../helpers/executeSpell'; -import { getCurrentOraclePriceByCollateralType } from '../../src/oracles'; +import { getOsmPrices } from '../../src/oracles'; import { overwriteCurrentOraclePrice } from '../../helpers/hardhat/overwrites'; import promptToSelectOneOption from '../helpers/promptToSelectOneOption'; @@ -52,8 +52,8 @@ const simulation: Simulation = { ); // overwrite oracle price await overwriteCurrentOraclePrice(TEST_NETWORK, collateralType, new BigNumber(1000)); - const oraclePrice = await getCurrentOraclePriceByCollateralType(TEST_NETWORK, collateralType); - console.info(`New ${collateralType} oracle price is ${oraclePrice.toFixed()} DAI`); + const oraclePrices = await getOsmPrices(TEST_NETWORK, collateralType); + console.info(`New ${collateralType} oracle price is ${oraclePrices.currentUnitPrice.toFixed()} DAI`); // create and liquidate vault const collateralOwned = await calculateMinCollateralAmountToOpenVault(collateralType); const vaultId = await createVaultWithCollateral(collateralType, collateralOwned); diff --git a/core/src/constants/COLLATERALS.ts b/core/src/constants/COLLATERALS.ts index 12fd8dc31..0c1082ca9 100644 --- a/core/src/constants/COLLATERALS.ts +++ b/core/src/constants/COLLATERALS.ts @@ -1,19 +1,27 @@ -import type { CollateralConfig, OracleCurrentAndNextPrices, OracleCurrentPriceOnly } from '../types'; +import type { CollateralConfig, ValueExtractionConfig } from '../types'; -export const CONFIG_WITH_NEXT_PRICE: OracleCurrentAndNextPrices = { - type: 'CurrentAndNextPrice', - currentPriceSlotAddress: '0x3', - nextPriceSlotAddress: '0x4', - hasDelay: true, - slotPriceValueBeginsAtPosition: 34, -}; -export const CONFIG_WITHOUT_NEXT_PRICE: OracleCurrentPriceOnly = { - type: 'CurrentPriceOnly', - currentPriceSlotAddress: '0x2', - hasDelay: false, - currentPriceValiditySlotAndOffset: { slot: '0x1', offset: 25 }, - slotPriceValueBeginsAtPosition: 0, -}; +const DEFAULT_ORACLE_CONFIG: ValueExtractionConfig[] = [ + { + slotAddress: '0x3', + format: ['uint128 isCurrentUnitPriceValid', 'uint128 currentUnitPrice'], + }, + { + slotAddress: '0x4', + format: ['uint128 isNextUnitPriceValid', 'uint128 nextUnitPrice'], + }, +]; +const STABLECOIN_ORACLE_CONFIG: ValueExtractionConfig[] = [ + { + slotAddress: '0x1', + format: ['bool isCurrentUnitPriceValid'], + wordSize: 32, + }, + { + slotAddress: '0x2', + format: ['uint128 currentUnitPrice'], + wordSize: 32, + }, +]; const COLLATERALS: Record = { 'AAVE-A': { @@ -25,7 +33,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'BAL-A': { title: 'Balancer', @@ -36,7 +44,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'BAT-A': { title: 'Basic Attention Token', @@ -47,7 +55,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'COMP-A': { title: 'Compound', @@ -58,7 +66,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'ETH-A': { title: 'Ether', @@ -69,7 +77,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: [], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'ETH-B': { title: 'Ether', @@ -81,7 +89,7 @@ const COLLATERALS: Record = { route: [], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'ETH-C': { title: 'Ether', @@ -92,7 +100,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: [], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'GUSD-A': { title: 'Gemini Dollar', @@ -103,7 +111,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: STABLECOIN_ORACLE_CONFIG, }, 'KNC-A': { title: 'Kyber Network Crystal', @@ -114,7 +122,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'LINK-A': { title: 'Chainlink', @@ -125,7 +133,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'LRC-A': { title: 'Loopring', @@ -136,7 +144,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'MANA-A': { title: 'Decentraland', @@ -147,7 +155,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'PAXUSD-A': { title: 'Paxos Standard', @@ -159,7 +167,7 @@ const COLLATERALS: Record = { route: ['ETH'], }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: STABLECOIN_ORACLE_CONFIG, }, 'RENBTC-A': { title: 'renBTC', @@ -170,7 +178,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'TUSD-A': { title: 'True USD', @@ -181,7 +189,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: STABLECOIN_ORACLE_CONFIG, }, 'UNI-A': { title: 'Uniswap', @@ -192,7 +200,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'USDC-A': { title: 'USD Coin', @@ -203,7 +211,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: STABLECOIN_ORACLE_CONFIG, }, 'USDC-B': { title: 'USD Coin', @@ -214,7 +222,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: STABLECOIN_ORACLE_CONFIG, }, 'USDT-A': { title: 'Tether USD', @@ -225,7 +233,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'WBTC-A': { title: 'Wrapped Bitcoin', @@ -236,7 +244,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'WBTC-B': { title: 'Wrapped BTC', @@ -247,7 +255,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'WBTC-C': { title: 'Wrapped BTC', @@ -258,7 +266,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'YFI-A': { title: 'yearn.finance', @@ -269,7 +277,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'ZRX-A': { title: '0x', @@ -280,7 +288,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'MATIC-A': { title: 'Matic', @@ -291,7 +299,7 @@ const COLLATERALS: Record = { callee: 'UniswapV3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'WSTETH-A': { title: 'Lido wstETH', @@ -302,7 +310,7 @@ const COLLATERALS: Record = { callee: 'WstETHCurveUniv3Callee', route: [], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'WSTETH-B': { title: 'Lido wstETH', @@ -313,7 +321,7 @@ const COLLATERALS: Record = { callee: 'WstETHCurveUniv3Callee', route: [], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'CRVV1ETHSTETH-A': { title: 'Curve stETH', @@ -324,7 +332,7 @@ const COLLATERALS: Record = { callee: 'CurveLpTokenUniv3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2DAIETH-A': { title: 'UNIV2DAIETH LP', @@ -336,7 +344,7 @@ const COLLATERALS: Record = { token0: 'DAI', token1: 'ETH', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2USDCETH-A': { title: 'UNIV2USDCETH LP', @@ -348,7 +356,7 @@ const COLLATERALS: Record = { token0: 'USDC', token1: 'ETH', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2ETHUSDT-A': { title: 'UNIV2ETHUSDT LP', @@ -360,7 +368,7 @@ const COLLATERALS: Record = { token0: 'ETH', token1: 'USDT', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2WBTCDAI-A': { title: 'UNIV2WBTCDAI LP', @@ -372,7 +380,7 @@ const COLLATERALS: Record = { token0: 'WBTC', token1: 'DAI', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2WBTCETH-A': { title: 'UNIV2WBTCETH LP', @@ -384,7 +392,7 @@ const COLLATERALS: Record = { token0: 'WBTC', token1: 'ETH', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2LINKETH-A': { title: 'UNIV2LINKETH LP', @@ -396,7 +404,7 @@ const COLLATERALS: Record = { token0: 'LINK', token1: 'ETH', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2UNIETH-A': { title: 'UNIV2UNIETH LP', @@ -408,7 +416,7 @@ const COLLATERALS: Record = { token0: 'UNI', token1: 'ETH', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2AAVEETH-A': { title: 'UNIV2AAVEETH LP', @@ -420,7 +428,7 @@ const COLLATERALS: Record = { token0: 'AAVE', token1: 'ETH', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2DAIUSDT-A': { title: 'UNIV2DAIUSDT LP', @@ -432,7 +440,7 @@ const COLLATERALS: Record = { token0: 'DAI', token1: 'USDT', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'UNIV2DAIUSDC-A': { title: 'UNIV2DAIUSDC LP', @@ -444,7 +452,7 @@ const COLLATERALS: Record = { token0: 'DAI', token1: 'USDC', }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, 'RETH-A': { title: 'Rocket Pool ETH', @@ -455,7 +463,7 @@ const COLLATERALS: Record = { callee: 'rETHCurveUniv3Callee', route: ['ETH'], }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: DEFAULT_ORACLE_CONFIG, }, }; diff --git a/core/src/helpers/createStructCoder.ts b/core/src/helpers/createStructCoder.ts index a660e041b..8a267a403 100644 --- a/core/src/helpers/createStructCoder.ts +++ b/core/src/helpers/createStructCoder.ts @@ -1,8 +1,8 @@ import { ethers } from 'ethers'; -const createStructCoder = function (length = 16) { +const createStructCoder = function (wordSize = 16) { const coder = new ethers.utils.AbiCoder(); - coder._getWordSize = () => length; + coder._getWordSize = () => wordSize; return coder; }; diff --git a/core/src/oracles.ts b/core/src/oracles.ts index 47cc79941..8eee17e34 100644 --- a/core/src/oracles.ts +++ b/core/src/oracles.ts @@ -1,153 +1,71 @@ +import { ethers } from 'ethers'; +import memoizee from 'memoizee'; import { getContractAddressByName, getContractInterfaceByName } from './contracts'; import getProvider from './provider'; -import { - CollateralConfig, - CollateralPriceSourceConfig, - OraclePrices, - OracleCurrentPriceOnly, - OracleCurrentAndNextPrices, - CollateralType, -} from './types'; +import { OraclePrices, ValueExtractionConfig } from './types'; import BigNumber from './bignumber'; -import { ethers } from 'ethers'; import { DAI_NUMBER_OF_DIGITS } from './constants/UNITS'; -import memoizee from 'memoizee'; import { getCollateralConfigByType } from './constants/COLLATERALS'; +import createStructCoder from './helpers/createStructCoder'; -const CACHE_EXPIRY_MS = 60 * 1000; +const CACHE_EXPIRY_MS = 30 * 1000; export const getOracleAddressByCollateralType = async function (network: string, collateralType: string) { const collateralConfig = getCollateralConfigByType(collateralType); return await getContractAddressByName(network, `PIP_${collateralConfig.symbol}`); }; -const getOraclePriceSameSlotValidity = async ( - slot: string, - slotPriceValueBeginsAtPosition: number, - provider: ethers.providers.JsonRpcProvider, - oracleAddress: string -) => { - /** - * Fetch the price by direct memory access (via slot address) - * The price is stored in the same slot as a validity marker: - * - split the received value into validity marker part and price part - * If the price is valid - return it, otherwise provide a NaN - **/ - const priceAndValidityHex = await provider.getStorageAt(oracleAddress, slot); - const isPriceValid = priceAndValidityHex.substring(0, slotPriceValueBeginsAtPosition); - if (parseInt(isPriceValid, 16) === 1) { - return new BigNumber(`0x${priceAndValidityHex.substring(slotPriceValueBeginsAtPosition)}`).shiftedBy( - -DAI_NUMBER_OF_DIGITS - ); +const getNextOraclePriceChange = async (network: string, collateralType: string) => { + const provider = await getProvider(network); + const oracleAddress = await getOracleAddressByCollateralType(network, collateralType); + const osmContractInterface = await getContractInterfaceByName('OSM'); + const osmContract = new ethers.Contract(oracleAddress, osmContractInterface, provider); + const lastPriceUpdateTimestampInSeconds = parseInt((await osmContract.zzz())._hex, 16); + if (!lastPriceUpdateTimestampInSeconds) { + new Date(NaN); } - return new BigNumber(NaN); -}; - -const getNextOraclePrice = async ( - oracle: CollateralPriceSourceConfig, - provider: ethers.providers.JsonRpcProvider, - oracleAddress: string -): Promise => { - // Determine if the next price can be fetched from the contract. - if (oracle.type !== 'CurrentAndNextPrice') { - return new BigNumber(NaN); + const priceUpdateFrequencyInSeconds = await osmContract.hop(); + if (!priceUpdateFrequencyInSeconds) { + new Date(NaN); } - return await getOraclePriceSameSlotValidity( - oracle.nextPriceSlotAddress, - oracle.slotPriceValueBeginsAtPosition, - provider, - oracleAddress - ); + return new Date((lastPriceUpdateTimestampInSeconds + priceUpdateFrequencyInSeconds) * 1000); }; -const currentPriceExtractors: Record = { - CurrentPriceOnly: async ( - oracle: OracleCurrentPriceOnly, - provider: ethers.providers.JsonRpcProvider, - oracleAddress: string - ) => { - /** - * Get current price from the contract via direct memory access (slot address) - * The price is stored in separate slot as a validity marker: - * - the marker might be stored in the same slot as some other irrelevent value - * - extract the byte that contains validity marker only - * If the price is valid - return it, otherwise provide a NaN - **/ - const currentPriceFeed = await provider.getStorageAt(oracleAddress, oracle.currentPriceSlotAddress); - const slotPriceValueBeginsAtPosition = oracle.slotPriceValueBeginsAtPosition; - const priceValiditySlotValue = await provider.getStorageAt( - oracleAddress, - oracle.currentPriceValiditySlotAndOffset.slot - ); - const isPriceValid = - parseInt(priceValiditySlotValue[oracle.currentPriceValiditySlotAndOffset.offset], 16) === 1; - return isPriceValid - ? new BigNumber(`0x${currentPriceFeed.substring(slotPriceValueBeginsAtPosition)}`).shiftedBy( - -DAI_NUMBER_OF_DIGITS - ) - : new BigNumber(NaN); - }, - CurrentAndNextPrice: async ( - oracle: OracleCurrentAndNextPrices, - provider: ethers.providers.JsonRpcProvider, - oracleAddress: string - ) => { - return await getOraclePriceSameSlotValidity( - oracle.currentPriceSlotAddress, - oracle.slotPriceValueBeginsAtPosition, - provider, - oracleAddress - ); - }, -}; -export const getCurrentOraclePrice = async ( - oracle: CollateralPriceSourceConfig, - provider: ethers.providers.JsonRpcProvider, - oracleAddress: string -) => { - return currentPriceExtractors[oracle.type](oracle, provider, oracleAddress); -}; - -const getNextOraclePriceChange = async ( - collateralConfig: CollateralConfig, - provider: ethers.providers.JsonRpcProvider, - oracleAddress: string -) => { - let nextPriceChange: Date = new Date(NaN); - if (collateralConfig.oracle.hasDelay) { - const osmContractInterface = await getContractInterfaceByName('OSM'); - const osmContract = new ethers.Contract(oracleAddress, osmContractInterface, provider); - const lastPriceUpdateAsHex = (await osmContract.zzz())._hex; - const priceUpdateFrequencyInSeconds = await osmContract.hop(); - const lastPriceUpdateTimestampInSeconds = new BigNumber(lastPriceUpdateAsHex).toNumber(); - nextPriceChange = new Date((lastPriceUpdateTimestampInSeconds + priceUpdateFrequencyInSeconds) * 1000); +const extractValuesFromAddress = async function ( + network: string, + contractAddress: string, + configs: ValueExtractionConfig[] +) { + const provider = await getProvider(network); + let values = {} as any; + for (const config of configs) { + const hexValues = await provider.getStorageAt(contractAddress, config.slotAddress); + values = { + ...values, + ...createStructCoder(config.wordSize).decode(config.format, hexValues), + }; } - return nextPriceChange; + return values; }; -export const getCurrentOraclePriceByCollateralType = async function (network: string, collateralType: string) { +const _getOsmPrices = async (network: string, collateralType: string): Promise => { const collateralConfig = getCollateralConfigByType(collateralType); - const provider = await getProvider(network); const oracleAddress = await getOracleAddressByCollateralType(network, collateralType); - const oraclePrice = await getCurrentOraclePrice(collateralConfig.oracle, provider, oracleAddress); - return oraclePrice; -}; - -const _getOsmPrices = async ( - network: string, - oracleAddress: string, - collateralType: CollateralType -): Promise => { - const provider = await getProvider(network); - const collateralConfig = getCollateralConfigByType(collateralType); + const prices = await extractValuesFromAddress(network, oracleAddress, collateralConfig.oracle); - const nextUnitCollateralPrice = await getNextOraclePrice(collateralConfig.oracle, provider, oracleAddress); - const currentUnitCollateralPrice = await getCurrentOraclePrice(collateralConfig.oracle, provider, oracleAddress); - const nextPriceChange = await getNextOraclePriceChange(collateralConfig, provider, oracleAddress); + const currentUnitPrice = prices.isCurrentUnitPriceValid + ? new BigNumber(prices.currentUnitPrice?._hex).shiftedBy(-DAI_NUMBER_OF_DIGITS) + : new BigNumber(NaN); + const nextUnitPrice = prices.isNextUnitPriceValid + ? new BigNumber(prices.nextUnitPrice?._hex).shiftedBy(-DAI_NUMBER_OF_DIGITS) + : new BigNumber(NaN); + const nextPriceChange = !nextUnitPrice.isNaN() + ? await getNextOraclePriceChange(network, collateralType) + : new Date(NaN); return { - currentUnitPrice: currentUnitCollateralPrice, - nextUnitPrice: nextUnitCollateralPrice, + currentUnitPrice, + nextUnitPrice, nextPriceChange, }; }; @@ -155,5 +73,5 @@ const _getOsmPrices = async ( export const getOsmPrices = memoizee(_getOsmPrices, { maxAge: CACHE_EXPIRY_MS, promise: true, - length: 3, + length: 2, }); diff --git a/core/src/types.ts b/core/src/types.ts index 8ee9cd067..cfb0941fa 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -97,33 +97,21 @@ export declare interface ValueSlotAddressAndOffset { offset: number; } +export declare interface ValueExtractionConfig { + slotAddress: string; + format: string[]; + wordSize?: number; +} + export declare interface CollateralConfig { title: string; ilk: string; symbol: string; decimals: number; exchange: RegularCalleeConfig | UniswapV2LpTokenCalleeConfig; - oracle: CollateralPriceSourceConfig; + oracle: ValueExtractionConfig[]; } -interface OracleConfigBase { - hasDelay: boolean; - slotPriceValueBeginsAtPosition: number; - currentPriceSlotAddress: string; -} - -export declare interface OracleCurrentAndNextPrices extends OracleConfigBase { - type: 'CurrentAndNextPrice'; - nextPriceSlotAddress: string; -} - -export declare interface OracleCurrentPriceOnly extends OracleConfigBase { - type: 'CurrentPriceOnly'; - currentPriceValiditySlotAndOffset: ValueSlotAddressAndOffset; -} - -export type CollateralPriceSourceConfig = OracleCurrentAndNextPrices | OracleCurrentPriceOnly; - export declare interface NetworkConfig { chainId: string; type: string; diff --git a/core/src/vaults.ts b/core/src/vaults.ts index e35655ab0..988cf52e6 100644 --- a/core/src/vaults.ts +++ b/core/src/vaults.ts @@ -301,10 +301,7 @@ const enrichVaultWithTransactonInformation = async ( const { liquidatiorContractAddress, liquidationPenaltyRatio } = await fetchCollateralLiquidationLimitsAndLiquidatorAddress(network, vault.collateralType); - const { liquidationRatio, oracleAddress } = await fetchLiquidationRatioAndOracleAddress( - network, - vault.collateralType - ); + const { liquidationRatio } = await fetchLiquidationRatioAndOracleAddress(network, vault.collateralType); const collateralizationRatio = vault.collateralAmount .multipliedBy(vault.minUnitPrice) .multipliedBy(liquidationRatio) @@ -313,11 +310,7 @@ const enrichVaultWithTransactonInformation = async ( const { transactionFeeLiquidationEth, transactionFeeLiquidationDai } = await getApproximateLiquidationFees( network ); - const { nextUnitPrice, nextPriceChange, currentUnitPrice } = await getOsmPrices( - network, - oracleAddress, - vault.collateralType - ); + const { nextUnitPrice, nextPriceChange, currentUnitPrice } = await getOsmPrices(network, vault.collateralType); const { auctionedAmountDai, state } = getAuctionedDaiAndAuctionState(proximityToLiquidation, vault); const { incentiveCombinedDai, incentiveConstantDai, incentiveRelativeDai } = await fetchVaultLiquidationIncentive( network, diff --git a/core/test/vault-test.ts b/core/test/vault-test.ts index b9a7c97bd..2eca7f1b4 100644 --- a/core/test/vault-test.ts +++ b/core/test/vault-test.ts @@ -1,3 +1,5 @@ +import chai, { expect } from 'chai'; +import deepEqualInAnyOrder from 'deep-equal-in-any-order'; import { collectStabilityFees, fetchLiquidationRatioAndOracleAddress, @@ -10,12 +12,9 @@ import { createWalletForRpc, resetNetwork, warpTime } from '../helpers/hardhat/n import { overwriteUintValue } from '../helpers/hardhat/slotOverwrite'; import { setupRpcUrlAndGetNetworks } from '../src/rpc'; import { HARDHAT_PRIVATE_KEY, HARDHAT_PUBLIC_KEY, LOCAL_RPC_URL, TEST_NETWORK } from '../helpers/constants'; -import { expect } from 'chai'; import { VaultTransactionLiquidated, VaultTransactionNotLiquidated, Vault, CollateralType } from '../src/types'; import BigNumber from '../src/bignumber'; import { createWalletFromPrivateKey } from '../src/signer'; -import deepEqualInAnyOrder from 'deep-equal-in-any-order'; -import chai from 'chai'; import { fetchAuctionByCollateralTypeAndAuctionIndex } from '../src/fetch'; import { fetchVATbalanceDAI } from '../src/wallet'; import createVaultWithCollateral, { @@ -231,9 +230,9 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date('2022-01-23T22:00:00.000Z'), }, 'ETH-A': { - nextUnitPrice: '1712.2106886', - currentUnitPrice: '1712.2106886', - nextPriceChange: new Date('2022-09-09T10:00:00.000Z'), + nextUnitPrice: '1602.1803800999999', + currentUnitPrice: '1602.1803800999999', + nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), }, 'ETH-B': { nextUnitPrice: '1602.1803800999999', @@ -241,9 +240,9 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), }, 'ETH-C': { - nextUnitPrice: '1208.0159951', - currentUnitPrice: '1227.067888375', - nextPriceChange: new Date('2022-06-13T11:00:00.000Z'), + nextUnitPrice: '1602.1803800999999', + currentUnitPrice: '1602.1803800999999', + nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), }, 'GUSD-A': { nextUnitPrice: 'NaN', @@ -405,7 +404,7 @@ describe('Sound values are extracted', () => { it(`can reach ${type} oracle`, async () => { const liquidationRatioAndAddress = await fetchLiquidationRatioAndOracleAddress(TEST_NETWORK, type); expect(liquidationRatioAndAddress.oracleAddress).not.to.eq(NULL_ADDRESS); - const prices = await getOsmPrices(TEST_NETWORK, liquidationRatioAndAddress.oracleAddress, type); + const prices = await getOsmPrices(TEST_NETWORK, type); expect(expectedReturn[type].currentUnitPrice).to.eq(prices.currentUnitPrice.toFixed()); if (expectedReturn[type].nextPriceChange.getTime()) { expect(expectedReturn[type].nextPriceChange.toISOString()).to.eq(prices.nextPriceChange.toISOString()); From e12f06ab580137e9e9238c74411980b2d18db9c6 Mon Sep 17 00:00:00 2001 From: Valia Fetisov Date: Thu, 27 Oct 2022 18:09:16 +0200 Subject: [PATCH 2/3] update types --- core/src/constants/COLLATERALS.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/constants/COLLATERALS.ts b/core/src/constants/COLLATERALS.ts index 0c1082ca9..d63d58651 100644 --- a/core/src/constants/COLLATERALS.ts +++ b/core/src/constants/COLLATERALS.ts @@ -3,11 +3,11 @@ import type { CollateralConfig, ValueExtractionConfig } from '../types'; const DEFAULT_ORACLE_CONFIG: ValueExtractionConfig[] = [ { slotAddress: '0x3', - format: ['uint128 isCurrentUnitPriceValid', 'uint128 currentUnitPrice'], + format: ['bool isCurrentUnitPriceValid', 'uint128 currentUnitPrice'], }, { slotAddress: '0x4', - format: ['uint128 isNextUnitPriceValid', 'uint128 nextUnitPrice'], + format: ['bool isNextUnitPriceValid', 'uint128 nextUnitPrice'], }, ]; const STABLECOIN_ORACLE_CONFIG: ValueExtractionConfig[] = [ From f05f986457a8444b45ca9d065b75401c0484270e Mon Sep 17 00:00:00 2001 From: Valia Fetisov Date: Tue, 29 Nov 2022 10:53:37 +0100 Subject: [PATCH 3/3] WIP rename collateral oracle types --- core/src/constants/COLLATERALS.ts | 10 ++++++---- core/src/oracles.ts | 8 ++++---- core/src/types.ts | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/src/constants/COLLATERALS.ts b/core/src/constants/COLLATERALS.ts index a66d4d89c..bfc87412a 100644 --- a/core/src/constants/COLLATERALS.ts +++ b/core/src/constants/COLLATERALS.ts @@ -3,22 +3,24 @@ import type { CollateralConfig, ValueExtractionConfig } from '../types'; const DEFAULT_ORACLE_CONFIG: ValueExtractionConfig[] = [ { slotAddress: '0x3', - format: ['bool isCurrentUnitPriceValid', 'uint128 currentUnitPrice'], + abiCoderFormat: ['bool isCurrentUnitPriceValid', 'uint128 currentUnitPrice'], + wordSize: 16, }, { slotAddress: '0x4', - format: ['bool isNextUnitPriceValid', 'uint128 nextUnitPrice'], + abiCoderFormat: ['bool isNextUnitPriceValid', 'uint128 nextUnitPrice'], + wordSize: 16, }, ]; const STABLECOIN_ORACLE_CONFIG: ValueExtractionConfig[] = [ { slotAddress: '0x1', - format: ['bool isCurrentUnitPriceValid'], + abiCoderFormat: ['bool isCurrentUnitPriceValid'], wordSize: 32, }, { slotAddress: '0x2', - format: ['uint128 currentUnitPrice'], + abiCoderFormat: ['uint128 currentUnitPrice'], wordSize: 32, }, ]; diff --git a/core/src/oracles.ts b/core/src/oracles.ts index 8eee17e34..52bdf75eb 100644 --- a/core/src/oracles.ts +++ b/core/src/oracles.ts @@ -34,15 +34,15 @@ const getNextOraclePriceChange = async (network: string, collateralType: string) const extractValuesFromAddress = async function ( network: string, contractAddress: string, - configs: ValueExtractionConfig[] + extractionConfigs: ValueExtractionConfig[] ) { const provider = await getProvider(network); let values = {} as any; - for (const config of configs) { - const hexValues = await provider.getStorageAt(contractAddress, config.slotAddress); + for (const extractionConfig of extractionConfigs) { + const hexValues = await provider.getStorageAt(contractAddress, extractionConfig.slotAddress); values = { ...values, - ...createStructCoder(config.wordSize).decode(config.format, hexValues), + ...createStructCoder(extractionConfig.wordSize).decode(extractionConfig.abiCoderFormat, hexValues), }; } return values; diff --git a/core/src/types.ts b/core/src/types.ts index b7a487b64..5070619f4 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -121,7 +121,7 @@ export declare interface ValueSlotAddressAndOffset { export declare interface ValueExtractionConfig { slotAddress: string; - format: string[]; + abiCoderFormat: string[]; wordSize?: number; }