Working oracle
Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
parent
1578f5ac69
commit
3f93127d19
@ -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();
|
||||
}
|
||||
|
@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
66
src/v2/interfaces/IUniswapV2Pair.sol
Normal file
66
src/v2/interfaces/IUniswapV2Pair.sol
Normal file
@ -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;
|
||||
}
|
64
src/v2/libraries/FixedPoint.sol
Normal file
64
src/v2/libraries/FixedPoint.sol
Normal file
@ -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);
|
||||
}
|
||||
}
|
43
src/v2/libraries/UniswapV2OracleLibrary.sol
Normal file
43
src/v2/libraries/UniswapV2OracleLibrary.sol
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user