From 3f664f721fee754c774c67c29fda174f45937a0c Mon Sep 17 00:00:00 2001 From: Jacob Eliosoff Date: Wed, 2 Dec 2020 13:41:43 -0500 Subject: [PATCH 1/2] Got rid of all these "GasMeasuredXOracle" contracts, that was never a good idea... One GasMeasuredOracleWrapper is enough. --- .../mocks/GasMeasuredChainlinkOracle.sol | 9 --- .../mocks/GasMeasuredCompoundOpenOracle.sol | 9 --- contracts/mocks/GasMeasuredMakerOracle.sol | 9 --- contracts/mocks/GasMeasuredMedianOracle.sol | 21 ----- contracts/mocks/GasMeasuredOracle.sol | 23 ------ contracts/mocks/GasMeasuredOracleWrapper.sol | 32 ++++++++ .../GasMeasuredOurUniswapV2SpotOracle.sol | 10 --- .../GasMeasuredOurUniswapV2TWAPOracle.sol | 14 ---- .../GasMeasuredUniswapMedianSpotOracle.sol | 15 ---- .../GasMeasuredUniswapMedianTWAPOracle.sol | 21 ----- contracts/mocks/TestOracle.sol | 3 +- test/01_TestOracle.test.js | 77 +++++++++++-------- 12 files changed, 77 insertions(+), 166 deletions(-) delete mode 100755 contracts/mocks/GasMeasuredChainlinkOracle.sol delete mode 100755 contracts/mocks/GasMeasuredCompoundOpenOracle.sol delete mode 100755 contracts/mocks/GasMeasuredMakerOracle.sol delete mode 100755 contracts/mocks/GasMeasuredMedianOracle.sol delete mode 100755 contracts/mocks/GasMeasuredOracle.sol create mode 100755 contracts/mocks/GasMeasuredOracleWrapper.sol delete mode 100755 contracts/mocks/GasMeasuredOurUniswapV2SpotOracle.sol delete mode 100755 contracts/mocks/GasMeasuredOurUniswapV2TWAPOracle.sol delete mode 100755 contracts/mocks/GasMeasuredUniswapMedianSpotOracle.sol delete mode 100755 contracts/mocks/GasMeasuredUniswapMedianTWAPOracle.sol diff --git a/contracts/mocks/GasMeasuredChainlinkOracle.sol b/contracts/mocks/GasMeasuredChainlinkOracle.sol deleted file mode 100755 index b6f94fe..0000000 --- a/contracts/mocks/GasMeasuredChainlinkOracle.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/ChainlinkOracle.sol"; - -contract GasMeasuredChainlinkOracle is ChainlinkOracle, GasMeasuredOracle("chainlink") { - constructor(AggregatorV3Interface chainlinkAggregator) public ChainlinkOracle(chainlinkAggregator) {} -} diff --git a/contracts/mocks/GasMeasuredCompoundOpenOracle.sol b/contracts/mocks/GasMeasuredCompoundOpenOracle.sol deleted file mode 100755 index 22b443d..0000000 --- a/contracts/mocks/GasMeasuredCompoundOpenOracle.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/CompoundOpenOracle.sol"; - -contract GasMeasuredCompoundOpenOracle is CompoundOpenOracle, GasMeasuredOracle("compound") { - constructor(UniswapAnchoredView compoundView) public CompoundOpenOracle(compoundView) {} -} diff --git a/contracts/mocks/GasMeasuredMakerOracle.sol b/contracts/mocks/GasMeasuredMakerOracle.sol deleted file mode 100755 index 1d94e33..0000000 --- a/contracts/mocks/GasMeasuredMakerOracle.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/MakerOracle.sol"; - -contract GasMeasuredMakerOracle is MakerOracle, GasMeasuredOracle("maker") { - constructor(IMakerPriceFeed medianizer) public MakerOracle(medianizer) {} -} diff --git a/contracts/mocks/GasMeasuredMedianOracle.sol b/contracts/mocks/GasMeasuredMedianOracle.sol deleted file mode 100755 index e40bdda..0000000 --- a/contracts/mocks/GasMeasuredMedianOracle.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/MedianOracle.sol"; - -contract GasMeasuredMedianOracle is MedianOracle, GasMeasuredOracle("median") { - uint private constant NUM_UNISWAP_PAIRS = 3; - - constructor( - AggregatorV3Interface chainlinkAggregator, - UniswapAnchoredView compoundView, - IUniswapV2Pair uniswapPair, uint uniswapToken0Decimals, uint uniswapToken1Decimals, bool uniswapTokensInReverseOrder - ) public - MedianOracle(chainlinkAggregator, compoundView, - uniswapPair, uniswapToken0Decimals, uniswapToken1Decimals, uniswapTokensInReverseOrder) {} - - function cacheLatestPrice() public override(Oracle, MedianOracle) returns (uint price) { - price = super.cacheLatestPrice(); - } -} diff --git a/contracts/mocks/GasMeasuredOracle.sol b/contracts/mocks/GasMeasuredOracle.sol deleted file mode 100755 index f4b0fdd..0000000 --- a/contracts/mocks/GasMeasuredOracle.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "../oracles/Oracle.sol"; -import "@nomiclabs/buidler/console.sol"; - -abstract contract GasMeasuredOracle is Oracle { - using SafeMath for uint; - - string internal oracleName; - - constructor(string memory name) public { - oracleName = name; - } - - function latestPriceWithGas() public virtual view returns (uint price) { - uint gasStart = gasleft(); - price = this.latestPrice(); - uint gasEnd = gasleft(); - console.log(" ", oracleName, "oracle.latestPrice() cost: ", gasStart.sub(gasEnd)); - } -} diff --git a/contracts/mocks/GasMeasuredOracleWrapper.sol b/contracts/mocks/GasMeasuredOracleWrapper.sol new file mode 100755 index 0000000..84a9a6f --- /dev/null +++ b/contracts/mocks/GasMeasuredOracleWrapper.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.6.6; + +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "../oracles/Oracle.sol"; +import "@nomiclabs/buidler/console.sol"; + +contract GasMeasuredOracleWrapper is Oracle { + using SafeMath for uint; + + Oracle internal immutable measuredOracle; + string internal oracleName; + + constructor(Oracle oracle, string memory name) public { + measuredOracle = oracle; + oracleName = name; + } + + function latestPrice() public virtual override view returns (uint price) { + uint gasStart = gasleft(); + price = measuredOracle.latestPrice(); + uint gasEnd = gasleft(); + console.log(" ", oracleName, "oracle.latestPrice() cost: ", gasStart.sub(gasEnd)); + } + + function cacheLatestPrice() public virtual override returns (uint price) { + uint gasStart = gasleft(); + price = measuredOracle.cacheLatestPrice(); + uint gasEnd = gasleft(); + console.log(" ", oracleName, "oracle.cacheLatestPrice() cost: ", gasStart.sub(gasEnd)); + } +} diff --git a/contracts/mocks/GasMeasuredOurUniswapV2SpotOracle.sol b/contracts/mocks/GasMeasuredOurUniswapV2SpotOracle.sol deleted file mode 100755 index 5023fd4..0000000 --- a/contracts/mocks/GasMeasuredOurUniswapV2SpotOracle.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/OurUniswapV2SpotOracle.sol"; - -contract GasMeasuredOurUniswapV2SpotOracle is OurUniswapV2SpotOracle, GasMeasuredOracle("uniswapSpot") { - constructor(IUniswapV2Pair pair, uint token0Decimals, uint token1Decimals, bool tokensInReverseOrder) public - OurUniswapV2SpotOracle(pair, token0Decimals, token1Decimals, tokensInReverseOrder) {} -} diff --git a/contracts/mocks/GasMeasuredOurUniswapV2TWAPOracle.sol b/contracts/mocks/GasMeasuredOurUniswapV2TWAPOracle.sol deleted file mode 100755 index f2be91d..0000000 --- a/contracts/mocks/GasMeasuredOurUniswapV2TWAPOracle.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/OurUniswapV2TWAPOracle.sol"; - -contract GasMeasuredOurUniswapV2TWAPOracle is OurUniswapV2TWAPOracle, GasMeasuredOracle("uniswapTWAP") { - constructor(IUniswapV2Pair pair, uint token0Decimals, uint token1Decimals, bool tokensInReverseOrder) public - OurUniswapV2TWAPOracle(pair, token0Decimals, token1Decimals, tokensInReverseOrder) {} - - function cacheLatestPrice() public override(Oracle, OurUniswapV2TWAPOracle) returns (uint price) { - price = super.cacheLatestPrice(); - } -} diff --git a/contracts/mocks/GasMeasuredUniswapMedianSpotOracle.sol b/contracts/mocks/GasMeasuredUniswapMedianSpotOracle.sol deleted file mode 100755 index 10391b5..0000000 --- a/contracts/mocks/GasMeasuredUniswapMedianSpotOracle.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/UniswapMedianSpotOracle.sol"; - -contract GasMeasuredUniswapMedianSpotOracle is UniswapMedianSpotOracle, GasMeasuredOracle("uniswapMedianSpot") { - uint private constant NUM_SOURCE_ORACLES = 3; - - constructor(IUniswapV2Pair[NUM_SOURCE_ORACLES] memory pairs, - uint[NUM_SOURCE_ORACLES] memory tokens0Decimals, - uint[NUM_SOURCE_ORACLES] memory tokens1Decimals, - bool[NUM_SOURCE_ORACLES] memory tokensInReverseOrder) public - UniswapMedianSpotOracle(pairs, tokens0Decimals, tokens1Decimals, tokensInReverseOrder) {} -} diff --git a/contracts/mocks/GasMeasuredUniswapMedianTWAPOracle.sol b/contracts/mocks/GasMeasuredUniswapMedianTWAPOracle.sol deleted file mode 100755 index a9eba44..0000000 --- a/contracts/mocks/GasMeasuredUniswapMedianTWAPOracle.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "./GasMeasuredOracle.sol"; -import "../oracles/UniswapMedianTWAPOracle.sol"; - -contract GasMeasuredUniswapMedianTWAPOracle is UniswapMedianTWAPOracle, GasMeasuredOracle("uniswapMedianTWAP") { - uint private constant NUM_SOURCE_ORACLES = 3; - - constructor( - IUniswapV2Pair[NUM_SOURCE_ORACLES] memory pairs, - uint[NUM_SOURCE_ORACLES] memory tokens0Decimals, - uint[NUM_SOURCE_ORACLES] memory tokens1Decimals, - bool[NUM_SOURCE_ORACLES] memory tokensInReverseOrder - ) public - UniswapMedianTWAPOracle(pairs, tokens0Decimals, tokens1Decimals, tokensInReverseOrder) {} - - function cacheLatestPrice() public override(Oracle, UniswapMedianTWAPOracle) returns (uint price) { - price = super.cacheLatestPrice(); - } -} diff --git a/contracts/mocks/TestOracle.sol b/contracts/mocks/TestOracle.sol index d815ab1..e443984 100755 --- a/contracts/mocks/TestOracle.sol +++ b/contracts/mocks/TestOracle.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.6.6; -import "./GasMeasuredOracle.sol"; import "./SettableOracle.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; -contract TestOracle is GasMeasuredOracle("test"), SettableOracle, Ownable { +contract TestOracle is SettableOracle, Ownable { uint internal savedPrice; constructor(uint p) public { diff --git a/test/01_TestOracle.test.js b/test/01_TestOracle.test.js index 3b6bfaf..25a1bcf 100644 --- a/test/01_TestOracle.test.js +++ b/test/01_TestOracle.test.js @@ -3,22 +3,24 @@ const { BN, expectRevert } = require('@openzeppelin/test-helpers') const TestOracle = artifacts.require('TestOracle') const Medianizer = artifacts.require('MockMakerMedianizer') -const GasMakerOracle = artifacts.require('GasMeasuredMakerOracle') +const MakerOracle = artifacts.require('MakerOracle') const Aggregator = artifacts.require('MockChainlinkAggregatorV3') -const GasChainlinkOracle = artifacts.require('GasMeasuredChainlinkOracle') +const ChainlinkOracle = artifacts.require('ChainlinkOracle') const UniswapAnchoredView = artifacts.require('MockUniswapAnchoredView') -const GasCompoundOracle = artifacts.require('GasMeasuredCompoundOpenOracle') +const CompoundOracle = artifacts.require('CompoundOpenOracle') const UniswapV2Pair = artifacts.require('MockUniswapV2Pair') -const GasUniswapSpotOracle = artifacts.require('GasMeasuredOurUniswapV2SpotOracle') -const GasUniswapTWAPOracle = artifacts.require('GasMeasuredOurUniswapV2TWAPOracle') +const UniswapSpotOracle = artifacts.require('OurUniswapV2SpotOracle') +const UniswapTWAPOracle = artifacts.require('OurUniswapV2TWAPOracle') -const GasUniswapMedianSpotOracle = artifacts.require('GasMeasuredUniswapMedianSpotOracle') -const GasUniswapMedianTWAPOracle = artifacts.require('GasMeasuredUniswapMedianTWAPOracle') +const UniswapMedianSpotOracle = artifacts.require('UniswapMedianSpotOracle') +const UniswapMedianTWAPOracle = artifacts.require('UniswapMedianTWAPOracle') -const GasMedianOracle = artifacts.require('GasMeasuredMedianOracle') +const MedianOracle = artifacts.require('MedianOracle') + +const GasMeasuredOracleWrapper = artifacts.require('GasMeasuredOracleWrapper') require('chai').use(require('chai-as-promised')).should() @@ -91,6 +93,7 @@ contract('Oracle pricing', (accounts) => { beforeEach(async () => { oracle = await TestOracle.new(testPriceWAD, { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "test", { from: deployer }) }) it('returns the correct price', async () => { @@ -99,7 +102,7 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) @@ -112,7 +115,8 @@ contract('Oracle pricing', (accounts) => { medianizer = await Medianizer.new({ from: deployer }) await medianizer.set(makerPriceWAD) - oracle = await GasMakerOracle.new(medianizer.address, { from: deployer }) + oracle = await MakerOracle.new(medianizer.address, { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "maker", { from: deployer }) }) it('returns the correct price', async () => { @@ -121,7 +125,7 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) @@ -134,7 +138,8 @@ contract('Oracle pricing', (accounts) => { aggregator = await Aggregator.new({ from: deployer }) await aggregator.set(chainlinkPrice) - oracle = await GasChainlinkOracle.new(aggregator.address, { from: deployer }) + oracle = await ChainlinkOracle.new(aggregator.address, { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "chainlink", { from: deployer }) }) it('returns the correct price', async () => { @@ -143,7 +148,7 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) @@ -156,7 +161,8 @@ contract('Oracle pricing', (accounts) => { anchoredView = await UniswapAnchoredView.new({ from: deployer }) await anchoredView.set(compoundPrice) - oracle = await GasCompoundOracle.new(anchoredView.address, { from: deployer }) + oracle = await CompoundOracle.new(anchoredView.address, { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "compound", { from: deployer }) }) it('returns the correct price', async () => { @@ -165,7 +171,7 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) @@ -178,8 +184,9 @@ contract('Oracle pricing', (accounts) => { pair = await UniswapV2Pair.new({ from: deployer }) await pair.setReserves(ethUsdtReserve0, ethUsdtReserve1, 0) - oracle = await GasUniswapSpotOracle.new(pair.address, ethDecimals, usdtDecimals, ethUsdtTokensInReverseOrder, - { from: deployer }) + oracle = await UniswapSpotOracle.new(pair.address, ethDecimals, usdtDecimals, ethUsdtTokensInReverseOrder, + { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "uniswapSpot", { from: deployer }) }) it('returns the correct price', async () => { @@ -189,7 +196,7 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) @@ -203,8 +210,9 @@ contract('Oracle pricing', (accounts) => { await pair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_0) await pair.setCumulativePrices(ethUsdtCumPrice0_0, ethUsdtCumPrice1_0) - oracle = await GasUniswapTWAPOracle.new(pair.address, ethDecimals, usdtDecimals, ethUsdtTokensInReverseOrder, - { from: deployer }) + oracle = await UniswapTWAPOracle.new(pair.address, ethDecimals, usdtDecimals, ethUsdtTokensInReverseOrder, + { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "uniswapTWAP", { from: deployer }) await oracle.cacheLatestPrice() await pair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_1) @@ -222,7 +230,7 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) @@ -241,11 +249,12 @@ contract('Oracle pricing', (accounts) => { daiEthPair = await UniswapV2Pair.new({ from: deployer }) await daiEthPair.setReserves(daiEthReserve0, daiEthReserve1, 0) - oracle = await GasUniswapMedianSpotOracle.new( + oracle = await UniswapMedianSpotOracle.new( [ethUsdtPair.address, usdcEthPair.address, daiEthPair.address], [ethDecimals, usdcDecimals, daiDecimals], [usdtDecimals, ethDecimals, ethDecimals], [ethUsdtTokensInReverseOrder, usdcEthTokensInReverseOrder, daiEthTokensInReverseOrder], { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(oracle.address, "uniswapMedianSpot", { from: deployer }) }) it('returns the correct price', async () => { @@ -256,13 +265,13 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) describe("with UniswapMedianTWAPOracle", () => { const [deployer] = accounts - let oracle + let rawOracle, oracle let ethUsdtPair, usdcEthPair, daiEthPair beforeEach(async () => { @@ -278,11 +287,12 @@ contract('Oracle pricing', (accounts) => { await daiEthPair.setReserves(daiEthReserve0, daiEthReserve1, daiEthTimestamp_0) await daiEthPair.setCumulativePrices(daiEthCumPrice0_0, daiEthCumPrice1_0) - oracle = await GasUniswapMedianTWAPOracle.new( + rawOracle = await UniswapMedianTWAPOracle.new( [ethUsdtPair.address, usdcEthPair.address, daiEthPair.address], [ethDecimals, usdcDecimals, daiDecimals], [usdtDecimals, ethDecimals, ethDecimals], [ethUsdtTokensInReverseOrder, usdcEthTokensInReverseOrder, daiEthTokensInReverseOrder], { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(rawOracle.address, "uniswapMedianTWAP", { from: deployer }) await oracle.cacheLatestPrice() await ethUsdtPair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_1) @@ -295,19 +305,19 @@ contract('Oracle pricing', (accounts) => { }) it('returns the correct price', async () => { - const ethUsdtPrice1 = (await oracle.latestUniswapPair1TWAPPrice()) + const ethUsdtPrice1 = (await rawOracle.latestUniswapPair1TWAPPrice()) const targetEthUsdtPriceNum = (ethUsdtCumPrice0_1.sub(ethUsdtCumPrice0_0)).mul(ethUsdtScaleFactor) const targetEthUsdtPriceDenom = (ethUsdtTimestamp_1.sub(ethUsdtTimestamp_0)).mul(uniswapCumPriceScalingFactor) const targetEthUsdtPrice1 = targetEthUsdtPriceNum.div(targetEthUsdtPriceDenom) shouldEqualApprox(ethUsdtPrice1, targetEthUsdtPrice1) - const usdcEthPrice1 = (await oracle.latestUniswapPair2TWAPPrice()) + const usdcEthPrice1 = (await rawOracle.latestUniswapPair2TWAPPrice()) const targetUsdcEthPriceNum = (usdcEthCumPrice1_1.sub(usdcEthCumPrice1_0)).mul(usdcEthScaleFactor) const targetUsdcEthPriceDenom = (usdcEthTimestamp_1.sub(usdcEthTimestamp_0)).mul(uniswapCumPriceScalingFactor) const targetUsdcEthPrice1 = targetUsdcEthPriceNum.div(targetUsdcEthPriceDenom) shouldEqualApprox(usdcEthPrice1, targetUsdcEthPrice1) - const daiEthPrice1 = (await oracle.latestUniswapPair3TWAPPrice()) + const daiEthPrice1 = (await rawOracle.latestUniswapPair3TWAPPrice()) const targetDaiEthPriceNum = (daiEthCumPrice1_1.sub(daiEthCumPrice1_0)).mul(daiEthScaleFactor) const targetDaiEthPriceDenom = (daiEthTimestamp_1.sub(daiEthTimestamp_0)).mul(uniswapCumPriceScalingFactor) const targetDaiEthPrice1 = targetDaiEthPriceNum.div(targetDaiEthPriceDenom) @@ -319,13 +329,13 @@ contract('Oracle pricing', (accounts) => { }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) describe("with MedianOracle", () => { const [deployer] = accounts - let oracle + let rawOracle, oracle let makerMedianizer, chainlinkAggregator, compoundView, usdcEthPair beforeEach(async () => { @@ -342,8 +352,9 @@ contract('Oracle pricing', (accounts) => { await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_0) await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_0, usdcEthCumPrice1_0) - oracle = await GasMedianOracle.new(chainlinkAggregator.address, compoundView.address, + rawOracle = await MedianOracle.new(chainlinkAggregator.address, compoundView.address, usdcEthPair.address, usdcDecimals, ethDecimals, usdcEthTokensInReverseOrder, { from: deployer }) + oracle = await GasMeasuredOracleWrapper.new(rawOracle.address, "median", { from: deployer }) await oracle.cacheLatestPrice() await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_1) @@ -353,13 +364,13 @@ contract('Oracle pricing', (accounts) => { it('returns the correct price', async () => { const oraclePrice = (await oracle.latestPrice()) - const uniswapPrice = (await oracle.latestUniswapTWAPPrice()) + const uniswapPrice = (await rawOracle.latestUniswapTWAPPrice()) const targetOraclePrice = median(chainlinkPriceWAD, compoundPriceWAD, uniswapPrice) oraclePrice.toString().should.equal(targetOraclePrice.toString()) }) it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPriceWithGas()) + const oraclePrice = (await oracle.latestPrice()) }) }) }) From 97440c85500d67fe498a558c761a73d84adb0219 Mon Sep 17 00:00:00 2001 From: Jacob Eliosoff Date: Wed, 2 Dec 2020 14:09:48 -0500 Subject: [PATCH 2/2] Trimmed a bunch of contracts/code we're not actually using: mostly just MakerOracle, OurUniswapV2SpotOracle, UniswapMedianSpotOracle, UniswapMedianTWAPOracle. --- contracts/mocks/MockMakerMedianizer.sol | 10 - contracts/oracles/MakerOracle.sol | 32 --- contracts/oracles/OurUniswapV2SpotOracle.sol | 32 --- contracts/oracles/OurUniswapV2TWAPOracle.sol | 5 +- contracts/oracles/UniswapMedianSpotOracle.sol | 52 ---- contracts/oracles/UniswapMedianTWAPOracle.sol | 242 ------------------ migrations/2_deploy_usmfum.js | 8 +- test/01_TestOracle.test.js | 183 +------------ test/03_USM.test.js | 14 +- 9 files changed, 23 insertions(+), 555 deletions(-) delete mode 100755 contracts/mocks/MockMakerMedianizer.sol delete mode 100755 contracts/oracles/MakerOracle.sol delete mode 100755 contracts/oracles/OurUniswapV2SpotOracle.sol delete mode 100755 contracts/oracles/UniswapMedianSpotOracle.sol delete mode 100755 contracts/oracles/UniswapMedianTWAPOracle.sol diff --git a/contracts/mocks/MockMakerMedianizer.sol b/contracts/mocks/MockMakerMedianizer.sol deleted file mode 100755 index c1a9d10..0000000 --- a/contracts/mocks/MockMakerMedianizer.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -contract MockMakerMedianizer { - uint public read; - - function set(uint value) external { - read = value; - } -} diff --git a/contracts/oracles/MakerOracle.sol b/contracts/oracles/MakerOracle.sol deleted file mode 100755 index 8842288..0000000 --- a/contracts/oracles/MakerOracle.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "./Oracle.sol"; - -interface IMakerPriceFeed { - function read() external view returns (bytes32); -} - -/** - * @title MakerOracle - */ -contract MakerOracle is Oracle { - using SafeMath for uint; - - IMakerPriceFeed private medianizer; - - constructor(IMakerPriceFeed medianizer_) public - { - medianizer = medianizer_; - } - - function latestPrice() public virtual override view returns (uint price) { - price = latestMakerPrice(); - } - - function latestMakerPrice() public view returns (uint price) { - // From https://studydefi.com/read-maker-medianizer/: - price = uint(medianizer.read()); // No need to scale, medianizer price is already 18 dec places - } -} diff --git a/contracts/oracles/OurUniswapV2SpotOracle.sol b/contracts/oracles/OurUniswapV2SpotOracle.sol deleted file mode 100755 index 642974e..0000000 --- a/contracts/oracles/OurUniswapV2SpotOracle.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; -import "./Oracle.sol"; -import "./OurUniswap.sol"; - -contract OurUniswapV2SpotOracle is Oracle { - using SafeMath for uint; - - OurUniswap.Pair private pair; - - /** - * Example pairs to pass in: - * ETH/USDT: 0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852, false, 18, 6 (WETH reserve is stored w/ 18 dec places, USDT w/ 18) - * USDC/ETH: 0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc, true, 6, 18 (USDC reserve is stored w/ 6 dec places, WETH w/ 18) - * DAI/ETH: 0xa478c2975ab1ea89e8196811f51a7b7ade33eb11, true, 18, 18 (DAI reserve is stored w/ 18 dec places, WETH w/ 18) - */ - constructor(IUniswapV2Pair uniswapPair, uint token0Decimals, uint token1Decimals, bool tokensInReverseOrder) public - { - pair = OurUniswap.createPair(uniswapPair, token0Decimals, token1Decimals, tokensInReverseOrder); - } - - function latestPrice() public virtual override view returns (uint price) { - price = OurUniswap.spotPrice(pair); - } - - function latestUniswapSpotPrice() public view returns (uint price) { - price = OurUniswap.spotPrice(pair); - } -} diff --git a/contracts/oracles/OurUniswapV2TWAPOracle.sol b/contracts/oracles/OurUniswapV2TWAPOracle.sol index 8928f7d..da1a1fc 100755 --- a/contracts/oracles/OurUniswapV2TWAPOracle.sol +++ b/contracts/oracles/OurUniswapV2TWAPOracle.sol @@ -43,7 +43,10 @@ contract OurUniswapV2TWAPOracle is Oracle { CumulativePrice private storedPriceB; /** - * See OurUniswapV2SpotOracle for example pairs to pass in. + * Example pairs to pass in: + * ETH/USDT: 0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852, false, 18, 6 (WETH reserve is stored w/ 18 dec places, USDT w/ 18) + * USDC/ETH: 0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc, true, 6, 18 (USDC reserve is stored w/ 6 dec places, WETH w/ 18) + * DAI/ETH: 0xa478c2975ab1ea89e8196811f51a7b7ade33eb11, true, 18, 18 (DAI reserve is stored w/ 18 dec places, WETH w/ 18) */ constructor(IUniswapV2Pair uniswapPair, uint token0Decimals, uint token1Decimals, bool tokensInReverseOrder) public { pair = OurUniswap.createPair(uniswapPair, token0Decimals, token1Decimals, tokensInReverseOrder); diff --git a/contracts/oracles/UniswapMedianSpotOracle.sol b/contracts/oracles/UniswapMedianSpotOracle.sol deleted file mode 100755 index f7a9dc2..0000000 --- a/contracts/oracles/UniswapMedianSpotOracle.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; -import "./Oracle.sol"; -import "./OurUniswap.sol"; -import "./Median.sol"; - -contract UniswapMedianSpotOracle is Oracle { - using SafeMath for uint; - - uint private constant NUM_SOURCE_ORACLES = 3; - - OurUniswap.Pair[NUM_SOURCE_ORACLES] private pairs; - - /** - * See OurUniswapV2SpotOracle for example pairs to pass in. - */ - constructor(IUniswapV2Pair[NUM_SOURCE_ORACLES] memory uniswapPairs, - uint[NUM_SOURCE_ORACLES] memory tokens0Decimals, - uint[NUM_SOURCE_ORACLES] memory tokens1Decimals, - bool[NUM_SOURCE_ORACLES] memory tokensInReverseOrder) public - { - for (uint i = 0; i < NUM_SOURCE_ORACLES; ++i) { - pairs[i] = OurUniswap.createPair(uniswapPairs[i], tokens0Decimals[i], tokens1Decimals[i], tokensInReverseOrder[i]); - } - } - - function latestPrice() public virtual override view returns (uint price) { - price = latestUniswapMedianSpotPrice(); - } - - function latestUniswapMedianSpotPrice() public view returns (uint price) { - // For maximum gas efficiency... - price = Median.median(OurUniswap.spotPrice(pairs[0]), - OurUniswap.spotPrice(pairs[1]), - OurUniswap.spotPrice(pairs[2])); - } - - function latestUniswapPair1SpotPrice() public view returns (uint price) { - price = OurUniswap.spotPrice(pairs[0]); - } - - function latestUniswapPair2SpotPrice() public view returns (uint price) { - price = OurUniswap.spotPrice(pairs[1]); - } - - function latestUniswapPair3SpotPrice() public view returns (uint price) { - price = OurUniswap.spotPrice(pairs[2]); - } -} diff --git a/contracts/oracles/UniswapMedianTWAPOracle.sol b/contracts/oracles/UniswapMedianTWAPOracle.sol deleted file mode 100755 index 14fe31e..0000000 --- a/contracts/oracles/UniswapMedianTWAPOracle.sol +++ /dev/null @@ -1,242 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.6.6; - -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; -import "./Oracle.sol"; -import "./OurUniswap.sol"; -import "./Median.sol"; - -contract UniswapMedianTWAPOracle is Oracle { - using SafeMath for uint; - - // Minimum age of the stored CumulativePrices we calculate our TWAP vs, and minimum gap between stored CumulativePrices. - // See also the more detailed explanation in OurUniswapV2TWAPOracle. - uint public constant MIN_TWAP_PERIOD = 2 minutes; - - uint private constant WAD = 10 ** 18; - uint private constant WAD_OVER_100 = WAD / 100; - uint private constant UINT32_MAX = 2 ** 32 - 1; // Should really be type(uint32).max, but that needs Solidity 0.6.8... - uint private constant TWO_TO_THE_48 = 2 ** 48; // See below for how we use this - uint private constant MAX_PRICE_SECONDS_CHANGE_WE_CAN_STORE = TWO_TO_THE_48 * WAD_OVER_100; // See unpackPriceSeconds() - - uint private constant NUM_SOURCE_ORACLES = 3; - - // Each CumulativePrices struct crams three pairs timestamps and cumulative prices ("price-seconds") into one 256-bit word. - struct CumulativePrices { - uint32 timestamp1; - uint48 priceSeconds1; // See OurUniswap.cumulativePrice() for an explanation of "priceSeconds" - uint32 timestamp2; - uint48 priceSeconds2; - uint32 timestamp3; - uint48 priceSeconds3; - } - - OurUniswap.Pair[NUM_SOURCE_ORACLES] private pairs; - - // See comment in OurUniswapV2TWAPOracle for why we store two CumulativePrices rather than one: - CumulativePrices private storedPricesA; - CumulativePrices private storedPricesB; - - /* ____________________ Constructor ____________________ */ - - /** - * See OurUniswapV2SpotOracle for example pairs to pass in - */ - constructor(IUniswapV2Pair[NUM_SOURCE_ORACLES] memory uniswapPairs, - uint[NUM_SOURCE_ORACLES] memory tokens0Decimals, - uint[NUM_SOURCE_ORACLES] memory tokens1Decimals, - bool[NUM_SOURCE_ORACLES] memory tokensInReverseOrder) public - { - for (uint i = 0; i < NUM_SOURCE_ORACLES; ++i) { - pairs[i] = OurUniswap.createPair(uniswapPairs[i], tokens0Decimals[i], tokens1Decimals[i], tokensInReverseOrder[i]); - } - } - - /* ____________________ Public stateful functions ____________________ */ - - function cacheLatestPrice() public virtual override returns (uint price) { - (CumulativePrices storage olderStoredPrices, CumulativePrices storage newerStoredPrices) = orderedStoredPrices(); - - uint[NUM_SOURCE_ORACLES] memory prices; - uint[NUM_SOURCE_ORACLES] memory timestamps; - uint[NUM_SOURCE_ORACLES] memory priceSeconds; - (price, prices, timestamps, priceSeconds) = latestPrices(newerStoredPrices); - - if (areNewAndStoredPricesFarEnoughApart(timestamps, newerStoredPrices)) { - storeCumulativePrices(timestamps, priceSeconds, olderStoredPrices); - } - } - - /* ____________________ Public view functions ____________________ */ - - function latestPrice() public virtual override view returns (uint price) { - price = latestUniswapMedianTWAPPrice(); - } - - function latestUniswapMedianTWAPPrice() public view returns (uint price) { - (, CumulativePrices storage newerStoredPrices) = orderedStoredPrices(); - (price, , , ) = latestPrices(newerStoredPrices); - } - - function latestUniswapPair1TWAPPrice() public view returns (uint price) { - (, CumulativePrices storage newerStoredPrices) = orderedStoredPrices(); - (, uint[NUM_SOURCE_ORACLES] memory prices, , ) = latestPrices(newerStoredPrices); - price = prices[0]; - } - - function latestUniswapPair2TWAPPrice() public view returns (uint price) { - (, CumulativePrices storage newerStoredPrices) = orderedStoredPrices(); - (, uint[NUM_SOURCE_ORACLES] memory prices, , ) = latestPrices(newerStoredPrices); - price = prices[1]; - } - - function latestUniswapPair3TWAPPrice() public view returns (uint price) { - (, CumulativePrices storage newerStoredPrices) = orderedStoredPrices(); - (, uint[NUM_SOURCE_ORACLES] memory prices, , ) = latestPrices(newerStoredPrices); - price = prices[2]; - } - - /* ____________________ Internal stateful functions ____________________ */ - - function storeCumulativePrices(uint[NUM_SOURCE_ORACLES] memory timestamps, - uint[NUM_SOURCE_ORACLES] memory priceSeconds, - CumulativePrices storage olderStoredPrices) - internal - { - for (uint i = 0; i < NUM_SOURCE_ORACLES; ++i) { - require(timestamps[i] <= UINT32_MAX, "timestamp overflow"); - } - - // We need to make the priceSeconds representation more compact than our usual WAD fixed-point decimal, in order to - // pack our update into a single 256-bit-word SSTORE. So we store in "pennies" (2-decimal-place precision, rather than - // 18-decimal-place). This still gives quite accurate ETH *prices*: to within around $0.0001. - // We also do "% TWO_TO_THE_48", rather than die on larger priceSeconds values with an overflow - see - // unpackPriceSeconds() below for how we avoid overflow (reasonably) safely. - // (Note: this assignment only stores because olderStoredPrices has modifier "storage" - ie, store by reference!) - olderStoredPrices.timestamp1 = uint32(timestamps[0]); - olderStoredPrices.timestamp2 = uint32(timestamps[1]); - olderStoredPrices.timestamp3 = uint32(timestamps[2]); - olderStoredPrices.priceSeconds1 = uint48((priceSeconds[0] / WAD_OVER_100) % TWO_TO_THE_48); - olderStoredPrices.priceSeconds2 = uint48((priceSeconds[1] / WAD_OVER_100) % TWO_TO_THE_48); - olderStoredPrices.priceSeconds3 = uint48((priceSeconds[2] / WAD_OVER_100) % TWO_TO_THE_48); - } - - /* ____________________ Internal view functions ____________________ */ - - function latestPrices(CumulativePrices storage newerStoredPrices) internal view - returns (uint medianPrice, - uint[NUM_SOURCE_ORACLES] memory prices, - uint[NUM_SOURCE_ORACLES] memory timestamps, - uint[NUM_SOURCE_ORACLES] memory priceSeconds) - { - for (uint i = 0; i < NUM_SOURCE_ORACLES; ++i) { - (timestamps[i], priceSeconds[i]) = OurUniswap.cumulativePrice(pairs[i]); - } - - CumulativePrices storage refPrices = storedPricesToCompareVs(timestamps, newerStoredPrices); - (uint[NUM_SOURCE_ORACLES] memory storedTimestamps, uint[NUM_SOURCE_ORACLES] memory storedPriceSeconds) = - unpackStoredPrices(refPrices, priceSeconds); - - for (uint i = 0; i < NUM_SOURCE_ORACLES; ++i) { - prices[i] = OurUniswap.calculateTWAP(timestamps[i], priceSeconds[i], storedTimestamps[i], storedPriceSeconds[i]); - } - medianPrice = Median.median(prices[0], prices[1], prices[2]); - } - - function storedPricesToCompareVs(uint[NUM_SOURCE_ORACLES] memory newTimestamps, CumulativePrices storage newerStoredPrices) - internal view returns (CumulativePrices storage refPrices) - { - bool aAcceptable = areNewAndStoredPricesFarEnoughApart(newTimestamps, storedPricesA); - bool bAcceptable = areNewAndStoredPricesFarEnoughApart(newTimestamps, storedPricesB); - - if (aAcceptable) { - if (bAcceptable) { - refPrices = newerStoredPrices; // Neither is *too* recent, so return the fresher of the two - } else { - refPrices = storedPricesA; // Only A is acceptable - } - } else if (bAcceptable) { - refPrices = storedPricesB; // Only B is acceptable - } else { - revert("Both stored prices too recent"); - } - } - - /** - * @return olderStoredPrices The older of the two stored CumulativePrices. - * @return newerStoredPrices The newer of the two. - */ - function orderedStoredPrices() internal view - returns (CumulativePrices storage olderStoredPrices, CumulativePrices storage newerStoredPrices) - { - // Because we only store a new CumulativePrices if its three timestamps are *all* > those of the other stored - // CumulativePrices, we can check which is newer by just comparing the first timestamp in each: - (olderStoredPrices, newerStoredPrices) = storedPricesB.timestamp1 > storedPricesA.timestamp1 ? - (storedPricesA, storedPricesB) : (storedPricesB, storedPricesA); - } - - /* ____________________ Internal pure functions ____________________ */ - - /** - * @notice New prices should only be stored if all three of the new timestamps are >= MIN_TWAP_PERIOD fresher than their - * corresponding latest stored timestamps: that is, we never want to store two cumulative prices < MIN_TWAP_PERIOD apart. - */ - function areNewAndStoredPricesFarEnoughApart(uint[NUM_SOURCE_ORACLES] memory newTimestamps, - CumulativePrices storage storedPrices) internal view - returns (bool farEnough) - { - farEnough = ((newTimestamps[0] >= storedPrices.timestamp1 + MIN_TWAP_PERIOD) && // No risk of overflow on a uint32 - (newTimestamps[1] >= storedPrices.timestamp2 + MIN_TWAP_PERIOD) && - (newTimestamps[2] >= storedPrices.timestamp3 + MIN_TWAP_PERIOD)); - } - - /** - * @notice Unpacks the compact stored CumulativePrices format (uint32s and uint48s) into regular WAD-scaled uints. - */ - function unpackStoredPrices(CumulativePrices storage storedPrices, uint[NUM_SOURCE_ORACLES] memory latestPriceSeconds) - internal view - returns (uint[NUM_SOURCE_ORACLES] memory storedTimestamps, uint[NUM_SOURCE_ORACLES] memory storedPriceSeconds) - { - storedTimestamps[0] = storedPrices.timestamp1; - storedTimestamps[1] = storedPrices.timestamp2; - storedTimestamps[2] = storedPrices.timestamp3; - - // Convert the 100-scaled uint48 stored priceSeconds (eg, 58132, representing $581.32) into our usual WAD fixed-point - // format (eg, 581.32 * 10**18): - storedPriceSeconds[0] = unpackPriceSeconds(storedPrices.priceSeconds1, latestPriceSeconds[0]); - storedPriceSeconds[1] = unpackPriceSeconds(storedPrices.priceSeconds2, latestPriceSeconds[1]); - storedPriceSeconds[2] = unpackPriceSeconds(storedPrices.priceSeconds3, latestPriceSeconds[2]); - } - - /** - * @notice Unpacks a 100-scaled uint48 stored priceSeconds value (eg, 88358132, representing $882,581.32) into our usual - * WAD fixed-point format (eg, 882,581.32 * 10**18). The easy part here is multiplying by (10**18 / 100), but we also make - * an adjustment if needed to account for the fact that we only store priceSeconds mod 2**48. Here's an intuitive example - * using decimals: - * - * Suppose we were storing priceSeconds % (10**6), ie, only the last 6 digits of the cumulative price. As long as the - * stored priceSeconds is < 1,000,000, the stored price = the actual price. But suppose we have a stored priceSeconds of - * 998,621, and also see that the latest priceSeconds from Uniswap is 23,000,067. Then we can infer that the stored - * 998,621 probably represents 22,998,621, not 998,621. This is the adjustment we make below. - * - * Of course, we're making an assumption here: that the actual difference between the stored priceSeconds and Uniswap's - * latest priceSeconds will never exceed the max value we can store (999,999 in the intuitive example above, 2**48 - 1 in - * our actual code below). This is a reasonable assumption though: a one-month gap between updates, during which ETH was - * priced at $1,000,000, would still add less than 2**48 to priceSeconds. - * - * Without this adjustment, we'd be assuming not just that the *difference* between successive updates will always be - * < 2**48, but that the priceSeconds value *itself* will always be < 2**48: a significantly shakier assumption. - */ - function unpackPriceSeconds(uint48 storedPriceSeconds, uint latestPriceSeconds) internal pure returns (uint priceSeconds) { - // First, convert to WAD format: - priceSeconds = storedPriceSeconds * WAD_OVER_100; - - // Now, if priceSeconds is still more than MAX_PRICE_SECONDS_CHANGE_WE_CAN_STORE below latestPriceSeconds, adjust it: - if (latestPriceSeconds >= priceSeconds + MAX_PRICE_SECONDS_CHANGE_WE_CAN_STORE) { - // Following the intuitive example above: how do we calculate 22,998,621 from 998,621 and 23,000,067? As follows: - // 23,000,067 - ((23,000,067 - 998,621) % 1,000,000) = 22,998,621. - priceSeconds = latestPriceSeconds - ((latestPriceSeconds - priceSeconds) % MAX_PRICE_SECONDS_CHANGE_WE_CAN_STORE); - } - } -} diff --git a/migrations/2_deploy_usmfum.js b/migrations/2_deploy_usmfum.js index ea612a5..eed107a 100644 --- a/migrations/2_deploy_usmfum.js +++ b/migrations/2_deploy_usmfum.js @@ -35,7 +35,7 @@ module.exports = async function(deployer, network) { const e18 = '1000000000000000000' let weth, aggregator, anchoredView, usdcEthPair let wethAddress, aggregatorAddress, anchoredViewAddress, usdcEthPairAddress - //const uniswapTokens0Decimals = [18, 6, 18] // See UniswapMedianOracle + //const uniswapTokens0Decimals = [18, 6, 18] // ETH/USDT, USDC/ETH, DAI/ETH. See OurUniswapV2TWAPOracle //const uniswapTokens1Decimals = [6, 18, 18] // See UniswapMedianOracle //const uniswapTokensInReverseOrder = [false, true, true] // See UniswapMedianOracle const usdcDecimals = 6 // See UniswapMedianOracle @@ -50,8 +50,8 @@ module.exports = async function(deployer, network) { const chainlinkPrice = '38598000000' // 8 dec places: see ChainlinkOracle const compoundPrice = '414174999' // 6 dec places: see CompoundOpenOracle - const usdcEthReserve0 = '260787673159143' - const usdcEthReserve1 = '696170744128378724814084' + const usdcEthCumPrice0 = '307631784275278277546624451305316303382174855535226' + const usdcEthCumPrice1 = '31377639132666967530700283664103' await deployer.deploy(MockAggregator) aggregator = await MockAggregator.deployed() @@ -65,7 +65,7 @@ module.exports = async function(deployer, network) { await deployer.deploy(MockUniswapV2Pair) usdcEthPair = await MockUniswapV2Pair.deployed() - await usdcEthPair.set(usdcEthReserve0, usdcEthReserve1) + await usdcEthPair.setCumulativePrices(usdcEthCumPrice0, usdcEthCumPrice1) usdcEthPairAddress = usdcEthPair.address } else { diff --git a/test/01_TestOracle.test.js b/test/01_TestOracle.test.js index 25a1bcf..51ee45a 100644 --- a/test/01_TestOracle.test.js +++ b/test/01_TestOracle.test.js @@ -2,9 +2,6 @@ const { BN, expectRevert } = require('@openzeppelin/test-helpers') const TestOracle = artifacts.require('TestOracle') -const Medianizer = artifacts.require('MockMakerMedianizer') -const MakerOracle = artifacts.require('MakerOracle') - const Aggregator = artifacts.require('MockChainlinkAggregatorV3') const ChainlinkOracle = artifacts.require('ChainlinkOracle') @@ -12,12 +9,8 @@ const UniswapAnchoredView = artifacts.require('MockUniswapAnchoredView') const CompoundOracle = artifacts.require('CompoundOpenOracle') const UniswapV2Pair = artifacts.require('MockUniswapV2Pair') -const UniswapSpotOracle = artifacts.require('OurUniswapV2SpotOracle') const UniswapTWAPOracle = artifacts.require('OurUniswapV2TWAPOracle') -const UniswapMedianSpotOracle = artifacts.require('UniswapMedianSpotOracle') -const UniswapMedianTWAPOracle = artifacts.require('UniswapMedianTWAPOracle') - const MedianOracle = artifacts.require('MedianOracle') const GasMeasuredOracleWrapper = artifacts.require('GasMeasuredOracleWrapper') @@ -27,23 +20,19 @@ require('chai').use(require('chai-as-promised')).should() contract('Oracle pricing', (accounts) => { const testPriceWAD = '250000000000000000000' // TestOracle just uses WAD (18-dec-place) pricing - const makerPriceWAD = '392110000000000000000' // Maker medianizer (IMakerPriceFeed) returns WAD 18-dec-place prices - const chainlinkPrice = '38598000000' // Chainlink aggregator (AggregatorV3Interface) stores 8 dec places const chainlinkPriceWAD = new BN(chainlinkPrice + '0000000000') // We want 18 dec places, so add 10 0s const compoundPrice = '414174999' // Compound view (UniswapAnchoredView) stores 6 dec places const compoundPriceWAD = new BN(compoundPrice + '000000000000') // We want 18 dec places, so add 12 0s - const ethDecimals = new BN(18) // See OurUniswapV2SpotOracle + const ethDecimals = new BN(18) // See OurUniswapV2TWAPOracle const usdtDecimals = new BN(6) const usdcDecimals = new BN(6) const daiDecimals = new BN(18) const uniswapCumPriceScalingFactor = (new BN(2)).pow(new BN(112)) - const ethUsdtReserve0 = new BN('646310144553926227215994') // From the ETH/USDT pair. See OurUniswapV2SpotOracle - const ethUsdtReserve1 = new BN('254384028636585') - const ethUsdtCumPrice0_0 = new BN('30197009659458262808281833965635') + const ethUsdtCumPrice0_0 = new BN('30197009659458262808281833965635') // From the ETH/USDT pair. See OurUniswapV2TWAPOracle const ethUsdtCumPrice1_0 = new BN('276776388531768266239160661937116320880685460468473') const ethUsdtTimestamp_0 = new BN('1606780564') const ethUsdtCumPrice0_1 = new BN('30198349396553956234684790868151') @@ -52,9 +41,7 @@ contract('Oracle pricing', (accounts) => { const ethUsdtTokensInReverseOrder = false const ethUsdtScaleFactor = (new BN(10)).pow(ethDecimals.add(new BN(18)).sub(usdtDecimals)) - const usdcEthReserve0 = new BN('260787673159143') // From the USDC/ETH pair - const usdcEthReserve1 = new BN('696170744128378724814084') - const usdcEthCumPrice0_0 = new BN('307631784275278277546624451305316303382174855535226') + const usdcEthCumPrice0_0 = new BN('307631784275278277546624451305316303382174855535226') // From the USDC/ETH pair const usdcEthCumPrice1_0 = new BN('31377639132666967530700283664103') const usdcEthTimestamp_0 = new BN('1606780664') const usdcEthCumPrice0_1 = new BN('307634635050611880719301156089846577363471806696356') @@ -63,9 +50,7 @@ contract('Oracle pricing', (accounts) => { const usdcEthTokensInReverseOrder = true const usdcEthScaleFactor = (new BN(10)).pow(ethDecimals.add(new BN(18)).sub(usdcDecimals)) - const daiEthReserve0 = new BN('178617913077721329213551886') // From the DAI/ETH pair - const daiEthReserve1 = new BN('480578265664207487333589') - const daiEthCumPrice0_0 = new BN('291033362911607134656453476145906896216') + const daiEthCumPrice0_0 = new BN('291033362911607134656453476145906896216') // From the DAI/ETH pair const daiEthCumPrice1_0 = new BN('30339833685805974401597062880404438583745289') const daiEthTimestamp_0 = new BN('1606780728') const daiEthCumPrice0_1 = new BN('291036072023637413832938851532265880018') @@ -106,29 +91,6 @@ contract('Oracle pricing', (accounts) => { }) }) - describe("with MakerOracle", () => { - const [deployer] = accounts - let oracle - let medianizer - - beforeEach(async () => { - medianizer = await Medianizer.new({ from: deployer }) - await medianizer.set(makerPriceWAD) - - oracle = await MakerOracle.new(medianizer.address, { from: deployer }) - oracle = await GasMeasuredOracleWrapper.new(oracle.address, "maker", { from: deployer }) - }) - - it('returns the correct price', async () => { - const oraclePrice = (await oracle.latestPrice()) - oraclePrice.toString().should.equal(makerPriceWAD) - }) - - it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPrice()) - }) - }) - describe("with ChainlinkOracle", () => { const [deployer] = accounts let oracle @@ -175,31 +137,6 @@ contract('Oracle pricing', (accounts) => { }) }) - describe("with OurUniswapV2SpotOracle", () => { - const [deployer] = accounts - let oracle - let pair - - beforeEach(async () => { - pair = await UniswapV2Pair.new({ from: deployer }) - await pair.setReserves(ethUsdtReserve0, ethUsdtReserve1, 0) - - oracle = await UniswapSpotOracle.new(pair.address, ethDecimals, usdtDecimals, ethUsdtTokensInReverseOrder, - { from: deployer }) - oracle = await GasMeasuredOracleWrapper.new(oracle.address, "uniswapSpot", { from: deployer }) - }) - - it('returns the correct price', async () => { - const oraclePrice = (await oracle.latestPrice()) - const targetPrice = ethUsdtReserve1.mul(ethUsdtScaleFactor).div(ethUsdtReserve0) // reverseOrder = false - oraclePrice.toString().should.equal(targetPrice.toString()) - }) - - it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPrice()) - }) - }) - describe("with OurUniswapV2TWAPOracle", () => { const [deployer] = accounts let oracle @@ -207,7 +144,7 @@ contract('Oracle pricing', (accounts) => { beforeEach(async () => { pair = await UniswapV2Pair.new({ from: deployer }) - await pair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_0) + await pair.setReserves(0, 0, ethUsdtTimestamp_0) await pair.setCumulativePrices(ethUsdtCumPrice0_0, ethUsdtCumPrice1_0) oracle = await UniswapTWAPOracle.new(pair.address, ethDecimals, usdtDecimals, ethUsdtTokensInReverseOrder, @@ -215,7 +152,7 @@ contract('Oracle pricing', (accounts) => { oracle = await GasMeasuredOracleWrapper.new(oracle.address, "uniswapTWAP", { from: deployer }) await oracle.cacheLatestPrice() - await pair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_1) + await pair.setReserves(0, 0, ethUsdtTimestamp_1) await pair.setCumulativePrices(ethUsdtCumPrice0_1, ethUsdtCumPrice1_1) //await oracle.cacheLatestPrice() // Not actually needed, unless we do further testing moving timestamps further forward }) @@ -234,114 +171,12 @@ contract('Oracle pricing', (accounts) => { }) }) - describe("with UniswapMedianSpotOracle", () => { - const [deployer] = accounts - let oracle - let ethUsdtPair, usdcEthPair, daiEthPair - - beforeEach(async () => { - ethUsdtPair = await UniswapV2Pair.new({ from: deployer }) - await ethUsdtPair.setReserves(ethUsdtReserve0, ethUsdtReserve1, 0) - - usdcEthPair = await UniswapV2Pair.new({ from: deployer }) - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, 0) - - daiEthPair = await UniswapV2Pair.new({ from: deployer }) - await daiEthPair.setReserves(daiEthReserve0, daiEthReserve1, 0) - - oracle = await UniswapMedianSpotOracle.new( - [ethUsdtPair.address, usdcEthPair.address, daiEthPair.address], - [ethDecimals, usdcDecimals, daiDecimals], - [usdtDecimals, ethDecimals, ethDecimals], - [ethUsdtTokensInReverseOrder, usdcEthTokensInReverseOrder, daiEthTokensInReverseOrder], { from: deployer }) - oracle = await GasMeasuredOracleWrapper.new(oracle.address, "uniswapMedianSpot", { from: deployer }) - }) - - it('returns the correct price', async () => { - const oraclePrice = (await oracle.latestPrice()) - // Of the three pairs, the USDC/ETH pair has the middle price, so UniswapMedianSpotOracle's price should match it: - const targetPrice = usdcEthReserve0.mul(usdcEthScaleFactor).div(usdcEthReserve1) // reverseOrder = true - oraclePrice.toString().should.equal(targetPrice.toString()) - }) - - it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPrice()) - }) - }) - - describe("with UniswapMedianTWAPOracle", () => { - const [deployer] = accounts - let rawOracle, oracle - let ethUsdtPair, usdcEthPair, daiEthPair - - beforeEach(async () => { - ethUsdtPair = await UniswapV2Pair.new({ from: deployer }) - await ethUsdtPair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_0) - await ethUsdtPair.setCumulativePrices(ethUsdtCumPrice0_0, ethUsdtCumPrice1_0) - - usdcEthPair = await UniswapV2Pair.new({ from: deployer }) - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_0) - await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_0, usdcEthCumPrice1_0) - - daiEthPair = await UniswapV2Pair.new({ from: deployer }) - await daiEthPair.setReserves(daiEthReserve0, daiEthReserve1, daiEthTimestamp_0) - await daiEthPair.setCumulativePrices(daiEthCumPrice0_0, daiEthCumPrice1_0) - - rawOracle = await UniswapMedianTWAPOracle.new( - [ethUsdtPair.address, usdcEthPair.address, daiEthPair.address], - [ethDecimals, usdcDecimals, daiDecimals], - [usdtDecimals, ethDecimals, ethDecimals], - [ethUsdtTokensInReverseOrder, usdcEthTokensInReverseOrder, daiEthTokensInReverseOrder], { from: deployer }) - oracle = await GasMeasuredOracleWrapper.new(rawOracle.address, "uniswapMedianTWAP", { from: deployer }) - await oracle.cacheLatestPrice() - - await ethUsdtPair.setReserves(ethUsdtReserve0, ethUsdtReserve1, ethUsdtTimestamp_1) - await ethUsdtPair.setCumulativePrices(ethUsdtCumPrice0_1, ethUsdtCumPrice1_1) - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_1) - await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_1, usdcEthCumPrice1_1) - await daiEthPair.setReserves(daiEthReserve0, daiEthReserve1, daiEthTimestamp_1) - await daiEthPair.setCumulativePrices(daiEthCumPrice0_1, daiEthCumPrice1_1) - //await oracle.cacheLatestPrice() // Not actually needed, unless we do further testing moving timestamps further forward - }) - - it('returns the correct price', async () => { - const ethUsdtPrice1 = (await rawOracle.latestUniswapPair1TWAPPrice()) - const targetEthUsdtPriceNum = (ethUsdtCumPrice0_1.sub(ethUsdtCumPrice0_0)).mul(ethUsdtScaleFactor) - const targetEthUsdtPriceDenom = (ethUsdtTimestamp_1.sub(ethUsdtTimestamp_0)).mul(uniswapCumPriceScalingFactor) - const targetEthUsdtPrice1 = targetEthUsdtPriceNum.div(targetEthUsdtPriceDenom) - shouldEqualApprox(ethUsdtPrice1, targetEthUsdtPrice1) - - const usdcEthPrice1 = (await rawOracle.latestUniswapPair2TWAPPrice()) - const targetUsdcEthPriceNum = (usdcEthCumPrice1_1.sub(usdcEthCumPrice1_0)).mul(usdcEthScaleFactor) - const targetUsdcEthPriceDenom = (usdcEthTimestamp_1.sub(usdcEthTimestamp_0)).mul(uniswapCumPriceScalingFactor) - const targetUsdcEthPrice1 = targetUsdcEthPriceNum.div(targetUsdcEthPriceDenom) - shouldEqualApprox(usdcEthPrice1, targetUsdcEthPrice1) - - const daiEthPrice1 = (await rawOracle.latestUniswapPair3TWAPPrice()) - const targetDaiEthPriceNum = (daiEthCumPrice1_1.sub(daiEthCumPrice1_0)).mul(daiEthScaleFactor) - const targetDaiEthPriceDenom = (daiEthTimestamp_1.sub(daiEthTimestamp_0)).mul(uniswapCumPriceScalingFactor) - const targetDaiEthPrice1 = targetDaiEthPriceNum.div(targetDaiEthPriceDenom) - shouldEqualApprox(daiEthPrice1, targetDaiEthPrice1) - - const oraclePrice1 = (await oracle.latestPrice()) - const targetOraclePrice1 = median(ethUsdtPrice1, usdcEthPrice1, daiEthPrice1) - oraclePrice1.toString().should.equal(targetOraclePrice1.toString()) - }) - - it('returns the price in a transaction', async () => { - const oraclePrice = (await oracle.latestPrice()) - }) - }) - describe("with MedianOracle", () => { const [deployer] = accounts let rawOracle, oracle - let makerMedianizer, chainlinkAggregator, compoundView, usdcEthPair + let chainlinkAggregator, compoundView, usdcEthPair beforeEach(async () => { - //makerMedianizer = await Medianizer.new({ from: deployer }) - //await makerMedianizer.set(makerPriceWAD) - chainlinkAggregator = await Aggregator.new({ from: deployer }) await chainlinkAggregator.set(chainlinkPrice) @@ -349,7 +184,7 @@ contract('Oracle pricing', (accounts) => { await compoundView.set(compoundPrice) usdcEthPair = await UniswapV2Pair.new({ from: deployer }) - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_0) + await usdcEthPair.setReserves(0, 0, usdcEthTimestamp_0) await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_0, usdcEthCumPrice1_0) rawOracle = await MedianOracle.new(chainlinkAggregator.address, compoundView.address, @@ -357,7 +192,7 @@ contract('Oracle pricing', (accounts) => { oracle = await GasMeasuredOracleWrapper.new(rawOracle.address, "median", { from: deployer }) await oracle.cacheLatestPrice() - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_1) + await usdcEthPair.setReserves(0, 0, usdcEthTimestamp_1) await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_1, usdcEthCumPrice1_1) //await oracle.cacheLatestPrice() // Not actually needed, unless we do further testing moving timestamps further forward }) diff --git a/test/03_USM.test.js b/test/03_USM.test.js index 18c13d3..ab45068 100644 --- a/test/03_USM.test.js +++ b/test/03_USM.test.js @@ -39,13 +39,11 @@ contract('USM', (accounts) => { const compoundPrice = '414174999' // 6 dec places: see CompoundOpenOracle let usdcEthPair - const usdcDecimals = SIX // See UniswapMedianSpotOracle - const ethDecimals = EIGHTEEN // See UniswapMedianSpotOracle - const uniswapTokensInReverseOrder = true // See UniswapMedianSpotOracle + const usdcDecimals = SIX // See UniswapMedianTWAPOracle + const ethDecimals = EIGHTEEN // See UniswapMedianTWAPOracle + const uniswapTokensInReverseOrder = true // See UniswapMedianTWAPOracle - const usdcEthReserve0 = new BN('260787673159143') // From the USDC/ETH pair - const usdcEthReserve1 = new BN('696170744128378724814084') - const usdcEthCumPrice0_0 = new BN('307631784275278277546624451305316303382174855535226') + const usdcEthCumPrice0_0 = new BN('307631784275278277546624451305316303382174855535226') // From the USDC/ETH pair const usdcEthCumPrice1_0 = new BN('31377639132666967530700283664103') const usdcEthTimestamp_0 = new BN('1606780664') const usdcEthCumPrice0_1 = new BN('307634635050611880719301156089846577363471806696356') @@ -118,7 +116,7 @@ contract('USM', (accounts) => { await anchoredView.set(compoundPrice) usdcEthPair = await UniswapV2Pair.new({ from: deployer }) - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_0) + await usdcEthPair.setReserves(0, 0, usdcEthTimestamp_0) await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_0, usdcEthCumPrice1_0) // USM @@ -128,7 +126,7 @@ contract('USM', (accounts) => { fum = await FUM.at(await usm.fum()) await usm.cacheLatestPrice() - await usdcEthPair.setReserves(usdcEthReserve0, usdcEthReserve1, usdcEthTimestamp_1) + await usdcEthPair.setReserves(0, 0, usdcEthTimestamp_1) await usdcEthPair.setCumulativePrices(usdcEthCumPrice0_1, usdcEthCumPrice1_1) priceWAD = await usm.latestPrice()