From 3f93127d19013d7ec80dda6980b34c64029b2a98 Mon Sep 17 00:00:00 2001 From: AlienTornadosaurusHex <> Date: Fri, 16 Jun 2023 19:49:44 +0000 Subject: [PATCH] Working oracle Signed-off-by: AlienTornadosaurusHex <> --- src/v2/CurveFeeOracle.sol | 14 +- src/v2/UniswapFeeOracle.sol | 196 +++----------------- src/v2/interfaces/IUniswapV2Pair.sol | 66 +++++++ src/v2/libraries/FixedPoint.sol | 64 +++++++ src/v2/libraries/UniswapV2OracleLibrary.sol | 43 +++++ test/OracleTests.sol | 133 ++++++++++++- 6 files changed, 330 insertions(+), 186 deletions(-) create mode 100644 src/v2/interfaces/IUniswapV2Pair.sol create mode 100644 src/v2/libraries/FixedPoint.sol create mode 100644 src/v2/libraries/UniswapV2OracleLibrary.sol diff --git a/src/v2/CurveFeeOracle.sol b/src/v2/CurveFeeOracle.sol index 722c6ed..372ca98 100644 --- a/src/v2/CurveFeeOracle.sol +++ b/src/v2/CurveFeeOracle.sol @@ -195,7 +195,7 @@ contract CurveFeeOracle is IFeeOracle { /** * @notice We will not need the Uniswap V3 Oracle forever though, TORN/ETH pools are possible on Curve too */ - bool public tornOracleIsUniswapV3; + bool public tornOracleIsUniswap; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -208,7 +208,7 @@ contract CurveFeeOracle is IFeeOracle { constructor(address _governanceProxyAddress) public { governanceProxyAddress = _governanceProxyAddress; - tornOracleIsUniswapV3 = true; + tornOracleIsUniswap = true; } modifier onlyGovernance() { @@ -236,11 +236,11 @@ contract CurveFeeOracle is IFeeOracle { // If the below is true, then this will finalize the price and give the TOKEN price in TORN // Above MUST be TOKEN price in ETH, then (ETH/TOKEN)/(ETH/TORN)=TORN/TOKEN, meaning the TOKEN price // in TORN - if (tornOracleIsUniswapV3) { - price = price * UniswapV3OracleHelper.RATIO_DIVIDER - / uniswapFeeOracle.getTokenPerTORN( + if (tornOracleIsUniswap) { + price = price + * uniswapFeeOracle.getTORNPerToken( IERC20(UniswapV3OracleHelper.WETH), _instance.fee.updateInterval - ); + ) / UniswapV3OracleHelper.RATIO_DIVIDER; } // The price is now either prepared by the chained oracles or by Uniswap V3, in any case we do the @@ -304,7 +304,7 @@ contract CurveFeeOracle is IFeeOracle { } function setTornOracleIsUniswap(bool _is) public virtual onlyGovernance { - tornOracleIsUniswapV3 = _is; + tornOracleIsUniswap = _is; if (_is) emit TornOracleIsUniswapV3(); else emit TornOracleIsCurve(); } diff --git a/src/v2/UniswapFeeOracle.sol b/src/v2/UniswapFeeOracle.sol index 6197a71..0fffdfb 100644 --- a/src/v2/UniswapFeeOracle.sol +++ b/src/v2/UniswapFeeOracle.sol @@ -19,169 +19,18 @@ import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ // Local imports +import { IUniswapV2Pair } from "./interfaces/IUniswapV2Pair.sol"; + import { IFeeOracle, InstanceWithFee } from "./interfaces/IFeeOracle.sol"; import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol"; +import { UniswapV2OracleLibrary } from "./libraries/UniswapV2OracleLibrary.sol"; + +import { FixedPoint } from "./libraries/FixedPoint.sol"; + import { InstanceState } from "./InstanceRegistry.sol"; -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UNISWAP INLINED ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -interface IUniswapV2Pair { - event Approval(address indexed owner, address indexed spender, uint256 value); - event Transfer(address indexed from, address indexed to, uint256 value); - - function name() external pure returns (string memory); - function symbol() external pure returns (string memory); - function decimals() external pure returns (uint8); - function totalSupply() external view returns (uint256); - function balanceOf(address owner) external view returns (uint256); - function allowance(address owner, address spender) external view returns (uint256); - - function approve(address spender, uint256 value) external returns (bool); - function transfer(address to, uint256 value) external returns (bool); - function transferFrom(address from, address to, uint256 value) external returns (bool); - - function DOMAIN_SEPARATOR() external view returns (bytes32); - function PERMIT_TYPEHASH() external pure returns (bytes32); - function nonces(address owner) external view returns (uint256); - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - event Mint(address indexed sender, uint256 amount0, uint256 amount1); - event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); - event Swap( - address indexed sender, - uint256 amount0In, - uint256 amount1In, - uint256 amount0Out, - uint256 amount1Out, - address indexed to - ); - event Sync(uint112 reserve0, uint112 reserve1); - - function MINIMUM_LIQUIDITY() external pure returns (uint256); - function factory() external view returns (address); - function token0() external view returns (address); - function token1() external view returns (address); - function getReserves() - external - view - returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); - function price0CumulativeLast() external view returns (uint256); - function price1CumulativeLast() external view returns (uint256); - function kLast() external view returns (uint256); - - function mint(address to) external returns (uint256 liquidity); - function burn(address to) external returns (uint256 amount0, uint256 amount1); - function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; - function skim(address to) external; - function sync() external; - - function initialize(address, address) external; -} - -library FixedPoint { - // range: [0, 2**112 - 1] - // resolution: 1 / 2**112 - struct uq112x112 { - uint224 _x; - } - - // range: [0, 2**144 - 1] - // resolution: 1 / 2**112 - struct uq144x112 { - uint256 _x; - } - - uint8 private constant RESOLUTION = 112; - - // encode a uint112 as a UQ112x112 - function encode(uint112 x) internal pure returns (uq112x112 memory) { - return uq112x112(uint224(x) << RESOLUTION); - } - - // encodes a uint144 as a UQ144x112 - function encode144(uint144 x) internal pure returns (uq144x112 memory) { - return uq144x112(uint256(x) << RESOLUTION); - } - - // divide a UQ112x112 by a uint112, returning a UQ112x112 - function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { - require(x != 0, "FixedPoint: DIV_BY_ZERO"); - return uq112x112(self._x / uint224(x)); - } - - // multiply a UQ112x112 by a uint, returning a UQ144x112 - // reverts on overflow - function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) { - uint256 z; - require( - y == 0 || (z = uint256(self._x) * y) / y == uint256(self._x), - "FixedPoint: MULTIPLICATION_OVERFLOW" - ); - return uq144x112(z); - } - - // returns a UQ112x112 which represents the ratio of the numerator to the denominator - // equivalent to encode(numerator).div(denominator) - function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { - require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); - return uq112x112((uint224(numerator) << RESOLUTION) / denominator); - } - - // decode a UQ112x112 into a uint112 by truncating after the radix point - function decode(uq112x112 memory self) internal pure returns (uint112) { - return uint112(self._x >> RESOLUTION); - } - - // decode a UQ144x112 into a uint144 by truncating after the radix point - function decode144(uq144x112 memory self) internal pure returns (uint144) { - return uint144(self._x >> RESOLUTION); - } -} - -library UniswapV2OracleLibrary { - using FixedPoint for *; - - // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - - // 1] - function currentBlockTimestamp() internal view returns (uint32) { - return uint32(block.timestamp % 2 ** 32); - } - - // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. - function currentCumulativePrices(address pair) - internal - view - returns (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) - { - blockTimestamp = currentBlockTimestamp(); - price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast(); - price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast(); - - // if time has elapsed since the last update on the pair, mock the accumulated price values - (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); - if (blockTimestampLast != blockTimestamp) { - // subtraction overflow is desired - uint32 timeElapsed = blockTimestamp - blockTimestampLast; - // addition overflow is desired - // counterfactual - price0Cumulative += uint256(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; - // counterfactual - price1Cumulative += uint256(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; - } - } -} - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OUR CONTRACT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ struct TWAPData { @@ -195,6 +44,11 @@ contract UniswapFeeOracle is IFeeOracle { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + /** + * @notice Ratio for TWAP calcs + */ + uint256 public constant Q112Ratio = 1_000_000_000; + /** * @notice Address of the Uniswap V2 TORN pool */ @@ -237,9 +91,8 @@ contract UniswapFeeOracle is IFeeOracle { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ event PoolFeeUpdated(IERC20 token, uint24 newPoolFee); - event GlobalTORNPoolFeeUpdated(uint24 newPoolFee); - event GlobalTwapIntervalSecondsUpdated(uint32 newUniswapTimePeriod); - event GlobalMinObservationCardinalityUpdated(uint16 newMinObservationCardinality); + event MinObservationCardinalityUpdated(uint16 newMinObservationCardinality); + event FeeOracleManagerAddressUpdated(address newFeeOracleManagerAddress); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -258,12 +111,12 @@ contract UniswapFeeOracle is IFeeOracle { } modifier onlyGovernance() { - require(msg.sender == governanceProxyAddress, "UniswapV3FeeOracle: onlyGovernance"); + require(msg.sender == governanceProxyAddress, "UniswapFeeOracle: onlyGovernance"); _; } modifier onlyFeeOracleManager() { - require(msg.sender == feeOracleManagerAddress, "UniswapV3FeeOracle: onlyGovernance"); + require(msg.sender == feeOracleManagerAddress, "UniswapFeeOracle: onlyFeeOracleManager"); _; } @@ -284,12 +137,12 @@ contract UniswapFeeOracle is IFeeOracle { _instance.state.isERC20 ? _instance.state.token : IERC20(UniswapV3OracleHelper.WETH); // Now get the price ratio - uint256 priceRatio = getTokenPerTORN(_instance.state.token, _instance.fee.updateInterval); + uint256 priceRatio = getTORNPerToken(_instance.state.token, _instance.fee.updateInterval); // Denomination (tokens) times torn per token, through e18 because both are in e18, // times fee percent and then through for proper value return uint160( - _instance.logic.denomination().mul(1e18).div(priceRatio).mul(uint256(_instance.fee.percent)).div( + _instance.logic.denomination().mul(priceRatio).div(1e18).mul(uint256(_instance.fee.percent)).div( uint256(_instance.fee.divisor) ) ); @@ -315,7 +168,7 @@ contract UniswapFeeOracle is IFeeOracle { // Save TWAP data, meaning the average price (TORN per ETH) last = TWAPData({ averagePrice: FixedPoint.uq112x112( - uint224((_price1CumulativeLast - lastCumulativeTORNPriceInETH) / elapsed) + uint224(((_price1CumulativeLast - lastCumulativeTORNPriceInETH) * Q112Ratio) / elapsed) ), updatedTimestamp: currentTimestamp }); @@ -325,20 +178,20 @@ contract UniswapFeeOracle is IFeeOracle { } } - function getTokenPerTORN(IERC20 _token, uint32 _interval) public view virtual returns (uint256) { + function getTORNPerToken(IERC20 _token, uint32 _interval) public view virtual returns (uint256) { // Get the average price of TOKEN in WETH uint256 ethPerTokenAverage = UniswapV3OracleHelper.getPriceOfTokenInWETH(address(_token), poolFeesByToken[_token], _interval); // TORN PER ETH * ETH PER TOKEN = TORN per TOKEN - return ethPerTokenAverage * last.averagePrice.decode() / 1e18; + return ethPerTokenAverage * last.averagePrice.decode() / Q112Ratio; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ function setPoolFeeForToken(IERC20 _token, uint24 _tokenPoolFee) public virtual onlyGovernance { // Check whether cardinality is initialized - require(minObservationCardinality != 0, "UniswapV3FeeOracle: cardinality not initialized"); + require(minObservationCardinality != 0, "UniswapFeeOracle: cardinality not initialized"); // Only do this if not zeroing out if (_tokenPoolFee != 0) { @@ -348,7 +201,7 @@ contract UniswapFeeOracle is IFeeOracle { address(_token), UniswapV3OracleHelper.WETH, _tokenPoolFee ); - require(poolAddress != address(0), "UniswapV3FeeOracle: pool for token and fee does not exist"); + require(poolAddress != address(0), "UniswapFeeOracle: pool for token and fee does not exist"); // Check whether the pool has a large enough observation cardinality @@ -356,7 +209,7 @@ contract UniswapFeeOracle is IFeeOracle { require( minObservationCardinality <= observationCardinalityNext, - "UniswapV3FeeOracle: pool observation cardinality low" + "UniswapFeeOracle: pool observation cardinality low" ); } @@ -373,11 +226,12 @@ contract UniswapFeeOracle is IFeeOracle { onlyGovernance { minObservationCardinality = _newGlobalMinObservationCardinality; - emit GlobalMinObservationCardinalityUpdated(_newGlobalMinObservationCardinality); + emit MinObservationCardinalityUpdated(_newGlobalMinObservationCardinality); } function setFeeOracleManagerAddress(address _newFeeOracleManagerAddress) public virtual onlyGovernance { feeOracleManagerAddress = _newFeeOracleManagerAddress; + emit FeeOracleManagerAddressUpdated(_newFeeOracleManagerAddress); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/src/v2/interfaces/IUniswapV2Pair.sol b/src/v2/interfaces/IUniswapV2Pair.sol new file mode 100644 index 0000000..1e8dd41 --- /dev/null +++ b/src/v2/interfaces/IUniswapV2Pair.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +interface IUniswapV2Pair { + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); + + function name() external pure returns (string memory); + function symbol() external pure returns (string memory); + function decimals() external pure returns (uint8); + function totalSupply() external view returns (uint256); + function balanceOf(address owner) external view returns (uint256); + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 value) external returns (bool); + function transfer(address to, uint256 value) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); + + function DOMAIN_SEPARATOR() external view returns (bytes32); + function PERMIT_TYPEHASH() external pure returns (bytes32); + function nonces(address owner) external view returns (uint256); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); + event Swap( + address indexed sender, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, + address indexed to + ); + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint256); + function factory() external view returns (address); + function token0() external view returns (address); + function token1() external view returns (address); + function getReserves() + external + view + returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); + function price0CumulativeLast() external view returns (uint256); + function price1CumulativeLast() external view returns (uint256); + function kLast() external view returns (uint256); + + function mint(address to) external returns (uint256 liquidity); + function burn(address to) external returns (uint256 amount0, uint256 amount1); + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external; + function skim(address to) external; + function sync() external; + + function initialize(address, address) external; +} diff --git a/src/v2/libraries/FixedPoint.sol b/src/v2/libraries/FixedPoint.sol new file mode 100644 index 0000000..5c5f440 --- /dev/null +++ b/src/v2/libraries/FixedPoint.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +library FixedPoint { + // range: [0, 2**112 - 1] + // resolution: 1 / 2**112 + struct uq112x112 { + uint224 _x; + } + + // range: [0, 2**144 - 1] + // resolution: 1 / 2**112 + struct uq144x112 { + uint256 _x; + } + + uint8 private constant RESOLUTION = 112; + + // encode a uint112 as a UQ112x112 + function encode(uint112 x) internal pure returns (uq112x112 memory) { + return uq112x112(uint224(x) << RESOLUTION); + } + + // encodes a uint144 as a UQ144x112 + function encode144(uint144 x) internal pure returns (uq144x112 memory) { + return uq144x112(uint256(x) << RESOLUTION); + } + + // divide a UQ112x112 by a uint112, returning a UQ112x112 + function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) { + require(x != 0, "FixedPoint: DIV_BY_ZERO"); + return uq112x112(self._x / uint224(x)); + } + + // multiply a UQ112x112 by a uint, returning a UQ144x112 + // reverts on overflow + function mul(uq112x112 memory self, uint256 y) internal pure returns (uq144x112 memory) { + uint256 z; + require( + y == 0 || (z = uint256(self._x) * y) / y == uint256(self._x), + "FixedPoint: MULTIPLICATION_OVERFLOW" + ); + return uq144x112(z); + } + + // returns a UQ112x112 which represents the ratio of the numerator to the denominator + // equivalent to encode(numerator).div(denominator) + function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) { + require(denominator > 0, "FixedPoint: DIV_BY_ZERO"); + return uq112x112((uint224(numerator) << RESOLUTION) / denominator); + } + + // decode a UQ112x112 into a uint112 by truncating after the radix point + function decode(uq112x112 memory self) internal pure returns (uint112) { + return uint112(self._x >> RESOLUTION); + } + + // decode a UQ144x112 into a uint144 by truncating after the radix point + function decode144(uq144x112 memory self) internal pure returns (uint144) { + return uint144(self._x >> RESOLUTION); + } +} diff --git a/src/v2/libraries/UniswapV2OracleLibrary.sol b/src/v2/libraries/UniswapV2OracleLibrary.sol new file mode 100644 index 0000000..ed78322 --- /dev/null +++ b/src/v2/libraries/UniswapV2OracleLibrary.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +// Local imports + +import { IUniswapV2Pair } from "../interfaces/IUniswapV2Pair.sol"; + +import { FixedPoint } from "./FixedPoint.sol"; + +library UniswapV2OracleLibrary { + using FixedPoint for *; + + // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - + // 1] + function currentBlockTimestamp() internal view returns (uint32) { + return uint32(block.timestamp % 2 ** 32); + } + + // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. + function currentCumulativePrices(address pair) + internal + view + returns (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) + { + blockTimestamp = currentBlockTimestamp(); + price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast(); + price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast(); + + // if time has elapsed since the last update on the pair, mock the accumulated price values + (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); + if (blockTimestampLast != blockTimestamp) { + // subtraction overflow is desired + uint32 timeElapsed = blockTimestamp - blockTimestampLast; + // addition overflow is desired + // counterfactual + price0Cumulative += uint256(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; + // counterfactual + price1Cumulative += uint256(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; + } + } +} diff --git a/test/OracleTests.sol b/test/OracleTests.sol index 578348c..2779af8 100644 --- a/test/OracleTests.sol +++ b/test/OracleTests.sol @@ -19,31 +19,84 @@ import { console2 } from "forge-std/console2.sol"; // Local imports -import { FeeOracleManager, FeeDataForOracle, InstanceWithFee } from "src/v2/FeeOracleManager.sol"; +import { TornadoProposalTest } from "./TornadoProposalTest.sol"; import { IGovernance, Proposal } from "common/interfaces/IGovernance.sol"; +import { IUniswapV2Pair } from "src/v2/interfaces/IUniswapV2Pair.sol"; + import { TornadoAddresses } from "common/TornadoAddresses.sol"; +import { FeeOracleManager, FeeDataForOracle, InstanceWithFee } from "src/v2/FeeOracleManager.sol"; + import { UniswapFeeOracle } from "src/v2/UniswapFeeOracle.sol"; import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol"; import { InstanceState } from "src/v2/InstanceRegistry.sol"; -contract OracleTests is Test { +interface IWethDepositable { + function deposit() external payable; +} + +contract Instances { + /* ETH instances */ + ITornadoInstance public constant eth01 = ITornadoInstance(0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc); + ITornadoInstance public constant eth1 = ITornadoInstance(0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936); + ITornadoInstance public constant eth10 = ITornadoInstance(0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF); + ITornadoInstance public constant eth100 = ITornadoInstance(0xA160cdAB225685dA1d56aa342Ad8841c3b53f291); + + /* DAI instances */ + ITornadoInstance public constant dai100 = ITornadoInstance(0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3); + ITornadoInstance public constant dai1000 = ITornadoInstance(0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144); + ITornadoInstance public constant dai10000 = ITornadoInstance(0x07687e702b410Fa43f4cB4Af7FA097918ffD2730); + ITornadoInstance public constant dai100000 = ITornadoInstance(0x23773E65ed146A459791799d01336DB287f25334); + + /* cDAI instances */ + ITornadoInstance public constant cdai100 = ITornadoInstance(0x22aaA7720ddd5388A3c0A3333430953C68f1849b); + ITornadoInstance public constant cdai1000 = ITornadoInstance(0x03893a7c7463AE47D46bc7f091665f1893656003); + ITornadoInstance public constant cdai10000 = ITornadoInstance(0x2717c5e28cf931547B621a5dddb772Ab6A35B701); + ITornadoInstance public constant cdai100000 = ITornadoInstance(0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af); + + /* USDT instances */ + ITornadoInstance public constant usdt100 = ITornadoInstance(0x169AD27A470D064DEDE56a2D3ff727986b15D52B); + ITornadoInstance public constant usdt1000 = ITornadoInstance(0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f); + + /* WBTC instances */ + ITornadoInstance public constant wbtc01 = ITornadoInstance(0x178169B423a011fff22B9e3F3abeA13414dDD0F1); + ITornadoInstance public constant wbtc1 = ITornadoInstance(0x610B717796ad172B316836AC95a2ffad065CeaB4); + ITornadoInstance public constant wbtc10 = ITornadoInstance(0xbB93e510BbCD0B7beb5A853875f9eC60275CF498); + + /* CRVUSD instances */ + ITornadoInstance public constant cu100 = ITornadoInstance(0x913a73486Dc4AA3832A56d461542836C1eeB93be); + ITornadoInstance public constant cu1_000 = ITornadoInstance(0x5A6b3C829dB3e938C885000c6E93CF35E74876a4); + ITornadoInstance public constant cu10_000 = ITornadoInstance(0x49f173CDAB99a2C3800F1255393DF9B7a17B82Bb); + ITornadoInstance public constant cu100_000 = ITornadoInstance(0x4640Dffc9fD0B113B983e3A350b070a119CA143C); + ITornadoInstance public constant cu1_000_000 = + ITornadoInstance(0xc4eA8Bd3Fd76f3c255395793B47F7c55aD59d991); +} + +contract OracleTests is Instances, TornadoProposalTest { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + // Tokens - constant + + IERC20 internal constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + + IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); + + // Contracts - constant + FeeOracleManager public constant feeOracleManager = FeeOracleManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7); + IUniswapV2Pair public constant uniTornPool = IUniswapV2Pair(0x0C722a487876989Af8a05FFfB6e32e45cc23FB3A); + address public constant crvUSDUSDCStableswap2Pool = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E; address public constant tricryptoUSDCPool = 0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B; - ITornadoInstance public constant cu10_000 = ITornadoInstance(0x49f173CDAB99a2C3800F1255393DF9B7a17B82Bb); - - IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); + // Contracts - mutable UniswapFeeOracle uniswapFeeOracle; @@ -51,11 +104,14 @@ contract OracleTests is Test { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - function setUp() public { + function setUp() public virtual override { vm.createSelectFork(vm.envString("MAINNET_RPC_URL")); + vm.rollFork(17_493_778); + + uniswapFeeOracle = new UniswapFeeOracle(address(this), address(this)); - uniswapFeeOracle = new UniswapFeeOracle(address(this), address(feeOracleManager)); uniswapFeeOracle.setMinObservationCardinality(10); + uniswapFeeOracle.setPoolFeeForToken(WETH, 0); feeOracle = new CurveFeeOracle(address(this)); feeOracle.setTornOracleIsUniswap(false); @@ -82,10 +138,23 @@ contract OracleTests is Test { } function test_curveFeeChainedTORN() public { + uint256 timeBeforeSwap = now; + feeOracle.setTornOracleIsUniswap(true); _setCurveFeeChainedOracleForInstance(feeOracle, cu10_000); // CRVUSD 10_000 + _advanceTORNETHMarket(); + + uniswapFeeOracle.update( + TORN, + InstanceWithFee({ + logic: cu10_000, + state: InstanceState(IERC20(address(0)), 0, false, false), + fee: FeeDataForOracle(0, 0, 0, 0, uint32(timeBeforeSwap)) + }) + ); + console2.log( "\nTORN Fee calculated ------------------------------------------------------\n", uint256( @@ -94,7 +163,7 @@ contract OracleTests is Test { InstanceWithFee({ logic: cu10_000, state: InstanceState(IERC20(cu10_000.token()), 0, true, true), - fee: FeeDataForOracle(0, 30, 10_000, 2 days, 0) + fee: FeeDataForOracle(0, 30, 10_000, 2 days, uint32(timeBeforeSwap)) }) ) ), @@ -102,8 +171,56 @@ contract OracleTests is Test { ); } + function test_UniswapTORNOracle() public { + uint256 timeBeforeSwap = now; + + _advanceTORNETHMarket(); + + InstanceWithFee memory feepool = InstanceWithFee({ + logic: eth100, + state: InstanceState(WETH, 0, false, true), + fee: FeeDataForOracle(0, 30, 10_000, 2 days, uint32(timeBeforeSwap)) + }); + + uniswapFeeOracle.update(TORN, feepool); + + console2.log( + "\nTORN Fee calculated ------------------------------------------------------\n", + uniswapFeeOracle.getFee(TORN, feepool), + "\n------------------------------------------------------------------------\n" + ); + } + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + function _advanceTORNETHMarket() internal { + console2.log("\ntrader is now going to move the price\n"); + + uint256 lastPriceAvg0 = uniTornPool.price0CumulativeLast(); + uint256 lastPriceAvg1 = uniTornPool.price0CumulativeLast(); + + uint256 rn = now; + address trader = address(bytes20(keccak256("trader"))); + + vm.deal(trader, 20 ether); + vm.prank(trader); + IWethDepositable(address(WETH)).deposit{ value: 19 ether }(); + + require(WETH.balanceOf(trader) == 19 ether, "deposit for weth"); + + vm.warp(rn + 2 days + 1 hours); + + vm.prank(trader); + WETH.transfer(address(uniTornPool), 19 ether); + + uniTornPool.swap(6_600_000_000_000_000_000_000, 0, trader, new bytes(0)); + + console2.log("\ntrader received =>", TORN.balanceOf(trader), "TORN\n"); + + require(lastPriceAvg0 != uniTornPool.price0CumulativeLast(), "twap moving 0 fail"); + require(lastPriceAvg1 != uniTornPool.price1CumulativeLast(), "twap moving 1 fail"); + } + function _setCurveFeeSimpleTricryptoOracleForInstance( CurveFeeOracle _feeOracle, ITornadoInstance _instance