2023-06-04 04:23:20 +03:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
pragma solidity ^0.6.12;
|
|
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
|
|
|
|
// OZ imports
|
|
|
|
|
|
|
|
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
2023-06-05 01:37:03 +03:00
|
|
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
|
|
|
|
|
// Uniswap imports
|
|
|
|
|
|
|
|
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
|
|
|
|
import { IUniswapV3PoolState } from "@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol";
|
|
|
|
|
|
|
|
// Tornado imports
|
|
|
|
|
|
|
|
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
|
2023-06-04 04:23:20 +03:00
|
|
|
|
|
|
|
// Local imports
|
|
|
|
|
|
|
|
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
|
|
|
|
2023-06-05 01:37:03 +03:00
|
|
|
import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
|
|
|
|
|
|
|
|
import { InstanceData } from "./InstanceRegistry.sol";
|
|
|
|
|
|
|
|
struct UniswapV3FeeData {
|
|
|
|
uint24 tornPoolFee;
|
|
|
|
uint24 tokenPoolFee;
|
|
|
|
uint32 timePeriod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @title UniswapV3FeeOracle
|
|
|
|
* @author AlienTornadosaurusHex
|
|
|
|
* @notice A TORN fee oracle for any Uniswap V3 token pool.
|
|
|
|
*/
|
2023-06-04 04:23:20 +03:00
|
|
|
contract UniswapV3FeeOracle is IFeeOracle {
|
2023-06-05 01:37:03 +03:00
|
|
|
using SafeMath for uint256;
|
|
|
|
|
|
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
|
|
|
|
/* @dev The Governance Proxy address */
|
|
|
|
address public immutable governanceProxyAddress;
|
|
|
|
|
|
|
|
/* @dev The Uniswap V3 Factory */
|
|
|
|
IUniswapV3Factory public immutable v3Factory;
|
|
|
|
|
|
|
|
/* @dev The global pool fee value which will always be used for TORN */
|
|
|
|
uint24 public globalTornUniswapPoolFee;
|
|
|
|
|
|
|
|
/* @dev The global time period value which will be used for all calculations */
|
|
|
|
uint32 public globalUniswapTimePeriod;
|
|
|
|
|
|
|
|
/* @dev The minimum observation cardinality required for a token */
|
|
|
|
uint32 public globalMinObservationCardinality;
|
|
|
|
|
|
|
|
/* @dev The feeData in which the above + token specific will be stored */
|
|
|
|
mapping(IERC20 => UniswapV3FeeData) public uniswapFeeData;
|
|
|
|
|
|
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
|
|
|
|
event UniswapPoolFeeUpdated(IERC20 token, uint24 newUniswapPoolFee);
|
|
|
|
event GlobalTornUniswapPoolFeeUpdated(uint24 newUniswapPoolFee);
|
|
|
|
event GlobalUniswapTimePeriodUpdated(uint32 newUniswapTimePeriod);
|
|
|
|
event GlobalMinObservationCardinalityUpdated(uint16 newMinObservationCardinality);
|
|
|
|
|
|
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
|
|
|
|
constructor(address _governanceProxyAddress, address _uniswapV3FactoryAddress) public {
|
|
|
|
governanceProxyAddress = _governanceProxyAddress;
|
|
|
|
v3Factory = IUniswapV3Factory(_uniswapV3FactoryAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
modifier onlyGovernance() {
|
|
|
|
require(msg.sender == governanceProxyAddress, "UniswapV3FeeOracle: onlyGovernance");
|
|
|
|
_;
|
|
|
|
}
|
|
|
|
|
|
|
|
function setGlobalTornUniswapPoolFee(uint24 _newGlobalTornUniswapPoolFee) public virtual onlyGovernance {
|
|
|
|
globalTornUniswapPoolFee = _newGlobalTornUniswapPoolFee;
|
|
|
|
emit GlobalTornUniswapPoolFeeUpdated(_newGlobalTornUniswapPoolFee);
|
|
|
|
}
|
|
|
|
|
|
|
|
function setGlobalUniswapTimePeriod(uint32 _newGlobalUniswapTimePeriod) public virtual onlyGovernance {
|
|
|
|
globalUniswapTimePeriod = _newGlobalUniswapTimePeriod;
|
|
|
|
emit GlobalUniswapTimePeriodUpdated(_newGlobalUniswapTimePeriod);
|
|
|
|
}
|
|
|
|
|
|
|
|
function setGlobalMinObservationCardinality(uint16 _newGlobalMinObservationCardinality)
|
|
|
|
public
|
|
|
|
virtual
|
|
|
|
onlyGovernance
|
|
|
|
{
|
|
|
|
globalMinObservationCardinality = _newGlobalMinObservationCardinality;
|
|
|
|
emit GlobalMinObservationCardinalityUpdated(_newGlobalMinObservationCardinality);
|
|
|
|
}
|
|
|
|
|
|
|
|
function setUniswapPoolFeeForToken(IERC20 _token, uint24 _tokenUniswapPoolFee)
|
|
|
|
public
|
|
|
|
virtual
|
|
|
|
onlyGovernance
|
|
|
|
{
|
|
|
|
uint24 _globalTornUniswapPoolFee = globalTornUniswapPoolFee;
|
|
|
|
uint32 _globalUniswapTimePeriod = globalUniswapTimePeriod;
|
|
|
|
|
|
|
|
require(_globalTornUniswapPoolFee != 0, "UniswapV3FeeOracle: torn pool fee not initialized");
|
|
|
|
require(_globalUniswapTimePeriod != 0, "UniswapV3FeeOracle: time period not initialized");
|
|
|
|
|
|
|
|
address poolAddress =
|
|
|
|
v3Factory.getPool(address(_token), UniswapV3OracleHelper.WETH, _tokenUniswapPoolFee);
|
|
|
|
|
|
|
|
require(poolAddress != address(0), "UniswapV3FeeOracle: pool for token and fee does not exist");
|
|
|
|
|
|
|
|
(,,,, uint16 observationCardinalityNext,,) = IUniswapV3PoolState(poolAddress).slot0();
|
|
|
|
|
|
|
|
require(
|
|
|
|
globalMinObservationCardinality <= observationCardinalityNext,
|
|
|
|
"UniswapV3FeeOracle: pool observation cardinality low"
|
|
|
|
);
|
|
|
|
|
|
|
|
uniswapFeeData[_token] = UniswapV3FeeData({
|
|
|
|
tornPoolFee: _globalTornUniswapPoolFee,
|
|
|
|
tokenPoolFee: _tokenUniswapPoolFee,
|
|
|
|
timePeriod: _globalUniswapTimePeriod
|
|
|
|
});
|
|
|
|
|
|
|
|
emit UniswapPoolFeeUpdated(_token, _tokenUniswapPoolFee);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getFee(
|
|
|
|
IERC20 _torn,
|
|
|
|
ITornadoInstance _instance,
|
|
|
|
InstanceData memory _data,
|
|
|
|
uint64 _feePercent,
|
|
|
|
uint64 _feePercentDivisor
|
|
|
|
) public view virtual override returns (uint160) {
|
|
|
|
// For safety, we won't allow calculating fees for disabled instances
|
|
|
|
require(_data.isEnabled, "UniswapV3FeeOracle: instance not enabled");
|
|
|
|
|
|
|
|
// If fee is 0 return
|
|
|
|
if (_feePercent == 0) return 0;
|
|
|
|
|
|
|
|
// If it's not an ERC20 it has to be ETH, use the WETH token
|
|
|
|
_data.token = _data.isERC20 ? _data.token : IERC20(UniswapV3OracleHelper.WETH);
|
|
|
|
|
|
|
|
// After we have decided our token get fee data for it
|
|
|
|
UniswapV3FeeData memory uniFeeData = uniswapFeeData[_data.token];
|
|
|
|
|
|
|
|
// Calc price ratio
|
|
|
|
uint256 tokenPriceRatio = UniswapV3OracleHelper.getPriceRatioOfTokens(
|
|
|
|
[address(_torn), address(_data.token)],
|
|
|
|
[uniFeeData.tornPoolFee, uniFeeData.tokenPoolFee],
|
|
|
|
uniFeeData.timePeriod
|
|
|
|
);
|
|
|
|
|
|
|
|
// And now all according to legacy calculation
|
|
|
|
return uint160(
|
|
|
|
_instance.denomination().mul(UniswapV3OracleHelper.RATIO_DIVIDER).div(tokenPriceRatio).mul(
|
|
|
|
uint256(_feePercent)
|
|
|
|
).div(_feePercentDivisor)
|
|
|
|
);
|
|
|
|
}
|
2023-06-04 04:23:20 +03:00
|
|
|
}
|