work on stufF

Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
AlienTornadosaurusHex 2023-06-04 01:23:20 +00:00
parent 735546619e
commit cd2b889c2f
18 changed files with 769 additions and 5 deletions

View File

@ -2,7 +2,7 @@
This is my personal reconstruction of the Tornado `relayer-registry` repository which, although there is one being hosted with the same name on Github, is not actually the most up-to-date version of the repository, meaning that the contracts in that one are not the deployed ones.
The contract information here was taken directly off of Etherscan and the necessary dependencies added, everything organized. It is advisable to use MacOS or Linux for this repo, and the setup goes as follows:
The contract information here was taken directly off of Etherscan and the necessary dependencies added, everything organized. It is advisable to use a UNIX compatible system for this repo, and the setup goes as follows:
```bash
git clone https://development.tornadocash.community/thcydra/relayer-registry
@ -10,7 +10,7 @@ cd relayer-registry
yarn
forge install
yarn build
forge -b # or forge -b --use 0.6.12
forge build
```
`yarn build` has to execute a script to modify a single Uniswap dependency because of a stupid version pragma, if you check it out (in `script/setup.sh`) you will just notice that it goes into the file mentioned and changes the version pragma from 0.7.0 to 0.6.12. That is all of the changes needed.

View File

@ -1,4 +1,23 @@
[profile.default]
# General
src = 'src'
out = 'out'
libs = ["node_modules", "lib"]
# Compiler
auto_detect_solc = true
via_ir = true
optimizer = true
optimizer-runs = 1
# Network
chain_id = 1
rpc_endpoints = { mainnet = "${MAINNET_RPC_URL}" }
# Tests
verbosity = 2
# Remappings
remappings = [
'ds-test/=lib/ds-test/src/',
'solmate/=lib/solmate/src/',
@ -10,3 +29,17 @@ remappings = [
'@uniswap/v3-periphery/=lib/v3-periphery/',
'@uniswap/v3-core/=lib/v3-core/',
]
[fmt]
line_length = 110
number_underscore = 'thousands'
bracket_spacing = true
wrap_comments = true
ignore = [
"./src/v1/*",
"./src/v1/interfaces/*",
"./src/v1/libraries/*",
"./src/v1/staking/*",
"./src/v1/tornado-proxy/*",
"./src/v1/utils/*"
]

View File

@ -1,8 +1,12 @@
{
"name": "@T-Hax/relayer-registry",
"author": "T-Hax",
"name": "@AlienTornadosaurusHex/relayer-registry",
"author": "AlienTornadosaurusHex",
"version": "1.0.0",
"description": "Foundry reconstruction of the \"relayer-registry\" project.",
"repository": {
"type": "git",
"url": "https://git.tornado.ws/AlienTornadosaurusHex/relayer-registry"
},
"scripts": {
"postinstall": "chmod +x script/* && ./script/setup.sh",
"build": "chmod +x script/* && ./script/setup.sh",

236
src/v2/FeeOracleManager.sol Normal file
View File

@ -0,0 +1,236 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ Imports
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
// Tornado Imports
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
// Local Imports
import { InstanceRegistry } from "../v1/tornado-proxy/InstanceRegistry.sol";
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
struct FeeData {
uint160 feeAmount;
uint64 feePercent;
uint32 lastUpdated;
}
/**
* @title FeeManagerLegacyStorage
* @author AlienTornadosaurusHex
* @dev This is contract will help us layout storage properly for a proxy upgrade for the impl
* FeeOracleManager (formerly FeeManager).
*/
contract FeeManagerLegacyStorage {
uint24 public deprecatedUniswapTornPoolSwappingFee;
uint32 public deprecatedUniswapTimePeriod;
uint24 public feeUpdateInterval;
mapping(ITornadoInstance => uint160) public oldFeesForInstance;
mapping(ITornadoInstance => uint256) public oldFeesForInstanceUpdateTime;
}
/**
* @title FeeOracleManager
* @author AlienTornadosaurusHex
* @notice A contract which manages fee oracles and received data for other contracts to consume.
* @dev This is an improved version of the FeeManager with a modified design from the original contract.
*/
contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* @dev Divide protocol fee by this to get the percent value */
uint64 public constant PROTOCOL_FEE_DIVISOR = 1 ether;
/* @dev The address of the TORN token */
address public immutable tornTokenAddress;
/* @dev The Governance Proxy address */
address public immutable governanceProxyAddress;
/* @dev The InstanceRegistry contract */
InstanceRegistry public instanceRegistry;
/* @dev Each instance has a dedicated fee oracle, these only compute the values */
mapping(ITornadoInstance => IFeeOracle) public instanceFeeOracles;
/* @dev The data for each instance will be stored in this contract */
mapping(ITornadoInstance => FeeData) public feeDataForInstance;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
event FeeUpdated(address indexed instance, uint256 newFee);
event NewOracleSet(address indexed instance, address oracle);
event NewFeeUpdateIntervalSet(uint24 limit);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(address _tornTokenAddress, address _governanceProxyAddress) public {
tornTokenAddress = _tornTokenAddress;
governanceProxyAddress = _governanceProxyAddress;
}
modifier onlyGovernance() {
require(msg.sender == governanceProxyAddress, "FeeOracleManager: onlyGovernance");
_;
}
/**
* @dev If there will be a need to initialize the proxy again, simply pad storage and inherit again,
* making sure to not reference old data anywhere.
*/
function initialize(
address _uniswapPoolFeeOracle,
address _instanceRegistryAddress,
address[] calldata _instanceAddresses,
uint256[] calldata _feePercents
) external onlyGovernance initializer {
uint256 numInstances = _instanceAddresses.length;
for (uint256 i = 0; i < numInstances; i++) {
ITornadoInstance instance = ITornadoInstance(_instanceAddresses[i]);
feeDataForInstance[instance] = FeeData({
feeAmount: oldFeesForInstance[instance],
feePercent: uint64(_feePercents[i]),
lastUpdated: uint32(oldFeesForInstanceUpdateTime[instance])
});
instanceFeeOracles[instance] = IFeeOracle(_uniswapPoolFeeOracle);
}
instanceRegistry = InstanceRegistry(_instanceRegistryAddress);
}
function instanceFeeWithUpdate(ITornadoInstance _instance) public virtual returns (uint160) {
FeeData memory feeData = feeDataForInstance[_instance];
if (feeUpdateInterval < now - feeData.lastUpdated) {
feeData.feeAmount = updateFee(_instance);
}
return feeData.feeAmount;
}
function updateAllFees() public virtual returns (uint160[] memory newFees) {
return updateFees(instanceRegistry.getAllInstanceAddresses());
}
function updateFees(ITornadoInstance[] memory _instances)
public
virtual
returns (uint160[] memory newFees)
{
uint256 numInstances = _instances.length;
newFees = new uint160[](numInstances);
for (uint256 i = 0; i < numInstances; i++) {
newFees[i] = updateFee(_instances[i]);
}
}
function updateFee(ITornadoInstance _instance) public virtual returns (uint160 newFee) {
// This will revert if no contract is set
newFee = instanceFeeOracles[_instance].getFee();
feeDataForInstance[_instance] = FeeData({
feeAmount: newFee,
feePercent: feeDataForInstance[_instance].feePercent,
lastUpdated: uint32(now)
});
emit FeeUpdated(address(_instance), newFee);
return newFee;
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function setInstanceRegistry(address _newInstanceRegistryAddress) external onlyGovernance {
instanceRegistry = InstanceRegistry(_newInstanceRegistryAddress);
}
function setFeeOracle(address _instanceAddress, address _oracleAddress) external onlyGovernance {
ITornadoInstance instance = ITornadoInstance(_instanceAddress);
IFeeOracle oracle = IFeeOracle(_oracleAddress);
// Reverts if no oracle or does not conform to interface
uint160 fee = oracle.getFee();
// Ok, set the oracle
instanceFeeOracles[instance] = oracle;
// Note down updated fee
feeDataForInstance[instance] = FeeData({
feeAmount: fee,
feePercent: feeDataForInstance[instance].feePercent,
lastUpdated: uint32(now)
});
emit NewOracleSet(_instanceAddress, _oracleAddress);
emit FeeUpdated(_instanceAddress, fee);
}
function setFeePercentForInstance(ITornadoInstance _instance, uint64 _newFeePercent)
external
onlyGovernance
{
feeDataForInstance[_instance].feePercent = _newFeePercent;
}
function setFeeUpdateInterval(uint24 newLimit) external onlyGovernance {
feeUpdateInterval = newLimit;
emit NewFeeUpdateIntervalSet(newLimit);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function version() public pure virtual returns (string memory) {
return "v2-oracle-manager";
}
function getUpdatedFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
return instanceFeeOracles[instance].getFee();
}
function getLastFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
return uint160(feeDataForInstance[instance].feeAmount);
}
function getLastUpdatedTimeForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
return feeDataForInstance[instance].lastUpdated;
}
function getFeeDeviations() public view virtual returns (int256[] memory results) {
ITornadoInstance[] memory instances = instanceRegistry.getAllInstanceAddresses();
uint256 numInstances = instances.length;
results = new int256[](numInstances);
for (uint256 i = 0; i < numInstances; i++) {
ITornadoInstance instance = instances[i];
uint256 marketFee = instanceFeeOracles[instance].getFee();
int256 deviation;
if (marketFee != 0) {
deviation = int256((feeDataForInstance[instance].feeAmount * 1000) / marketFee) - 1000;
}
results[i] = deviation;
}
}
}

221
src/v2/InstanceRegistry.sol Normal file
View File

@ -0,0 +1,221 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ Imports
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
// Tornado Imports
import { EnsResolve } from "torn-token/contracts/ENS.sol";
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
// Local imports
import { TornadoRouter } from "./TornadoRouter.sol";
/**
* @title InstanceRegistry
* @author AlienTornadosaurusHex
* @notice A contract which enumerates Tornado Cash pool instances, and also stores essential data regarding
* them.
* @dev This is contract will help us layout storage properly for a proxy upgrade for the impl
* InstanceRegistry.
*/
contract InstanceRegistryLegacyStorage {
/* From first contract */
enum LegacyStatePlaceholder {
DISABLED,
ENABLED
}
/* From first contract */
struct LegacyInstanceStructPlaceholder {
bool deprecatedIsERC20;
address deprecatedToken;
LegacyStatePlaceholder deprecatedState;
uint24 deprecatedUniswapPoolSwappingFee;
uint32 deprecatedProtocolFeePercentage;
}
/* From Initializable.sol of first contract */
bool private _deprecatedInitialized;
/* From Initializable.sol of first contract */
bool private _deprecatedInitializing;
/* From first contract */
address private _deprecatedRouterAddress;
/* From first contract */
mapping(address => LegacyInstanceStructPlaceholder) private _deprecatedInstances;
/* From first contract */
ITornadoInstance[] private _deprecatedInstanceIds;
}
struct InstanceData {
IERC20 token;
uint80 index;
bool isERC20;
bool isEnabled;
}
/**
* @title InstanceRegistry
* @author AlienTornadosaurusHex
* @notice A contract which enumerates Tornado Cash pool instances, and also stores essential data regarding
* them.
* @dev This is an improved version of the InstanceRegistry with a modified design from the original contract.
*/
contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initializable {
using SafeERC20 for IERC20;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* @dev The address of the Governance proxy */
address public immutable governanceProxyAddress;
/* @dev Essential data regarding instances, see struct above */
mapping(ITornadoInstance => InstanceData) public instanceData;
/* @dev All instances enumerable */
ITornadoInstance[] public instances;
/* @dev The router which processes txs which we must command to approve tokens */
TornadoRouter public router;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
event NewRouterRegistered(address newRouterAddress);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(address _governanceProxyAddress) public {
governanceProxyAddress = _governanceProxyAddress;
}
modifier onlyGovernance() {
require(msg.sender == governanceProxyAddress, "InstanceRegistry: onlyGovernance");
_;
}
function initialize(address[] memory _instanceAddresses, address _router)
external
onlyGovernance
initializer
{
uint256 numInstances = _instanceAddresses.length;
router = TornadoRouter(_router);
for (uint256 i = 0; i < numInstances; i++) {
addInstance(ITornadoInstance(_instanceAddresses[i]));
}
}
function addInstance(ITornadoInstance _instance) public virtual onlyGovernance {
bool isEnabled = instanceData[_instance].isEnabled;
bool isERC20 = false;
IERC20 token;
// ETH instances do not know of a `token()` call
try _instance.token() returns (address _tokenAddress) {
token = IERC20(_tokenAddress);
isERC20 = true;
} catch {
/* It's an ETH instance, do nothing */
}
require(!isEnabled, "InstanceRegistry: can't add the same instance.");
if (isERC20) {
uint256 routerAllowanceForInstance = token.allowance(address(router), address(_instance));
if (routerAllowanceForInstance == 0) {
router.approveTokenForInstance(token, address(_instance), type(uint256).max);
}
}
instances.push(_instance);
instanceData[_instance] = InstanceData({
token: token,
index: uint64(instances.length - 1),
isERC20: token != IERC20(address(0)), // TODO: Is this true?
isEnabled: isEnabled
});
}
function removeInstanceByIndex(uint256 _instanceIndex) public virtual {
_removeInstanceByAddress(address(instances[_instanceIndex]));
}
function removeInstanceByAddress(address _instanceAddress) public virtual {
_removeInstanceByAddress(_instanceAddress);
}
function _removeInstanceByAddress(address _instanceAddress) internal virtual onlyGovernance {
ITornadoInstance instance = ITornadoInstance(_instanceAddress);
InstanceData memory data = instanceData[instance];
if (data.isERC20) {
uint256 routerAllowanceForInstance = data.token.allowance(address(router), _instanceAddress);
if (routerAllowanceForInstance != 0) {
router.approveTokenForInstance(data.token, _instanceAddress, 0);
}
}
instances[data.index] = instances[instances.length - 1];
instances.pop();
delete instanceData[instance];
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function setTornadoRouter(address _newRouterAddress) external onlyGovernance {
router = TornadoRouter(_newRouterAddress);
emit NewRouterRegistered(_newRouterAddress);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function version() public pure virtual returns (string memory) {
return "v2-oracle-manager";
}
function getAllInstances() public view virtual returns (ITornadoInstance[] memory allInstances) {
uint256 numInstances = instances.length;
allInstances = new ITornadoInstance[](instances.length);
for (uint256 i = 0; i < numInstances; i++) {
allInstances[i] = instances[i];
}
}
function getAllInstanceData() public view virtual returns (InstanceData[] memory data) {
uint256 numInstances = instances.length;
data = new InstanceData[](numInstances);
for (uint256 i = 0; i < numInstances; i++) {
data[i] = instanceData[instances[i]];
}
}
function getInstanceToken(ITornadoInstance _instance) public view virtual returns (IERC20) {
return instanceData[_instance].token;
}
function getInstanceIndex(ITornadoInstance _instance) public view virtual returns (uint80) {
return instanceData[_instance].index;
}
}

142
src/v2/TornadoRouter.sol Normal file
View File

@ -0,0 +1,142 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ Imports
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/Initializable.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { Math } from "@openzeppelin/contracts/math/Math.sol";
// Tornado Imports
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
// Local imports
import { RelayerRegistry } from "../v1/RelayerRegistry.sol";
import { InstanceRegistry, InstanceData } from "./InstanceRegistry.sol";
contract TornadoRouter is Initializable {
using SafeERC20 for IERC20;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
address public immutable governanceProxyAddress;
InstanceRegistry public instanceRegistry;
RelayerRegistry public relayerRegistry;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
event EncryptedNote(address indexed sender, bytes encryptedNote);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(address _governanceProxyAddress) public {
governanceProxyAddress = _governanceProxyAddress;
}
modifier onlyGovernance() {
require(msg.sender == governanceProxyAddress, "TornadoRouter: onlyGovernance");
_;
}
modifier onlyInstanceRegistry() {
require(msg.sender == address(instanceRegistry), "TornadoRouter: onlyInstanceRegistry");
_;
}
function initalize(address _instanceRegistryAddress, address _relayerRegistryAddress)
external
onlyGovernance
initializer
{
instanceRegistry = InstanceRegistry(_instanceRegistryAddress);
relayerRegistry = RelayerRegistry(_relayerRegistryAddress);
}
function deposit(ITornadoInstance _tornado, bytes32 _commitment, bytes calldata _encryptedNote)
public
payable
virtual
{
(IERC20 token,, bool isERC20, bool isEnabled) = instanceRegistry.instanceData(_tornado);
require(isEnabled, "TornadoRouter: instance not enabled");
if (isERC20) {
token.safeTransferFrom(msg.sender, address(this), _tornado.denomination());
}
_tornado.deposit{ value: msg.value }(_commitment);
emit EncryptedNote(msg.sender, _encryptedNote);
}
function withdraw(
ITornadoInstance _tornado,
bytes calldata _proof,
bytes32 _root,
bytes32 _nullifierHash,
address payable _recipient,
address payable _relayer,
uint256 _fee,
uint256 _refund
) public payable virtual {
(,,, bool isEnabled) = instanceRegistry.instanceData(_tornado);
require(isEnabled, "TornadoRouter: instance not enabled");
relayerRegistry.burn(msg.sender, _relayer, _tornado);
_tornado.withdraw{ value: msg.value }(
_proof, _root, _nullifierHash, _recipient, _relayer, _fee, _refund
);
}
function backupNotes(bytes[] calldata _encryptedNotes) public virtual {
for (uint256 i = 0; i < _encryptedNotes.length; i++) {
emit EncryptedNote(msg.sender, _encryptedNotes[i]);
}
}
function rescueTokens(IERC20 _token, address payable _to, uint256 _amount)
public
virtual
onlyGovernance
{
require(_to != address(0), "TORN: can not send to zero address");
if (_token == IERC20(0)) {
// for Ether
uint256 totalBalance = address(this).balance;
uint256 balance = Math.min(totalBalance, _amount);
_to.transfer(balance);
} else {
// any other erc20
uint256 totalBalance = _token.balanceOf(address(this));
uint256 balance = Math.min(totalBalance, _amount);
require(balance > 0, "TORN: trying to send 0 balance");
_token.safeTransfer(_to, balance);
}
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function approveTokenForInstance(IERC20 _token, address _spender, uint256 _amount)
external
onlyInstanceRegistry
{
_token.safeApprove(_spender, _amount);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
function version() public pure virtual returns (string memory) {
return "v2-oracle-manager";
}
}

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ imports
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
// Local imports
import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
contract UniswapV3FeeOracle is IFeeOracle {
function getFee() public view virtual override returns (uint160) { }
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
interface IFeeOracle {
function getFee() external view returns (uint160);
}

View File

@ -0,0 +1,103 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import { OracleLibrary } from "@uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol";
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import { LowGasSafeMath } from "@uniswap/v3-core/contracts/libraries/LowGasSafeMath.sol";
interface IERC20Decimals {
function decimals() external view returns (uint8);
}
library UniswapV3OracleHelper {
using LowGasSafeMath for uint256;
IUniswapV3Factory internal constant UniswapV3Factory =
IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 internal constant RATIO_DIVIDER = 1e18;
/**
* @notice This function should return the price of baseToken in quoteToken, as in: quote/base (WETH/TORN)
* @dev uses the Uniswap written OracleLibrary "getQuoteAtTick", does not call external libraries,
* uses decimals() for the correct power of 10
* @param baseToken token which will be denominated in quote token
* @param quoteToken token in which price will be denominated
* @param fee the uniswap pool fee, pools have different fees so this is a pool selector for our usecase
* @param period the amount of seconds we are going to look into the past for the new token price
* @return returns the price of baseToken in quoteToken
*
*/
function getPriceOfTokenInToken(address baseToken, address quoteToken, uint24 fee, uint32 period)
internal
view
returns (uint256)
{
uint128 base = uint128(10) ** uint128(IERC20Decimals(quoteToken).decimals());
if (baseToken == quoteToken) {
return base;
} else {
return OracleLibrary.getQuoteAtTick(
OracleLibrary.consult(UniswapV3Factory.getPool(baseToken, quoteToken, fee), period),
base,
baseToken,
quoteToken
);
}
}
/**
* @notice This function should return the price of token in WETH
* @dev simply feeds WETH in to the above function
* @param token token which will be denominated in WETH
* @param fee the uniswap pool fee, pools have different fees so this is a pool selector for our usecase
* @param period the amount of seconds we are going to look into the past for the new token price
* @return returns the price of token in WETH
*
*/
function getPriceOfTokenInWETH(address token, uint24 fee, uint32 period)
internal
view
returns (uint256)
{
return getPriceOfTokenInToken(token, WETH, fee, period);
}
/**
* @notice This function should return the price of WETH in token
* @dev simply feeds WETH into getPriceOfTokenInToken
* @param token token which WETH will be denominated in
* @param fee the uniswap pool fee, pools have different fees so this is a pool selector for our usecase
* @param period the amount of seconds we are going to look into the past for the new token price
* @return returns the price of token in WETH
*
*/
function getPriceOfWETHInToken(address token, uint24 fee, uint32 period)
internal
view
returns (uint256)
{
return getPriceOfTokenInToken(WETH, token, fee, period);
}
/**
* @notice This function returns the price of token[0] in token[1], but more precisely and importantly the
* price ratio of the tokens in WETH
* @dev this is done as to always have good prices due to WETH-token pools mostly always having the most
* liquidity
* @param tokens array of tokens to get ratio for
* @param fees the uniswap pool FEES, since these are two independent tokens
* @param period the amount of seconds we are going to look into the past for the new token price
* @return returns the price of token[0] in token[1]
*
*/
function getPriceRatioOfTokens(address[2] memory tokens, uint24[2] memory fees, uint32 period)
internal
view
returns (uint256)
{
return getPriceOfTokenInWETH(tokens[0], fees[0], period).mul(RATIO_DIVIDER)
/ getPriceOfTokenInWETH(tokens[1], fees[1], period);
}
}