Write proposals logic

Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
AlienTornadosaurusHex 2023-06-08 23:21:32 +00:00
parent deebb13953
commit 19f7d15c6f
5 changed files with 443 additions and 12 deletions

@ -41,5 +41,6 @@ ignore = [
"./src/v1/libraries/*",
"./src/v1/staking/*",
"./src/v1/tornado-proxy/*",
"./src/v1/utils/*"
"./src/v1/utils/*",
"./src/common/AdminUpgradeableProxy.sol"
]

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "@openzeppelin/contracts/proxy/TransparentUpgradeableProxy.sol";
/**
* @dev TransparentUpgradeableProxy where admin is allowed to call implementation methods.
*/
contract AdminUpgradeableProxy is TransparentUpgradeableProxy {
/**
* @dev Initializes an upgradeable proxy backed by the implementation at `_logic`.
*/
constructor(
address _logic,
address _admin,
bytes memory _data
) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
/**
* @dev Override to allow admin access the fallback function.
*/
function _beforeFallback() internal override {}
}

@ -0,0 +1,162 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ Imports
import { AdminUpgradeableProxy } from "../common/AdminUpgradeableProxy.sol";
// Tornado imports
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
// Local V2 imports
import { FeeOracleManager } from "../v2/FeeOracleManager.sol";
import { InstanceRegistry } from "../v2/InstanceRegistry.sol";
import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "../v2/CurveFeeOracle.sol";
import { UniswapV3FeeOracle } from "../v2/UniswapV3FeeOracle.sol";
import { TornadoRouter } from "../v2/TornadoRouter.sol";
/**
* @title CRVUSDInstancesProposal
* @author AlienTornadosaurusHex
* @notice Proposal to add crvUSD instances in 100, 1000, 10000, 100000, 1000000 denominations.
*/
contract CRVUSDInstancesProposal {
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* @dev The first Curve pool which is capable of being used as a safe oracle due to analytical EMA price */
address public constant crvUSDUSDCStableswap2Pool = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E;
/* @dev The second Curve pool which is capable of being used as a safe oracle due to analytical EMA price */
address public constant tricryptoUSDCPool = 0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B;
/* @dev The InstanceRegistry contract, for registering instances, proxy must be upgraded */
InstanceRegistry public constant instanceRegistry =
InstanceRegistry(0xB20c66C4DE72433F3cE747b58B86830c459CA911);
/* @dev The FeeOracleManager contract, proxy must be upgraded */
FeeOracleManager public constant feeOracleManager =
FeeOracleManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7);
/* @dev This is the address of the Uniswap V3 Oracle which we will use for all of our traditional
instances, but it will also help the Curve instances, the former must have been deployed with the address
of this */
address public immutable deployedUniswapV3FeeOracleAddress;
/* @dev This is the CurveFeeOracle contract which will be deployed beforehand and which will be able to
use multiple Curve pools as oracles, at once. */
CurveFeeOracle public immutable curveFeeOracle;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(address _deployedCurveFeeOracleAddress, address _deployedUniswapV3FeeOracleAddress) public {
curveFeeOracle = CurveFeeOracle(_deployedCurveFeeOracleAddress);
deployedUniswapV3FeeOracleAddress = _deployedUniswapV3FeeOracleAddress;
}
/**
* @dev This function also executes further internal functions inlined below.
*/
function executeProposal() external {
// Load the deployed crvUSD instances
ITornadoInstance cu100 = ITornadoInstance(0x913a73486Dc4AA3832A56d461542836C1eeB93be);
ITornadoInstance cu1_000 = ITornadoInstance(0x5A6b3C829dB3e938C885000c6E93CF35E74876a4);
ITornadoInstance cu10_000 = ITornadoInstance(0x49f173CDAB99a2C3800F1255393DF9B7a17B82Bb);
ITornadoInstance cu100_000 = ITornadoInstance(0x4640Dffc9fD0B113B983e3A350b070a119CA143C);
ITornadoInstance cu1_000_000 = ITornadoInstance(0xc4eA8Bd3Fd76f3c255395793B47F7c55aD59d991);
// Ok, first add the Uniswap V3 Oracle to the contract
curveFeeOracle.setUniswapV3FeeOracle(UniswapV3FeeOracle(deployedUniswapV3FeeOracleAddress));
// Then, add necessary oracles for the CRVUSD price, to the CurveFeeOracle
_setCurveFeeChainedOracleForInstance(curveFeeOracle, cu100);
_setCurveFeeChainedOracleForInstance(curveFeeOracle, cu1_000);
_setCurveFeeChainedOracleForInstance(curveFeeOracle, cu10_000);
_setCurveFeeChainedOracleForInstance(curveFeeOracle, cu100_000);
_setCurveFeeChainedOracleForInstance(curveFeeOracle, cu1_000_000);
// Then, add the instances to the InstanceRegistry
instanceRegistry.addInstance(cu100);
instanceRegistry.addInstance(cu1_000);
instanceRegistry.addInstance(cu10_000);
instanceRegistry.addInstance(cu100_000);
instanceRegistry.addInstance(cu1_000_000);
// Finally, set all necessary data in the FeeOracleManager
feeOracleManager.setFeeOracle(address(cu100), address(curveFeeOracle));
feeOracleManager.setFeeOracle(address(cu1_000), address(curveFeeOracle));
feeOracleManager.setFeeOracle(address(cu10_000), address(curveFeeOracle));
feeOracleManager.setFeeOracle(address(cu100_000), address(curveFeeOracle));
feeOracleManager.setFeeOracle(address(cu1_000_000), address(curveFeeOracle));
// The fee will be 0.3 % for those pools for which it makes sense
feeOracleManager.setFeePercentForInstance(cu100, 0);
feeOracleManager.setFeePercentForInstance(cu1_000, 0);
feeOracleManager.setFeePercentForInstance(cu10_000, 30);
feeOracleManager.setFeePercentForInstance(cu100_000, 30);
feeOracleManager.setFeePercentForInstance(cu1_000_000, 30);
}
/**
* @dev This function adds pools as fee oracles to the main curve fee oracle contract. The oracles are
* chained, this means that multiple pools can be used as one oracle using the library which can be found
* in the contract file. This means:
*
* (...pool contracts as oracles) ===(`price_oracle`)===> CurveFeeOracle ===(`getFee()`)===>
* FeeOracleManager ===(`instanceFeeWithUpdate`)===> RelayerRegistry
*/
function _setCurveFeeChainedOracleForInstance(CurveFeeOracle feeOracle, ITornadoInstance _instance)
internal
{
// Add the oracles which are the USDC/CRVUSD stableswap and tricryptoUSDC pools
ICurvePriceOracle[] memory _oracles = new ICurvePriceOracle[](2);
_oracles[0] = ICurvePriceOracle(crvUSDUSDCStableswap2Pool);
_oracles[1] = ICurvePriceOracle(tricryptoUSDCPool);
// Add the selectors, so which functions to call because tricrypto pools supports getting prices for
// multiple tokens, denominated in the first token, which is usually a stable
bytes4[] memory _selectors = new bytes4[](2);
_selectors[0] = CurveChainedOracles.PRICE_ORACLE_SELECTOR;
_selectors[1] = CurveChainedOracles.PRICE_ORACLE_UINT256_SELECTOR;
// Specify for the second that the ether price must be retrieved
uint8[] memory _coins = new uint8[](2);
_coins[1] = 1; // ETHER
// In order to receive the CRVUSD price, its price in USDC must be read out (the USDC price in crvUSD
// is worthless because we do not have a common denominator then) and then the second price must be
// inverted to receive the accurate ETH/CRVUSD price, meaning ETH per CRVUSD, which will then be
// divided by ETH per TORN, and then (ETH/CRVUSD)/(ETH/TORN) = (ETH/CRVUSD)*(TORN/ETH) = (TORN/CRVUSD)
// which is the price that the oracle should be supplying
bool[] memory _invert = new bool[](2);
_invert[0] = false; // DO NOT INVERT, PRICE IS CRVUSD IN USDC
_invert[1] = true; // INVERT, PRICE IS ETH IN USDC, BUT WE NEED USDC IN ETH
// (USDC/CRVUSD)*(ETH/USDC) = (ETH/CRVUSD)
feeOracle.modifyChainedOracleForInstance(
_instance, _oracles, _selectors, _coins, _invert, "ETH/CRVUSD"
);
}
}

@ -0,0 +1,238 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ Imports
import { AdminUpgradeableProxy } from "../common/AdminUpgradeableProxy.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// Tornado imports
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
// Local V2 imports
import { FeeOracleManager } from "../v2/FeeOracleManager.sol";
import { InstanceRegistry } from "../v2/InstanceRegistry.sol";
import { UniswapV3FeeOracle } from "../v2/UniswapV3FeeOracle.sol";
import { TornadoRouter } from "../v2/TornadoRouter.sol";
/**
* @title InfrastructureUpgradeProposal
* @author AlienTornadosaurusHex
* @notice Proposal which will upgrade only the SURROUNDING infrastrucure in connection with the relayer
* registry such that multiple oracles can be used and so the DAO can add more instances.
*/
contract InfrastructureUpgradeProposal {
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* @dev The address of the current FeeManager proxy, future FeeOracleManager */
address payable public constant feeManagerProxyAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7;
/* @dev The address of the current InstanceRegistry proxy, this will also be upgraded */
address payable public constant instanceRegistryProxyAddress = 0xB20c66C4DE72433F3cE747b58B86830c459CA911;
/* @dev This is the Uniswap V3 Oracle which we will use for all of our traditional instances, but it will
also help the CurveFeeOracle, the former must have been deployed witht the address of this */
address public immutable deployedUniswapV3FeeOracleAddress;
/* @dev The implementation address of the FeeManager upgrade contract */
address public immutable deployedFeeOracleManagerImplementationAddress;
/* @dev The implementation address of the InstanceRegistry upgrade contract */
address public immutable deployedInstanceRegistryImplementationAddress;
/* @dev The address of the new, cleaner, better TornadoRouter */
address public immutable deployedTornadoRouterAddress;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
constructor(
address _deployedUniswapV3FeeOracleAddress,
address _deployedFeeOracleManagerImplementationAddress,
address _deployedInstanceRegistryImplementationAddress,
address _deployedTornadoRouterAddress
) public {
deployedUniswapV3FeeOracleAddress = _deployedUniswapV3FeeOracleAddress;
deployedFeeOracleManagerImplementationAddress = _deployedFeeOracleManagerImplementationAddress;
deployedInstanceRegistryImplementationAddress = _deployedInstanceRegistryImplementationAddress;
deployedTornadoRouterAddress = _deployedTornadoRouterAddress;
}
/**
* @dev This function also executes further internal functions inlined below.
*/
function executeProposal() external {
// Upgrade FeeManager (V1) Proxy to FeeOracleManager (V2)
AdminUpgradeableProxy(feeManagerProxyAddress).upgradeTo(deployedFeeOracleManagerImplementationAddress);
// Now initialize the FeeOracleManager immediately, this initialization will record the old legacy
// data in combination with the new oracle (which returns a fee value compatible with legacy) and in
// accordance with the new data structures being used, which are more logical and segmented than what
// the original implementation was using. The data which is used in the internal functions was read
// out from the on-chain instance registry.
FeeOracleManager(feeManagerProxyAddress).initialize(
deployedUniswapV3FeeOracleAddress,
instanceRegistryProxyAddress,
_getAllInstances(),
_getAllInstanceFeePercents()
);
// Upgrade InstanceRegistry (V1) Proxy to InstanceRegistry (V2)
AdminUpgradeableProxy(instanceRegistryProxyAddress).upgradeTo(
deployedInstanceRegistryImplementationAddress
);
// Initialize this one too, we are abandoning all of the older data here because either the data
// structures don't fit or we can't do a clean addition of all instances, it is better to add the
// instances as fresh data because it's anyways a simple contract, it just stores some basic data on
// instances which isn't mutable for them, instead only determined for each. The new Tornado Router is
// also set.
InstanceRegistry(instanceRegistryProxyAddress).initialize(
_getAllInstances(), TornadoRouter(deployedTornadoRouterAddress)
);
// The Uniswap V3 Fee Oracle also needs global data like the old FeeManager (Uniswap V3 functionality
// has now been split out) did, the legacy data will be used and in this version also the minimum
// observation cardinality for Uniswap V3 pools is set, it'll be a value that most pools can supply
// and in general if it is too strict a proposal adding instances can also just set it to a lower
// value, kind of gamey, but just to "enforce" some constraint or inform the user one exists
UniswapV3FeeOracle v3FeeOracle = UniswapV3FeeOracle(deployedUniswapV3FeeOracleAddress);
v3FeeOracle.setGlobalTornPoolFee(10_000, true); // Legacy value, still makes sense
v3FeeOracle.setGlobalTwapIntervalSeconds(5400); // Legacy value, still makes sense
v3FeeOracle.setGlobalMinObservationCardinality(10); // TODO: Check the value
// Each of the instances are going to require a Uniswap Pool Fee to be set such that we actually know
// what pools to lookup when querying for Oracle data. This data has also been read out from the
// instance registry and it basically has the 3000 (0.3%) and 500 (0.05%) non-stable and stable
// respectively "default" pool fees. This is not to be confused with pool protocol fees, these have
// been recorded in the (first) legacy initialization above
_setAllInstancePoolFeesInOracle(v3FeeOracle);
}
function _setAllInstancePoolFeesInOracle(UniswapV3FeeOracle _v3FeeOracle) internal {
ITornadoInstance[] memory instances = _getAllInstances();
IERC20 weth = IERC20(instances[0].token());
IERC20 dai = IERC20(instances[4].token());
IERC20 cdai = IERC20(instances[8].token());
IERC20 usdc = IERC20(instances[12].token());
IERC20 usdt = IERC20(instances[14].token());
IERC20 wbtc = IERC20(instances[16].token());
/* ETH instances */
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
/* DAI instances */
_v3FeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
/* cDAI instances */
_v3FeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
/* USDC instances */
_v3FeeOracle.setPoolFeeForToken(usdc, uint24(0x1f4));
_v3FeeOracle.setPoolFeeForToken(usdc, uint24(0x1f4));
/* USDT instances */
_v3FeeOracle.setPoolFeeForToken(usdt, uint24(0x1f4));
_v3FeeOracle.setPoolFeeForToken(usdt, uint24(0x1f4));
/* WBTC instances */
_v3FeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
_v3FeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
}
function _getAllInstances() internal pure returns (ITornadoInstance[] memory _addresses) {
_addresses = new ITornadoInstance[](19);
/* ETH instances */
_addresses[0] = ITornadoInstance(0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc);
_addresses[1] = ITornadoInstance(0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936);
_addresses[2] = ITornadoInstance(0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF);
_addresses[3] = ITornadoInstance(0xA160cdAB225685dA1d56aa342Ad8841c3b53f291);
/* DAI instances */
_addresses[4] = ITornadoInstance(0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3);
_addresses[5] = ITornadoInstance(0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144);
_addresses[6] = ITornadoInstance(0x07687e702b410Fa43f4cB4Af7FA097918ffD2730);
_addresses[7] = ITornadoInstance(0x23773E65ed146A459791799d01336DB287f25334);
/* cDAI instances */
_addresses[8] = ITornadoInstance(0x22aaA7720ddd5388A3c0A3333430953C68f1849b);
_addresses[9] = ITornadoInstance(0x03893a7c7463AE47D46bc7f091665f1893656003);
_addresses[10] = ITornadoInstance(0x2717c5e28cf931547B621a5dddb772Ab6A35B701);
_addresses[11] = ITornadoInstance(0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af);
/* USDC instances */
_addresses[12] = ITornadoInstance(0xd96f2B1c14Db8458374d9Aca76E26c3D18364307);
_addresses[13] = ITornadoInstance(0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D);
/* USDT instances */
_addresses[14] = ITornadoInstance(0x169AD27A470D064DEDE56a2D3ff727986b15D52B);
_addresses[15] = ITornadoInstance(0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f);
/* WBTC instances */
_addresses[16] = ITornadoInstance(0x178169B423a011fff22B9e3F3abeA13414dDD0F1);
_addresses[17] = ITornadoInstance(0x610B717796ad172B316836AC95a2ffad065CeaB4);
_addresses[18] = ITornadoInstance(0xbB93e510BbCD0B7beb5A853875f9eC60275CF498);
}
function _getAllInstanceFeePercents() internal pure returns (uint256[] memory _percents) {
_percents = new uint256[](19);
/* ETH instances */
_percents[0] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[1] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
_percents[2] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
_percents[3] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
/* DAI instances */
_percents[4] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[5] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[6] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
_percents[7] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
/* cDAI instances */
_percents[8] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[9] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[11] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[12] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
/* USDC instances */
_percents[13] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[14] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
/* USDT instances */
_percents[15] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
_percents[16] = uint256(0x0000000000000000000000000000000000000000000000000000000000000000);
/* WBTC instances */
_percents[17] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
_percents[18] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
_percents[19] = uint256(0x000000000000000000000000000000000000000000000000000000000000001e);
}
}

@ -104,15 +104,15 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
function initialize(
address _uniswapV3FeeOracle,
address _instanceRegistryAddress,
address[] calldata _instanceAddresses,
ITornadoInstance[] calldata _instances,
uint256[] calldata _feePercents
) external onlyGovernance initializer {
// Get num of existing instances
uint256 numInstances = _instanceAddresses.length;
uint256 numInstances = _instances.length;
for (uint256 i = 0; i < numInstances; i++) {
// For each instance
ITornadoInstance instance = ITornadoInstance(_instanceAddresses[i]);
ITornadoInstance instance = _instances[i];
// Store it's old data and the percent fees which Governance will command
feeDataForInstance[instance] = FeeData({
@ -165,14 +165,19 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
// Now update if we do not respect the interval or we respect it and are in the interval
if (!_respectFeeUpdateInterval || (feeUpdateInterval < -feeData.lastUpdated + now)) {
// This will revert if no contract is set
feeData.feeAmount = instanceFeeOracles[_instance].getFee(
torn,
_instance,
instanceRegistry.getInstanceData(_instance),
feeData.feePercent,
FEE_PERCENT_DIVISOR
);
// There must a be a fee set otherwise it's just 0
if (feeData.feePercent != 0) {
// This will revert if no contract is set
feeData.feeAmount = instanceFeeOracles[_instance].getFee(
torn,
_instance,
instanceRegistry.getInstanceData(_instance),
feeData.feePercent,
FEE_PERCENT_DIVISOR
);
} else {
feeData.feeAmount = 0;
}
// Store
feeDataForInstance[_instance] = FeeData({