use uniswap v2 for twap
Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
parent
60be3e5936
commit
b25f8d569c
@ -1 +1,7 @@
|
||||
# Network & other
|
||||
MAINNET_RPC_URL=
|
||||
ETHERSCAN_KEY=
|
||||
PRIVATE_KEY=
|
||||
|
||||
# The one deployed from this repo
|
||||
TORNADO_ROUTER_ADDRESS=
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -8,3 +8,6 @@ yarn-error.log
|
||||
yarn.lock
|
||||
cache_hardhat
|
||||
artifacts
|
||||
broadcast
|
||||
.yarn
|
||||
yarn.lock
|
19
foundry.toml
19
foundry.toml
@ -2,7 +2,7 @@
|
||||
# General
|
||||
src = 'src'
|
||||
out = 'out'
|
||||
libs = ["node_modules", "lib"]
|
||||
libs = [ "lib"]
|
||||
|
||||
# Compiler
|
||||
evm_version = 'shanghai'
|
||||
@ -18,21 +18,8 @@ rpc_endpoints = { mainnet = "${MAINNET_RPC_URL}" }
|
||||
# Tests
|
||||
verbosity = 2
|
||||
|
||||
# Remappings
|
||||
remappings = [
|
||||
'src/=src/',
|
||||
'base/=src/base/',
|
||||
'common/=src/common/',
|
||||
'ds-test/=lib/ds-test/src/',
|
||||
'solmate/=lib/solmate/src/',
|
||||
'forge-std/=lib/forge-std/src/',
|
||||
'torn-token/=lib/torn-token/',
|
||||
'tornado-governance/=lib/tornado-governance/',
|
||||
'tornado-anonymity-mining/=lib/tornado-anonymity-mining/',
|
||||
'@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/',
|
||||
'@uniswap/v3-periphery/=lib/v3-periphery/',
|
||||
'@uniswap/v3-core/=lib/v3-core/',
|
||||
]
|
||||
[etherscan]
|
||||
mainnet = { key = "${ETHERSCAN_KEY}" }
|
||||
|
||||
[fmt]
|
||||
line_length = 110
|
||||
|
67
gas/all.md
Normal file
67
gas/all.md
Normal file
@ -0,0 +1,67 @@
|
||||
| src/proposals/CRVUSDInstancesProposal.sol:CRVUSDInstancesProposal contract | | | | | |
|
||||
| -------------------------------------------------------------------------- | --------------- | ------- | ------- | ------- | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 743500 | 4033 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| executeProposal | 1217647 | 1217647 | 1217647 | 1217647 | 1 |
|
||||
|
||||
|
||||
| src/proposals/InfrastructureUpgradeProposal.sol:InfrastructureUpgradeProposal contract | | | | | |
|
||||
| -------------------------------------------------------------------------------------- | --------------- | ------- | ------- | ------- | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 1198424 | 6427 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| executeProposal | 2630600 | 2630600 | 2630600 | 2630600 | 1 |
|
||||
|
||||
|
||||
| src/v2/CurveFeeOracle.sol:CurveFeeOracle contract | | | | | |
|
||||
| ------------------------------------------------- | --------------- | ----- | ------ | ------ | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 1168992 | 5938 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| getFee | 1463 | 1463 | 1463 | 1463 | 5 |
|
||||
| modifyChainedOracleForInstance | 83128 | 93498 | 83128 | 132879 | 5 |
|
||||
| setUniswapV3FeeOracle | 1713 | 2713 | 2713 | 3713 | 2 |
|
||||
|
||||
|
||||
| src/v2/FeeOracleManager.sol:FeeOracleManager contract | | | | | |
|
||||
| ----------------------------------------------------- | --------------- | ------ | ------ | ------ | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 1420779 | 7415 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| initialize | 847003 | 847003 | 847003 | 847003 | 1 |
|
||||
| setFeeOracle | 54524 | 54524 | 54524 | 54524 | 5 |
|
||||
| setFeePercentForInstance | 2466 | 2466 | 2466 | 2466 | 5 |
|
||||
|
||||
|
||||
| src/v2/InstanceRegistry.sol:InstanceRegistry contract | | | | | |
|
||||
| ----------------------------------------------------- | --------------- | ------- | ------- | ------- | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 1340669 | 6890 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| addInstance | 84801 | 85801 | 84801 | 89801 | 5 |
|
||||
| getInstanceData | 1469 | 1469 | 1469 | 1469 | 5 |
|
||||
| initialize | 1419165 | 1419165 | 1419165 | 1419165 | 1 |
|
||||
|
||||
|
||||
| src/v2/TornadoRouter.sol:TornadoRouter contract | | | | | |
|
||||
| ----------------------------------------------- | --------------- | ----- | ------ | ----- | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 1079192 | 5584 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| approveTokenForInstance | 27373 | 29130 | 27762 | 33647 | 18 |
|
||||
| initialize | 45887 | 45887 | 45887 | 45887 | 1 |
|
||||
|
||||
|
||||
| src/v2/UniswapV3FeeOracle.sol:UniswapV3FeeOracle contract | | | | | |
|
||||
| --------------------------------------------------------- | --------------- | ----- | ------ | ----- | ------- |
|
||||
| Deployment Cost | Deployment Size | | | | |
|
||||
| 1357082 | 6972 | | | | |
|
||||
| Function Name | min | avg | median | max | # calls |
|
||||
| setGlobalMinObservationCardinality | 1773 | 1773 | 1773 | 1773 | 2 |
|
||||
| setGlobalTornPoolFee | 45872 | 45872 | 45872 | 45872 | 1 |
|
||||
| setGlobalTwapIntervalSeconds | 1666 | 1666 | 1666 | 1666 | 1 |
|
||||
| setPoolFeeForToken | 2688 | 12087 | 5895 | 36795 | 17 |
|
||||
|
||||
|
||||
|
14
remappings.txt
Normal file
14
remappings.txt
Normal file
@ -0,0 +1,14 @@
|
||||
src/=src/
|
||||
base/=src/base/
|
||||
common/=src/common/
|
||||
ds-test/=lib/ds-test/src/
|
||||
solmate/=lib/solmate/src/
|
||||
forge-std/=lib/forge-std/src/
|
||||
torn-token/=lib/torn-token/
|
||||
tornado-governance/=lib/tornado-governance/
|
||||
tornado-anonymity-mining/=lib/tornado-anonymity-mining/
|
||||
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
|
||||
@uniswap/v2-core/=node_modules/@uniswap/v2-core/contracts/
|
||||
@uniswap/v2-periphery/=node_modules/@uniswap/v2-periphery/contracts/
|
||||
@uniswap/v3-periphery/=lib/v3-periphery/
|
||||
@uniswap/v3-core/=lib/v3-core/
|
138
script/Swap.sol.x
Executable file
138
script/Swap.sol.x
Executable file
@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
// STD imports
|
||||
|
||||
import { Script } from "forge-std/Script.sol";
|
||||
|
||||
import { console2 } from "forge-std/console2.sol";
|
||||
|
||||
// Local imports
|
||||
|
||||
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
|
||||
|
||||
import { FeeManager } from "../src/v1/tornado-proxy/FeeManager.sol";
|
||||
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
// SPD
|
||||
|
||||
/// @title Callback for IUniswapV3PoolActions#swap
|
||||
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
|
||||
interface IUniswapV3SwapCallback {
|
||||
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
|
||||
/// @dev In the implementation you must pay the pool tokens owed for the swap.
|
||||
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical
|
||||
/// UniswapV3Factory.
|
||||
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
|
||||
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by
|
||||
/// the pool by
|
||||
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
|
||||
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by
|
||||
/// the pool by
|
||||
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
|
||||
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
|
||||
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
|
||||
}
|
||||
|
||||
/// @title Router token swapping functionality
|
||||
/// @notice Functions for swapping tokens via Uniswap V3
|
||||
interface ISwapRouter is IUniswapV3SwapCallback {
|
||||
struct ExactInputSingleParams {
|
||||
address tokenIn;
|
||||
address tokenOut;
|
||||
uint24 fee;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountIn;
|
||||
uint256 amountOutMinimum;
|
||||
uint160 sqrtPriceLimitX96;
|
||||
}
|
||||
|
||||
/// @notice Swaps `amountIn` of one token for as much as possible of another token
|
||||
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
|
||||
/// @return amountOut The amount of the received token
|
||||
function exactInputSingle(ExactInputSingleParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256 amountOut);
|
||||
|
||||
struct ExactInputParams {
|
||||
bytes path;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountIn;
|
||||
uint256 amountOutMinimum;
|
||||
}
|
||||
|
||||
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
|
||||
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in
|
||||
/// calldata
|
||||
/// @return amountOut The amount of the received token
|
||||
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
|
||||
|
||||
struct ExactOutputSingleParams {
|
||||
address tokenIn;
|
||||
address tokenOut;
|
||||
uint24 fee;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountOut;
|
||||
uint256 amountInMaximum;
|
||||
uint160 sqrtPriceLimitX96;
|
||||
}
|
||||
|
||||
/// @notice Swaps as little as possible of one token for `amountOut` of another token
|
||||
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
|
||||
/// @return amountIn The amount of the input token
|
||||
function exactOutputSingle(ExactOutputSingleParams calldata params)
|
||||
external
|
||||
payable
|
||||
returns (uint256 amountIn);
|
||||
|
||||
struct ExactOutputParams {
|
||||
bytes path;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountOut;
|
||||
uint256 amountInMaximum;
|
||||
}
|
||||
|
||||
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path
|
||||
/// (reversed)
|
||||
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in
|
||||
/// calldata
|
||||
/// @return amountIn The amount of the input token
|
||||
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
|
||||
}
|
||||
|
||||
contract DeployScript is Script {
|
||||
IERC20 public constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
|
||||
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||
|
||||
function run() external {
|
||||
uint256 key = vm.envUint("PRIVATE_KEY");
|
||||
|
||||
ISwapRouter router = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
|
||||
|
||||
uint256 bal = TORN.balanceOf(0x895CB4C75e9be8ABA117bF4E044416C855018ea0);
|
||||
|
||||
vm.startBroadcast(key);
|
||||
|
||||
TORN.approve(address(router), bal);
|
||||
|
||||
ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
|
||||
path: abi.encodePacked(TORN, uint24(10_000), WETH),
|
||||
recipient: 0x895CB4C75e9be8ABA117bF4E044416C855018ea0,
|
||||
deadline: block.timestamp + 6 minutes,
|
||||
amountIn: bal,
|
||||
amountOutMinimum: 0
|
||||
});
|
||||
|
||||
router.exactInput(params);
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
28
script/TornadoRouter.s.sol.x
Executable file
28
script/TornadoRouter.s.sol.x
Executable file
@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
// STD imports
|
||||
|
||||
import { Script } from "forge-std/Script.sol";
|
||||
|
||||
import { console2 } from "forge-std/console2.sol";
|
||||
|
||||
// Local imports
|
||||
|
||||
import { TornadoAddresses } from "../src/common/TornadoAddresses.sol";
|
||||
|
||||
import { TornadoRouter } from "../src/v2/TornadoRouter.sol";
|
||||
|
||||
contract DeployScript is TornadoAddresses, Script {
|
||||
function run() external {
|
||||
uint256 key = vm.envUint("PRIVATE_KEY");
|
||||
|
||||
vm.startBroadcast(key);
|
||||
|
||||
TornadoRouter router = new TornadoRouter(getGovernanceProxyAddress());
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
38
script/Update.sol.x
Executable file
38
script/Update.sol.x
Executable file
@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
// STD imports
|
||||
|
||||
import { Script } from "forge-std/Script.sol";
|
||||
|
||||
import { console2 } from "forge-std/console2.sol";
|
||||
|
||||
// Local imports
|
||||
|
||||
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
|
||||
|
||||
import { FeeManager } from "../src/v1/tornado-proxy/FeeManager.sol";
|
||||
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
contract DeployScript is Script {
|
||||
IERC20 public constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
|
||||
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||
|
||||
function run() external {
|
||||
uint256 key = vm.envUint("PRIVATE_KEY");
|
||||
|
||||
vm.startBroadcast(key);
|
||||
|
||||
FeeManager fees = FeeManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7);
|
||||
|
||||
fees.updateFee(ITornadoInstance(0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF));
|
||||
fees.updateFee(ITornadoInstance(0xA160cdAB225685dA1d56aa342Ad8841c3b53f291));
|
||||
fees.updateFee(ITornadoInstance(0x07687e702b410Fa43f4cB4Af7FA097918ffD2730));
|
||||
fees.updateFee(ITornadoInstance(0x23773E65ed146A459791799d01336DB287f25334));
|
||||
|
||||
vm.stopBroadcast();
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ import { InstanceRegistry } from "../v2/InstanceRegistry.sol";
|
||||
|
||||
import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "../v2/CurveFeeOracle.sol";
|
||||
|
||||
import { UniswapV3FeeOracle } from "../v2/UniswapV3FeeOracle.sol";
|
||||
import { UniswapFeeOracle } from "../v2/UniswapFeeOracle.sol";
|
||||
|
||||
import { TornadoRouter } from "../v2/TornadoRouter.sol";
|
||||
|
||||
@ -47,7 +47,7 @@ contract CRVUSDInstancesProposal {
|
||||
|
||||
/* @dev This is 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 */
|
||||
UniswapV3FeeOracle public immutable v3FeeOracle;
|
||||
UniswapFeeOracle public immutable uniswapFeeOracle;
|
||||
|
||||
/* @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. */
|
||||
@ -55,9 +55,9 @@ contract CRVUSDInstancesProposal {
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
constructor(address _deployedCurveFeeOracleAddress, address _deployedUniswapV3FeeOracleAddress) public {
|
||||
constructor(address _deployedCurveFeeOracleAddress, address _deployedUniswapFeeOracleAddress) public {
|
||||
curveFeeOracle = CurveFeeOracle(_deployedCurveFeeOracleAddress);
|
||||
v3FeeOracle = UniswapV3FeeOracle(_deployedUniswapV3FeeOracleAddress);
|
||||
uniswapFeeOracle = UniswapFeeOracle(_deployedUniswapFeeOracleAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +74,7 @@ contract CRVUSDInstancesProposal {
|
||||
|
||||
// Ok, first add the Uniswap V3 Oracle to the contract
|
||||
|
||||
curveFeeOracle.setUniswapV3FeeOracle(v3FeeOracle);
|
||||
curveFeeOracle.setUniswapFeeOracle(uniswapFeeOracle);
|
||||
|
||||
// Then, add necessary oracles for the CRVUSD price, to the CurveFeeOracle
|
||||
|
||||
|
@ -18,7 +18,7 @@ import { FeeOracleManager } from "../v2/FeeOracleManager.sol";
|
||||
|
||||
import { InstanceRegistry } from "../v2/InstanceRegistry.sol";
|
||||
|
||||
import { UniswapV3FeeOracle, UniswapV3OracleHelper } from "../v2/UniswapV3FeeOracle.sol";
|
||||
import { UniswapFeeOracle, UniswapV3OracleHelper } from "../v2/UniswapFeeOracle.sol";
|
||||
|
||||
import { TornadoRouter } from "../v2/TornadoRouter.sol";
|
||||
|
||||
@ -42,7 +42,7 @@ contract InfrastructureUpgradeProposal {
|
||||
|
||||
/* @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;
|
||||
address public immutable deployedUniswapFeeOracleAddress;
|
||||
|
||||
/* @dev The implementation address of the FeeManager upgrade contract */
|
||||
address public immutable deployedFeeOracleManagerImplementationAddress;
|
||||
@ -56,12 +56,12 @@ contract InfrastructureUpgradeProposal {
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
constructor(
|
||||
address _deployedUniswapV3FeeOracleAddress,
|
||||
address _deployedUniswapFeeOracleAddress,
|
||||
address _deployedFeeOracleManagerImplementationAddress,
|
||||
address _deployedInstanceRegistryImplementationAddress,
|
||||
address _deployedTornadoRouterAddress
|
||||
) public {
|
||||
deployedUniswapV3FeeOracleAddress = _deployedUniswapV3FeeOracleAddress;
|
||||
deployedUniswapFeeOracleAddress = _deployedUniswapFeeOracleAddress;
|
||||
deployedFeeOracleManagerImplementationAddress = _deployedFeeOracleManagerImplementationAddress;
|
||||
deployedInstanceRegistryImplementationAddress = _deployedInstanceRegistryImplementationAddress;
|
||||
deployedTornadoRouterAddress = _deployedTornadoRouterAddress;
|
||||
@ -91,7 +91,7 @@ contract InfrastructureUpgradeProposal {
|
||||
// out from the on-chain instance registry.
|
||||
|
||||
FeeOracleManager(feeManagerProxyAddress).initialize(
|
||||
deployedUniswapV3FeeOracleAddress,
|
||||
deployedUniswapFeeOracleAddress,
|
||||
instanceRegistryProxyAddress,
|
||||
_getAllInstances(),
|
||||
_getAllInstanceFeePercents()
|
||||
@ -117,13 +117,11 @@ contract InfrastructureUpgradeProposal {
|
||||
// legacy cDAI pools which are in a kind of stupid liquidity condition, in any case there is a small
|
||||
// amount of funds in there so let's add them too
|
||||
|
||||
UniswapV3FeeOracle v3FeeOracle = UniswapV3FeeOracle(deployedUniswapV3FeeOracleAddress);
|
||||
UniswapFeeOracle uniswapFeeOracle = UniswapFeeOracle(deployedUniswapFeeOracleAddress);
|
||||
|
||||
v3FeeOracle.setGlobalTornPoolFee(10_000, true); // Legacy value, still makes sense
|
||||
uniswapFeeOracle.setTwapIntervalSeconds(5400); // Legacy value, still makes sense
|
||||
|
||||
v3FeeOracle.setGlobalTwapIntervalSeconds(5400); // Legacy value, still makes sense
|
||||
|
||||
v3FeeOracle.setGlobalMinObservationCardinality(1); // Set it to minimum so cDAI passes
|
||||
uniswapFeeOracle.setMinObservationCardinality(1); // Set it to minimum so cDAI passes
|
||||
|
||||
// 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
|
||||
@ -131,12 +129,12 @@ contract InfrastructureUpgradeProposal {
|
||||
// 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);
|
||||
_setAllInstancePoolFeesInOracle(uniswapFeeOracle);
|
||||
|
||||
v3FeeOracle.setGlobalMinObservationCardinality(10); // Now set the cardinality to a reasonable value
|
||||
uniswapFeeOracle.setMinObservationCardinality(10); // Now set the cardinality to a reasonable value
|
||||
}
|
||||
|
||||
function _setAllInstancePoolFeesInOracle(UniswapV3FeeOracle _v3FeeOracle) internal {
|
||||
function _setAllInstancePoolFeesInOracle(UniswapFeeOracle _uniswapFeeOracle) internal {
|
||||
ITornadoInstance[] memory instances = _getAllInstances();
|
||||
|
||||
IERC20 weth = IERC20(UniswapV3OracleHelper.WETH);
|
||||
@ -146,31 +144,31 @@ contract InfrastructureUpgradeProposal {
|
||||
IERC20 wbtc = IERC20(instances[14].token());
|
||||
|
||||
/* ETH instances */
|
||||
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_v3FeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(weth, uint24(0x000));
|
||||
_uniswapFeeOracle.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));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(dai, uint24(0xbb8));
|
||||
_uniswapFeeOracle.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));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(cdai, uint24(0xbb8));
|
||||
|
||||
/* USDT instances */
|
||||
_v3FeeOracle.setPoolFeeForToken(usdt, uint24(0x1f4));
|
||||
_v3FeeOracle.setPoolFeeForToken(usdt, uint24(0x1f4));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(usdt, uint24(0x1f4));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(usdt, uint24(0x1f4));
|
||||
|
||||
/* WBTC instances */
|
||||
_v3FeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
|
||||
_v3FeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
|
||||
_v3FeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
|
||||
_uniswapFeeOracle.setPoolFeeForToken(wbtc, uint24(0xbb8));
|
||||
}
|
||||
|
||||
function _getAllInstances() internal pure returns (ITornadoInstance[] memory _addresses) {
|
||||
|
@ -18,7 +18,7 @@ import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
||||
|
||||
import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
|
||||
|
||||
import { UniswapV3FeeOracle } from "./UniswapV3FeeOracle.sol";
|
||||
import { UniswapFeeOracle } from "./UniswapFeeOracle.sol";
|
||||
|
||||
import { InstanceData } from "./InstanceRegistry.sol";
|
||||
|
||||
@ -170,28 +170,38 @@ contract CurveFeeOracle is IFeeOracle {
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* @dev The Governance Proxy address */
|
||||
/**
|
||||
* @notice The Governance Proxy address
|
||||
*/
|
||||
address public immutable governanceProxyAddress;
|
||||
|
||||
/* @dev For each instance, a set of data which translates to a set of chained price oracle calls, we call
|
||||
this data "chainedPriceOracles" because it encodes all data necessary to execute the chain and calc the
|
||||
price */
|
||||
/**
|
||||
* @notice For each instance, a set of data which translates to a set of chained price oracle calls, we
|
||||
* call this data "chainedPriceOracles" because it encodes all data necessary to execute the chain and
|
||||
* calc the price
|
||||
*/
|
||||
mapping(ITornadoInstance => bytes) public chainedPriceOracles;
|
||||
|
||||
/* @dev When setting, store the names as a historical record, key is keccak256(bytes) */
|
||||
/**
|
||||
* @notice When setting, store the names as a historical record, key is keccak256(bytes)
|
||||
*/
|
||||
mapping(bytes32 => string) public chainedPriceOracleNames;
|
||||
|
||||
/* @dev Our Uniswap V3 Fee Oracle, we will need it some time for TORN/ETH */
|
||||
UniswapV3FeeOracle public v3FeeOracle;
|
||||
/**
|
||||
* @notice Our Uniswap V3 Fee Oracle, we will need it some time for TORN/ETH
|
||||
*/
|
||||
UniswapFeeOracle public uniswapFeeOracle;
|
||||
|
||||
/* @dev We will not need the Uniswap V3 Oracle forever though, TORN/ETH pools are possible on Curve too */
|
||||
/**
|
||||
* @notice We will not need the Uniswap V3 Oracle forever though, TORN/ETH pools are possible on Curve too
|
||||
*/
|
||||
bool public tornOracleIsUniswapV3;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
event TornOracleIsCurve();
|
||||
event TornOracleIsUniswapV3();
|
||||
event UniswapV3FeeOracleUpdated(address _newUniswapV3FeeOracleAddress);
|
||||
event UniswapFeeOracleUpdated(address _newUniswapFeeOracleAddress);
|
||||
event ChainedOracleUpdated(address indexed instance, bytes32 newOracleHash, string newName);
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
@ -202,18 +212,20 @@ contract CurveFeeOracle is IFeeOracle {
|
||||
}
|
||||
|
||||
modifier onlyGovernance() {
|
||||
require(msg.sender == governanceProxyAddress, "UniswapV3FeeOracle: onlyGovernance");
|
||||
require(msg.sender == governanceProxyAddress, "UniswapFeeOracle: onlyGovernance");
|
||||
_;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function update() public virtual override { }
|
||||
|
||||
function getFee(
|
||||
IERC20 _torn,
|
||||
IERC20,
|
||||
ITornadoInstance _instance,
|
||||
InstanceData memory,
|
||||
uint64 _feePercent,
|
||||
uint64 _feePercentDivisor
|
||||
uint32 _feePercent,
|
||||
uint32 _feePercentDivisor
|
||||
) public view virtual override returns (uint160) {
|
||||
// If fee is 0 return
|
||||
if (_feePercent == 0) return 0;
|
||||
@ -226,7 +238,7 @@ contract CurveFeeOracle is IFeeOracle {
|
||||
// in TORN
|
||||
if (tornOracleIsUniswapV3) {
|
||||
price = price * UniswapV3OracleHelper.RATIO_DIVIDER
|
||||
/ v3FeeOracle.getPriceRatioOfTokens(_torn, IERC20(UniswapV3OracleHelper.WETH));
|
||||
/ uniswapFeeOracle.getTokenPerTORN(IERC20(UniswapV3OracleHelper.WETH));
|
||||
}
|
||||
|
||||
// The price is now either prepared by the chained oracles or by Uniswap V3, in any case we do the
|
||||
@ -284,9 +296,9 @@ contract CurveFeeOracle is IFeeOracle {
|
||||
emit ChainedOracleUpdated(address(_instance), oracleHash, _name);
|
||||
}
|
||||
|
||||
function setUniswapV3FeeOracle(UniswapV3FeeOracle _uniswapV3FeeOracle) public virtual onlyGovernance {
|
||||
v3FeeOracle = _uniswapV3FeeOracle;
|
||||
emit UniswapV3FeeOracleUpdated(address(_uniswapV3FeeOracle));
|
||||
function setUniswapFeeOracle(UniswapFeeOracle _uniswapFeeOracle) public virtual onlyGovernance {
|
||||
uniswapFeeOracle = _uniswapFeeOracle;
|
||||
emit UniswapFeeOracleUpdated(address(_uniswapFeeOracle));
|
||||
}
|
||||
|
||||
function setTornOracleIsUniswapV3(bool _is) public virtual onlyGovernance {
|
||||
|
@ -25,19 +25,29 @@ import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
||||
* FeeOracleManager (formerly FeeManager).
|
||||
*/
|
||||
contract FeeManagerLegacyStorage {
|
||||
/* @dev From first contract */
|
||||
/**
|
||||
* @dev From first contract
|
||||
*/
|
||||
uint24 private _deprecatedUniswapTornPoolSwappingFee;
|
||||
|
||||
/* @dev From first contract */
|
||||
/**
|
||||
* @dev From first contract
|
||||
*/
|
||||
uint32 private _deprecatedUniswapTimePeriod;
|
||||
|
||||
/* @dev From first contract, the only value we keep alive */
|
||||
uint24 public feeUpdateInterval;
|
||||
/**
|
||||
* @dev From first contract, the only value we keep alive
|
||||
*/
|
||||
uint24 internal _oldFeeUpdateInterval;
|
||||
|
||||
/* @dev From first contract, only used for initialization to preserve old values */
|
||||
/**
|
||||
* @dev From first contract, only used for initialization to preserve old values
|
||||
*/
|
||||
mapping(ITornadoInstance => uint160) internal _oldFeesForInstance;
|
||||
|
||||
/* @dev From first contract, only used for initialization to preserve old values */
|
||||
/**
|
||||
* @dev From first contract, only used for initialization to preserve old values
|
||||
*/
|
||||
mapping(ITornadoInstance => uint256) internal _oldFeesForInstanceUpdateTime;
|
||||
}
|
||||
|
||||
@ -45,9 +55,10 @@ contract FeeManagerLegacyStorage {
|
||||
* @notice Fee data which is valid and also influences fee updating across all oracles.
|
||||
*/
|
||||
struct FeeData {
|
||||
uint160 feeAmount;
|
||||
uint64 feePercent;
|
||||
uint32 lastUpdated;
|
||||
uint160 amount;
|
||||
uint32 percent;
|
||||
uint32 updateInterval;
|
||||
uint32 lastUpdateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,30 +70,42 @@ struct FeeData {
|
||||
contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* @dev Divide protocol fee by this to get the percent value */
|
||||
uint64 public constant FEE_PERCENT_DIVISOR = 1 ether;
|
||||
/**
|
||||
* @notice Divide protocol fee by this to get the percent value
|
||||
*/
|
||||
uint32 public constant FEE_PERCENT_DIVISOR = 10_000;
|
||||
|
||||
/* @dev The Governance Proxy address */
|
||||
/**
|
||||
* @notice The Governance Proxy address
|
||||
*/
|
||||
address public immutable governanceProxyAddress;
|
||||
|
||||
/* @dev The TORN token */
|
||||
/**
|
||||
* @notice The TORN token
|
||||
*/
|
||||
IERC20 public immutable torn;
|
||||
|
||||
/* @dev The InstanceRegistry contract */
|
||||
/**
|
||||
* @notice The InstanceRegistry contract
|
||||
*/
|
||||
InstanceRegistry public instanceRegistry;
|
||||
|
||||
/* @dev Each instance has a dedicated fee oracle, these only compute the values */
|
||||
/**
|
||||
* @notice 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;
|
||||
/**
|
||||
* @notice The data for each instance will be stored in this contract
|
||||
*/
|
||||
mapping(ITornadoInstance => FeeData) public feesByInstance;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
event FeeUpdateIntervalUpdated(uint24 newLimit);
|
||||
event FeeUpdated(address indexed instance, uint256 newFee);
|
||||
event OracleUpdated(address indexed instance, address oracle);
|
||||
event InstanceFeePercentUpdated(address indexed instance, uint64 newFeePercent);
|
||||
event InstanceFeePercentUpdated(address indexed instance, uint32 newFeePercent);
|
||||
event InstanceRegistryUpdated(address newAddress);
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
@ -102,10 +125,10 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
* making sure to not reference old data anywhere.
|
||||
*/
|
||||
function initialize(
|
||||
address _uniswapV3FeeOracle,
|
||||
address _uniswapFeeOracle,
|
||||
address _instanceRegistryAddress,
|
||||
ITornadoInstance[] memory _instances,
|
||||
uint256[] memory _feePercents
|
||||
uint256[] memory _percents
|
||||
) external onlyGovernance initializer {
|
||||
// Get num of existing instances
|
||||
uint256 numInstances = _instances.length;
|
||||
@ -115,14 +138,15 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
ITornadoInstance instance = _instances[i];
|
||||
|
||||
// Store it's old data and the percent fees which Governance will command
|
||||
feeDataForInstance[instance] = FeeData({
|
||||
feeAmount: _oldFeesForInstance[instance],
|
||||
feePercent: uint64(_feePercents[i]),
|
||||
lastUpdated: uint32(_oldFeesForInstanceUpdateTime[instance])
|
||||
feesByInstance[instance] = FeeData({
|
||||
amount: _oldFeesForInstance[instance],
|
||||
percent: uint32(_percents[i]),
|
||||
updateInterval: _oldFeeUpdateInterval,
|
||||
lastUpdateTime: uint32(_oldFeesForInstanceUpdateTime[instance])
|
||||
});
|
||||
|
||||
// All old pools use the uniswap fee oracle
|
||||
instanceFeeOracles[instance] = IFeeOracle(_uniswapV3FeeOracle);
|
||||
instanceFeeOracles[instance] = IFeeOracle(_uniswapFeeOracle);
|
||||
}
|
||||
|
||||
// Finally also store the instance registry
|
||||
@ -160,42 +184,45 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
virtual
|
||||
returns (uint160)
|
||||
{
|
||||
// First get fee data
|
||||
FeeData memory feeData = feeDataForInstance[_instance];
|
||||
// Get fee data & oracle
|
||||
FeeData memory fee = feesByInstance[_instance];
|
||||
IFeeOracle oracle = instanceFeeOracles[_instance];
|
||||
|
||||
// Check whether the instance is registered
|
||||
require(address(oracle) != address(0), "FeeOracleManager: instance has no oracle");
|
||||
|
||||
// Now update if we do not respect the interval or we respect it and are in the interval
|
||||
if (!_respectFeeUpdateInterval || (feeUpdateInterval < -feeData.lastUpdated + now)) {
|
||||
IFeeOracle oracle = instanceFeeOracles[_instance];
|
||||
|
||||
// Check whether the instance is registered
|
||||
require(address(oracle) != address(0), "FeeOracleManager: instance has no oracle");
|
||||
if (!_respectFeeUpdateInterval || (fee.updateInterval < now - fee.lastUpdateTime)) {
|
||||
// Allow oracle to gate for fee manager and implement own logic
|
||||
oracle.update();
|
||||
|
||||
// There must a be a fee set otherwise it's just 0
|
||||
if (feeData.feePercent != 0) {
|
||||
feeData.feeAmount = oracle.getFee(
|
||||
if (fee.percent != 0) {
|
||||
fee.amount = oracle.getFee(
|
||||
torn,
|
||||
_instance,
|
||||
instanceRegistry.getInstanceData(_instance),
|
||||
feeData.feePercent,
|
||||
fee.percent,
|
||||
FEE_PERCENT_DIVISOR
|
||||
);
|
||||
} else {
|
||||
feeData.feeAmount = 0;
|
||||
fee.amount = 0;
|
||||
}
|
||||
|
||||
// Store
|
||||
feeDataForInstance[_instance] = FeeData({
|
||||
feeAmount: feeData.feeAmount,
|
||||
feePercent: feeData.feePercent,
|
||||
lastUpdated: uint32(now)
|
||||
feesByInstance[_instance] = FeeData({
|
||||
amount: fee.amount,
|
||||
percent: fee.percent,
|
||||
updateInterval: fee.updateInterval,
|
||||
lastUpdateTime: uint32(now)
|
||||
});
|
||||
|
||||
// Log
|
||||
emit FeeUpdated(address(_instance), feeData.feeAmount);
|
||||
emit FeeUpdated(address(_instance), fee.amount);
|
||||
}
|
||||
|
||||
// In any case return a value
|
||||
return feeData.feeAmount;
|
||||
return fee.amount;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
@ -212,45 +239,50 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
|
||||
// Nominally fee percent should be set first for an instance, but we cannot be sure
|
||||
// whether fee percent 0 is intentional, so we don't check
|
||||
FeeData memory feeData = feeDataForInstance[instance];
|
||||
|
||||
// Fee which may be recalculated
|
||||
uint160 fee;
|
||||
FeeData memory fee = feesByInstance[instance];
|
||||
|
||||
// An address(0) oracle means we're removing an oracle for an instance
|
||||
if (_oracleAddress != address(0)) {
|
||||
// Reverts if oracle does not conform to interface
|
||||
fee = oracle.getFee(
|
||||
torn,
|
||||
instance,
|
||||
instanceRegistry.getInstanceData(instance),
|
||||
feeData.feePercent,
|
||||
FEE_PERCENT_DIVISOR
|
||||
// Reverts if oracle doesn't implement
|
||||
oracle.update();
|
||||
|
||||
// Reverts if oracle doesn't implement
|
||||
fee.amount = oracle.getFee(
|
||||
torn, instance, instanceRegistry.getInstanceData(instance), fee.percent, FEE_PERCENT_DIVISOR
|
||||
);
|
||||
|
||||
// Note down updated fee
|
||||
feeDataForInstance[instance] =
|
||||
FeeData({ feeAmount: fee, feePercent: feeData.feePercent, lastUpdated: uint32(now) });
|
||||
feesByInstance[instance] = FeeData({
|
||||
amount: fee.amount,
|
||||
percent: fee.percent,
|
||||
updateInterval: fee.updateInterval,
|
||||
lastUpdateTime: uint32(now)
|
||||
});
|
||||
|
||||
// Log fee update
|
||||
emit FeeUpdated(_instanceAddress, fee.amount);
|
||||
}
|
||||
|
||||
// Ok, set the oracle
|
||||
instanceFeeOracles[instance] = oracle;
|
||||
|
||||
// Logs
|
||||
// Log oracle update
|
||||
emit OracleUpdated(_instanceAddress, _oracleAddress);
|
||||
emit FeeUpdated(_instanceAddress, fee);
|
||||
}
|
||||
|
||||
function setFeePercentForInstance(ITornadoInstance _instance, uint64 _newFeePercent)
|
||||
function setFeePercentForInstance(ITornadoInstance _instance, uint32 _newFeePercent)
|
||||
external
|
||||
onlyGovernance
|
||||
{
|
||||
feeDataForInstance[_instance].feePercent = _newFeePercent;
|
||||
feesByInstance[_instance].percent = _newFeePercent;
|
||||
emit InstanceFeePercentUpdated(address(_instance), _newFeePercent);
|
||||
}
|
||||
|
||||
function setFeeUpdateInterval(uint24 newLimit) external onlyGovernance {
|
||||
feeUpdateInterval = newLimit;
|
||||
function setFeeUpdateIntervalForInstance(ITornadoInstance _instance, uint24 newLimit)
|
||||
external
|
||||
onlyGovernance
|
||||
{
|
||||
feesByInstance[_instance].updateInterval = newLimit;
|
||||
emit FeeUpdateIntervalUpdated(newLimit);
|
||||
}
|
||||
|
||||
@ -265,21 +297,30 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
torn,
|
||||
instance,
|
||||
instanceRegistry.getInstanceData(instance),
|
||||
feeDataForInstance[instance].feePercent,
|
||||
feesByInstance[instance].percent,
|
||||
FEE_PERCENT_DIVISOR
|
||||
);
|
||||
}
|
||||
|
||||
function getLastFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
|
||||
return feeDataForInstance[instance].feeAmount;
|
||||
return feesByInstance[instance].amount;
|
||||
}
|
||||
|
||||
function getLastUpdatedTimeForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
|
||||
return feeDataForInstance[instance].lastUpdated;
|
||||
return feesByInstance[instance].lastUpdateTime;
|
||||
}
|
||||
|
||||
function getFeePercentForInstance(ITornadoInstance instance) public view virtual returns (uint64) {
|
||||
return feeDataForInstance[instance].feePercent;
|
||||
function getFeePercentForInstance(ITornadoInstance instance) public view virtual returns (uint32) {
|
||||
return feesByInstance[instance].percent;
|
||||
}
|
||||
|
||||
function getFeeUpdateIntervalForInstance(ITornadoInstance instance)
|
||||
public
|
||||
view
|
||||
virtual
|
||||
returns (uint32)
|
||||
{
|
||||
return feesByInstance[instance].updateInterval;
|
||||
}
|
||||
|
||||
function getAllFeeDeviations() public view virtual returns (int256[] memory) {
|
||||
@ -299,23 +340,15 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
||||
for (uint256 i = 0; i < numInstances; i++) {
|
||||
ITornadoInstance instance = _instances[i];
|
||||
|
||||
FeeData memory feeData = feeDataForInstance[instance];
|
||||
FeeData memory fee = feesByInstance[instance];
|
||||
|
||||
uint256 marketFee = instanceFeeOracles[instance].getFee(
|
||||
torn,
|
||||
instance,
|
||||
instanceRegistry.getInstanceData(instance),
|
||||
feeData.feePercent,
|
||||
FEE_PERCENT_DIVISOR
|
||||
torn, instance, instanceRegistry.getInstanceData(instance), fee.percent, FEE_PERCENT_DIVISOR
|
||||
);
|
||||
|
||||
int256 deviation;
|
||||
|
||||
if (marketFee != 0) {
|
||||
deviation = int256((feeData.feeAmount * 1000) / marketFee) - 1000;
|
||||
deviations[i] = int256((fee.amount * 1000) / marketFee) - 1000;
|
||||
}
|
||||
|
||||
deviations[i] = deviation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,19 +44,29 @@ contract InstanceRegistryLegacyStorage {
|
||||
uint32 deprecatedProtocolFeePercentage;
|
||||
}
|
||||
|
||||
/* @dev From Initializable.sol of first contract */
|
||||
/**
|
||||
* @dev From Initializable.sol of first contract
|
||||
*/
|
||||
bool private _deprecatedInitialized;
|
||||
|
||||
/* @dev From Initializable.sol of first contract */
|
||||
/**
|
||||
* @dev From Initializable.sol of first contract
|
||||
*/
|
||||
bool private _deprecatedInitializing;
|
||||
|
||||
/* @dev From first contract */
|
||||
/**
|
||||
* @dev From first contract
|
||||
*/
|
||||
address private _deprecatedRouterAddress;
|
||||
|
||||
/* @dev From first contract */
|
||||
/**
|
||||
* @dev From first contract
|
||||
*/
|
||||
mapping(address => LegacyInstanceStructPlaceholder) private _deprecatedInstances;
|
||||
|
||||
/* @dev From first contract */
|
||||
/**
|
||||
* @dev From first contract
|
||||
*/
|
||||
ITornadoInstance[] private _deprecatedInstanceIds;
|
||||
}
|
||||
|
||||
@ -82,16 +92,24 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* @dev The address of the Governance proxy */
|
||||
/**
|
||||
* @notice The address of the Governance proxy
|
||||
*/
|
||||
address public immutable governanceProxyAddress;
|
||||
|
||||
/* @dev Essential data regarding instances, see struct above */
|
||||
/**
|
||||
* @notice Essential data regarding instances, see struct above
|
||||
*/
|
||||
mapping(ITornadoInstance => InstanceData) public instanceData;
|
||||
|
||||
/* @dev All instances enumerable */
|
||||
/**
|
||||
* @notice All instances enumerable
|
||||
*/
|
||||
ITornadoInstance[] public instances;
|
||||
|
||||
/* @dev The router which processes txs which we must command to approve tokens */
|
||||
/**
|
||||
* @notice The router which processes txs which we must command to approve tokens
|
||||
*/
|
||||
TornadoRouter public router;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
@ -255,10 +273,10 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
||||
virtual
|
||||
returns (ITornadoInstance[] memory allInstances)
|
||||
{
|
||||
allInstances = new ITornadoInstance[](-_inclusiveStartIndex + 1 + _inclusiveEndIndex);
|
||||
allInstances = new ITornadoInstance[](1 + _inclusiveEndIndex - _inclusiveStartIndex);
|
||||
|
||||
for (uint256 i = _inclusiveStartIndex; i < _inclusiveEndIndex + 1; i++) {
|
||||
allInstances[i] = instances[i];
|
||||
allInstances[i - _inclusiveStartIndex] = instances[i];
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,10 +290,10 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
||||
virtual
|
||||
returns (InstanceData[] memory data)
|
||||
{
|
||||
data = new InstanceData[](-_inclusiveStartIndex + 1 + _inclusiveEndIndex);
|
||||
data = new InstanceData[](1 + _inclusiveEndIndex - _inclusiveStartIndex);
|
||||
|
||||
for (uint256 i = _inclusiveStartIndex; i < _inclusiveEndIndex + 1; i++) {
|
||||
data[i] = instanceData[instances[i]];
|
||||
data[i - _inclusiveStartIndex] = instanceData[instances[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,13 +30,19 @@ contract TornadoRouter is Initializable {
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* @dev The address of the Governance proxy */
|
||||
/**
|
||||
* @notice The address of the Governance proxy
|
||||
*/
|
||||
address public immutable governanceProxyAddress;
|
||||
|
||||
/* @dev The instance registry */
|
||||
/**
|
||||
* @notice The instance registry
|
||||
*/
|
||||
InstanceRegistry public instanceRegistry;
|
||||
|
||||
/* @dev The relayer registry */
|
||||
/**
|
||||
* @notice The relayer registry
|
||||
*/
|
||||
RelayerRegistry public relayerRegistry;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
@ -72,6 +78,12 @@ contract TornadoRouter is Initializable {
|
||||
relayerRegistry = RelayerRegistry(_relayerRegistryAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Function to deposit into a Tornado instance.
|
||||
* @param _tornado The instance to deposit into (address).
|
||||
* @param _commitment The commitment which will be added to the Merkle Tree.
|
||||
* @param _encryptedNote An encrypted note tied to the commitment which may be logged.
|
||||
*/
|
||||
function deposit(ITornadoInstance _tornado, bytes32 _commitment, bytes calldata _encryptedNote)
|
||||
public
|
||||
payable
|
||||
@ -79,6 +91,7 @@ contract TornadoRouter is Initializable {
|
||||
{
|
||||
(IERC20 token,, bool isERC20, bool isEnabled) = instanceRegistry.instanceData(_tornado);
|
||||
|
||||
// Better than having it revert at safeTransferFrom
|
||||
require(isEnabled, "TornadoRouter: instance not enabled");
|
||||
|
||||
if (isERC20) {
|
||||
@ -90,6 +103,28 @@ contract TornadoRouter is Initializable {
|
||||
emit EncryptedNote(msg.sender, _encryptedNote);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Withdraw from a Tornado Instance. NOTE: Until infrastructure, which will include a
|
||||
* smart-contract relayer wallet, isn't deployed, this function will be the only gateway for withdrawing
|
||||
* from the _new_ instances. But fear not! Each instance contains a permissionless function by the name of
|
||||
* `checkInfrastructureIsDead()`, which can be used in the case that the infra is deactivated or
|
||||
* malfunctions (this contract can't malfunction to block withdrawals). It makes the instance work just
|
||||
* like a regular one again! The intention is to call the formerly mentioned function when the new
|
||||
* infrastructure is deployed, and until then, use the simple fix in the new instances to route
|
||||
* withdrawals
|
||||
* through the registry. ALSO NOTE, that manual depositors and withdrawers will still be able to
|
||||
* permissionlessly use the instances, they anyways do not rely on the `relayer` address field, and as
|
||||
* such they will be able to claim their deposits no matter whether the check is effective or not!
|
||||
* @param _tornado The Tornado instance to withdraw from.
|
||||
* @param _proof Bytes proof data.
|
||||
* @param _root A current or historical bytes32 root of the Merkle Tree within the proofs context.
|
||||
* @param _nullifierHash The bytes32 nullifierHash for the deposit.
|
||||
* @param _recipient The address of recipient for withdrawn funds.
|
||||
* @param _relayer The address of the relayer which will be making the withdrawal.
|
||||
* @param _fee The fee in bips to pay the relayer.
|
||||
* @param _refund If swapping into ETH on the other side, use this to specify how much should be paid for
|
||||
* it.
|
||||
*/
|
||||
function withdraw(
|
||||
ITornadoInstance _tornado,
|
||||
bytes calldata _proof,
|
||||
@ -100,11 +135,19 @@ contract TornadoRouter is Initializable {
|
||||
uint256 _fee,
|
||||
uint256 _refund
|
||||
) public payable virtual {
|
||||
// If an instance is turned off, we still want withdrawals to pass
|
||||
// so instead of requiring `isEnabled`, we will only use it to
|
||||
// decide whether relayers should burn or not. This is beneficial
|
||||
// for every party involved!
|
||||
(,,, bool isEnabled) = instanceRegistry.instanceData(_tornado);
|
||||
|
||||
require(isEnabled, "TornadoRouter: instance not enabled");
|
||||
|
||||
relayerRegistry.burn(msg.sender, _relayer, _tornado);
|
||||
// Remove the require and instead just skip if there is no
|
||||
// relayer being used or the instance is not enabled. Note that,
|
||||
// even if the registry gets hijacked, you can just do
|
||||
// manual withdrawals (using the SDK, for example).
|
||||
if (_relayer != address(0) && isEnabled) {
|
||||
relayerRegistry.burn(msg.sender, _relayer, _tornado);
|
||||
}
|
||||
|
||||
_tornado.withdraw{ value: msg.value }(
|
||||
_proof, _root, _nullifierHash, _recipient, _relayer, _fee, _refund
|
||||
|
394
src/v2/UniswapFeeOracle.sol
Normal file
394
src/v2/UniswapFeeOracle.sol
Normal file
@ -0,0 +1,394 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
// OZ imports
|
||||
|
||||
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
||||
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";
|
||||
|
||||
// Local imports
|
||||
|
||||
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
||||
|
||||
import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
|
||||
|
||||
import { InstanceData } 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 {
|
||||
FixedPoint.uq112x112 averagePrice;
|
||||
uint32 updatedTimestamp;
|
||||
}
|
||||
|
||||
contract UniswapFeeOracle is IFeeOracle {
|
||||
using SafeMath for uint256;
|
||||
using FixedPoint for *;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/**
|
||||
* @notice Address of the Uniswap V2 TORN pool
|
||||
*/
|
||||
address public constant v2TORNPoolAddress = 0x0C722a487876989Af8a05FFfB6e32e45cc23FB3A;
|
||||
|
||||
/**
|
||||
* @notice The Governance Proxy address
|
||||
*/
|
||||
address public immutable governanceProxyAddress;
|
||||
|
||||
/**
|
||||
* @notice The Fee Oracle Manager address
|
||||
*/
|
||||
address public feeOracleManagerAddress;
|
||||
|
||||
/**
|
||||
* @notice The TWAP interval in seconds (this is common)
|
||||
*/
|
||||
uint32 public twapIntervalSeconds;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ V3 ORACLE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/**
|
||||
* @notice Uniswap V3 pool fees for each token registered
|
||||
*/
|
||||
mapping(IERC20 => uint24) public poolFeesByToken;
|
||||
|
||||
/**
|
||||
* @notice The minimum observation cardinality for other pools
|
||||
*/
|
||||
uint16 public minObservationCardinality;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ V2 TWAP ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/**
|
||||
* @notice Uniswap V2 torn pool cumulative price last
|
||||
*/
|
||||
uint256 public lastCumulativeTORNPriceInETH;
|
||||
|
||||
/**
|
||||
* @notice Last TWAP data
|
||||
*/
|
||||
TWAPData public last;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
event PoolFeeUpdated(IERC20 token, uint24 newPoolFee);
|
||||
event GlobalTORNPoolFeeUpdated(uint24 newPoolFee);
|
||||
event GlobalTwapIntervalSecondsUpdated(uint32 newUniswapTimePeriod);
|
||||
event GlobalMinObservationCardinalityUpdated(uint16 newMinObservationCardinality);
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
constructor(address _governanceProxyAddress, address _feeOracleManagerAddress) public {
|
||||
governanceProxyAddress = _governanceProxyAddress;
|
||||
feeOracleManagerAddress = _feeOracleManagerAddress;
|
||||
|
||||
(, uint256 _price1CumulativeLast, uint32 _timestamp) =
|
||||
UniswapV2OracleLibrary.currentCumulativePrices(v2TORNPoolAddress);
|
||||
|
||||
lastCumulativeTORNPriceInETH = _price1CumulativeLast;
|
||||
last.updatedTimestamp = _timestamp;
|
||||
}
|
||||
|
||||
modifier onlyGovernance() {
|
||||
require(msg.sender == governanceProxyAddress, "UniswapV3FeeOracle: onlyGovernance");
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyFeeOracleManager() {
|
||||
require(msg.sender == feeOracleManagerAddress, "UniswapV3FeeOracle: onlyGovernance");
|
||||
_;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MAIN ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function getFee(
|
||||
IERC20,
|
||||
ITornadoInstance _instance,
|
||||
InstanceData memory _data,
|
||||
uint32 _feePercent,
|
||||
uint32 _feePercentDivisor
|
||||
) public view virtual override returns (uint160) {
|
||||
// If 0%, 0
|
||||
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);
|
||||
|
||||
// Now get the price ratio
|
||||
uint256 priceRatio = getTokenPerTORN(_data.token);
|
||||
|
||||
// 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.denomination().mul(1e18).div(priceRatio).mul(uint256(_feePercent)).div(
|
||||
uint256(_feePercentDivisor)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getTokenPerTORN(IERC20 _token) public view virtual returns (uint256) {
|
||||
// Get the average price of ETH in the TOKEN
|
||||
uint256 ethPerTokenAverage = UniswapV3OracleHelper.getPriceOfTokenInWETH(
|
||||
address(_token), poolFeesByToken[_token], twapIntervalSeconds
|
||||
);
|
||||
|
||||
// Now get average price of TORN per TOKEN
|
||||
return ethPerTokenAverage * last.averagePrice.decode() / 1e18;
|
||||
}
|
||||
|
||||
function update() public virtual override onlyFeeOracleManager {
|
||||
// Get the timestamp of the last update
|
||||
uint32 timestampLastUpdate = last.updatedTimestamp;
|
||||
|
||||
// Get the current one by Uniswaps logic, has inbuilt overflow
|
||||
uint32 currentTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
|
||||
|
||||
// Calculate elapsed time, no matter whether overflow (uniswap logic)
|
||||
uint32 elapsed = currentTimestamp - timestampLastUpdate;
|
||||
|
||||
// Get last token0/token1 meaning TORN per ETH
|
||||
(, uint256 _price1CumulativeLast,) = UniswapV2OracleLibrary.currentCumulativePrices(v2TORNPoolAddress);
|
||||
|
||||
// Save TWAP data, meaning the average price (TORN per ETH)
|
||||
last = TWAPData({
|
||||
averagePrice: FixedPoint.uq112x112(
|
||||
uint224((_price1CumulativeLast - lastCumulativeTORNPriceInETH) / elapsed)
|
||||
),
|
||||
updatedTimestamp: currentTimestamp
|
||||
});
|
||||
|
||||
// Update the cumulative value
|
||||
lastCumulativeTORNPriceInETH = _price1CumulativeLast;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function setPoolFeeForToken(IERC20 _token, uint24 _tokenPoolFee) public virtual onlyGovernance {
|
||||
// Get global config
|
||||
|
||||
// Check whether globals are initialized
|
||||
|
||||
require(twapIntervalSeconds != 0, "UniswapV3FeeOracle: time period not initialized");
|
||||
require(minObservationCardinality != 0, "UniswapV3FeeOracle: cardinality not initialized");
|
||||
|
||||
// Only do this if not zeroing out
|
||||
if (_tokenPoolFee != 0) {
|
||||
// Check whether a pool exists for the token + fee combination
|
||||
|
||||
address poolAddress = UniswapV3OracleHelper.UniswapV3Factory.getPool(
|
||||
address(_token), UniswapV3OracleHelper.WETH, _tokenPoolFee
|
||||
);
|
||||
|
||||
require(poolAddress != address(0), "UniswapV3FeeOracle: pool for token and fee does not exist");
|
||||
|
||||
// Check whether the pool has a large enough observation cardinality
|
||||
|
||||
(,,,, uint16 observationCardinalityNext,,) = IUniswapV3PoolState(poolAddress).slot0();
|
||||
|
||||
require(
|
||||
minObservationCardinality <= observationCardinalityNext,
|
||||
"UniswapV3FeeOracle: pool observation cardinality low"
|
||||
);
|
||||
}
|
||||
|
||||
// Store & log
|
||||
|
||||
poolFeesByToken[_token] = _tokenPoolFee;
|
||||
|
||||
emit PoolFeeUpdated(_token, _tokenPoolFee);
|
||||
}
|
||||
|
||||
function setTwapIntervalSeconds(uint32 _newGlobalTwapIntervalSeconds) public virtual onlyGovernance {
|
||||
twapIntervalSeconds = _newGlobalTwapIntervalSeconds;
|
||||
emit GlobalTwapIntervalSecondsUpdated(_newGlobalTwapIntervalSeconds);
|
||||
}
|
||||
|
||||
function setMinObservationCardinality(uint16 _newGlobalMinObservationCardinality)
|
||||
public
|
||||
virtual
|
||||
onlyGovernance
|
||||
{
|
||||
minObservationCardinality = _newGlobalMinObservationCardinality;
|
||||
emit GlobalMinObservationCardinalityUpdated(_newGlobalMinObservationCardinality);
|
||||
}
|
||||
|
||||
function setFeeOracleManagerAddress(address _newFeeOracleManagerAddress) public virtual onlyGovernance {
|
||||
feeOracleManagerAddress = _newFeeOracleManagerAddress;
|
||||
}
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function getAverageTORNPerETH() public view virtual returns (uint256) {
|
||||
return last.averagePrice.decode();
|
||||
}
|
||||
}
|
@ -46,13 +46,19 @@ contract UniswapV3FeeOracle is IFeeOracle {
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* @dev The Governance Proxy address */
|
||||
/**
|
||||
* @notice The Governance Proxy address
|
||||
*/
|
||||
address public immutable governanceProxyAddress;
|
||||
|
||||
/* @dev Global configuration valid across all `getFee()` (and other) calls */
|
||||
/**
|
||||
* @notice Global configuration valid across all `getFee()` (and other) calls
|
||||
*/
|
||||
GlobalOracleConfig public globals;
|
||||
|
||||
/* @dev Uniswap pool fees for each token registered */
|
||||
/**
|
||||
* @notice Uniswap pool fees for each token registered
|
||||
*/
|
||||
mapping(IERC20 => uint24) public poolFeesByToken;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
@ -75,12 +81,14 @@ contract UniswapV3FeeOracle is IFeeOracle {
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function update() public virtual override { }
|
||||
|
||||
function getFee(
|
||||
IERC20 _torn,
|
||||
ITornadoInstance _instance,
|
||||
InstanceData memory _data,
|
||||
uint64 _feePercent,
|
||||
uint64 _feePercentDivisor
|
||||
uint32 _feePercent,
|
||||
uint32 _feePercentDivisor
|
||||
) public view virtual override returns (uint160) {
|
||||
// If fee is 0 return
|
||||
if (_feePercent == 0) return 0;
|
||||
|
@ -16,11 +16,13 @@ import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/
|
||||
import { InstanceData } from "../InstanceRegistry.sol";
|
||||
|
||||
interface IFeeOracle {
|
||||
function update() external;
|
||||
|
||||
function getFee(
|
||||
IERC20 _torn,
|
||||
ITornadoInstance _instance,
|
||||
InstanceData memory _data,
|
||||
uint64 _feePercent,
|
||||
uint64 _feePercentDivisor
|
||||
uint32 _feePercent,
|
||||
uint32 _feePercentDivisor
|
||||
) external view returns (uint160);
|
||||
}
|
||||
|
@ -19,18 +19,23 @@ import { console2 } from "forge-std/console2.sol";
|
||||
|
||||
// Local imports
|
||||
|
||||
import { FeeOracleManager } from "src/v2/FeeOracleManager.sol";
|
||||
|
||||
import { IGovernance, Proposal } from "common/interfaces/IGovernance.sol";
|
||||
|
||||
import { TornadoAddresses } from "common/TornadoAddresses.sol";
|
||||
|
||||
import { UniswapV3FeeOracle } from "src/v2/UniswapV3FeeOracle.sol";
|
||||
import { UniswapFeeOracle } from "src/v2/UniswapFeeOracle.sol";
|
||||
|
||||
import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol";
|
||||
|
||||
import { InstanceData } from "src/v2/InstanceRegistry.sol";
|
||||
|
||||
contract OracleTests is Test {
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
FeeOracleManager public constant feeOracleManager =
|
||||
FeeOracleManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7);
|
||||
|
||||
address public constant crvUSDUSDCStableswap2Pool = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E;
|
||||
|
||||
@ -40,23 +45,22 @@ contract OracleTests is Test {
|
||||
|
||||
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||
|
||||
UniswapV3FeeOracle v3FeeOracle;
|
||||
UniswapFeeOracle uniswapFeeOracle;
|
||||
|
||||
CurveFeeOracle feeOracle;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function setUp() public {
|
||||
vm.createSelectFork(vm.envString("MAINNET_RPC_URL"));
|
||||
|
||||
v3FeeOracle = new UniswapV3FeeOracle(address(this));
|
||||
v3FeeOracle.setGlobalTornPoolFee(10_000, true);
|
||||
v3FeeOracle.setGlobalTwapIntervalSeconds(5400);
|
||||
v3FeeOracle.setGlobalMinObservationCardinality(10);
|
||||
uniswapFeeOracle = new UniswapFeeOracle(address(this), address(feeOracleManager));
|
||||
uniswapFeeOracle.setTwapIntervalSeconds(5400);
|
||||
uniswapFeeOracle.setMinObservationCardinality(10);
|
||||
|
||||
feeOracle = new CurveFeeOracle(address(this));
|
||||
feeOracle.setTornOracleIsUniswapV3(false);
|
||||
feeOracle.setUniswapV3FeeOracle(v3FeeOracle);
|
||||
feeOracle.setUniswapFeeOracle(uniswapFeeOracle);
|
||||
}
|
||||
|
||||
function test_curveFeeSingleTricrypto() public {
|
||||
@ -89,7 +93,7 @@ contract OracleTests is Test {
|
||||
);
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
function _setCurveFeeSimpleTricryptoOracleForInstance(
|
||||
CurveFeeOracle _feeOracle,
|
||||
|
@ -11,13 +11,17 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
|
||||
import { console2 } from "forge-std/console2.sol";
|
||||
|
||||
// Tornado imports
|
||||
|
||||
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
|
||||
|
||||
// Local imports
|
||||
|
||||
import { UniswapV3FeeOracle } from "src/v2/UniswapV3FeeOracle.sol";
|
||||
import { UniswapFeeOracle } from "src/v2/UniswapFeeOracle.sol";
|
||||
|
||||
import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol";
|
||||
|
||||
import { InstanceRegistry } from "src/v2/InstanceRegistry.sol";
|
||||
import { InstanceRegistry, InstanceData } from "src/v2/InstanceRegistry.sol";
|
||||
|
||||
import { FeeOracleManager } from "src/v2/FeeOracleManager.sol";
|
||||
|
||||
@ -31,49 +35,486 @@ import { CRVUSDInstancesProposal } from "src/proposals/CRVUSDInstancesProposal.s
|
||||
|
||||
import { TornadoProposalTest, ProposalState } from "./TornadoProposalTest.sol";
|
||||
|
||||
contract ProposalTests is TornadoProposalTest {
|
||||
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 ProposalTests is Instances, TornadoProposalTest {
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
|
||||
IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
|
||||
|
||||
CurveFeeOracle curveFeeOracle;
|
||||
FeeOracleManager public constant feeOracleManager =
|
||||
FeeOracleManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7);
|
||||
|
||||
UniswapV3FeeOracle v3FeeOracle;
|
||||
InstanceRegistry public constant instanceRegistry =
|
||||
InstanceRegistry(0xB20c66C4DE72433F3cE747b58B86830c459CA911);
|
||||
|
||||
// Implementations
|
||||
|
||||
InstanceRegistry implInstanceRegisty;
|
||||
|
||||
FeeOracleManager feeOracleManager;
|
||||
FeeOracleManager implFeeOracleManager;
|
||||
|
||||
// Actual contracts
|
||||
|
||||
CurveFeeOracle curveFeeOracle;
|
||||
|
||||
UniswapFeeOracle uniswapFeeOracle;
|
||||
|
||||
TornadoRouter router;
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
function setUp() public override {
|
||||
super.setUp();
|
||||
TornadoProposalTest.setUp();
|
||||
|
||||
vm.rollFork(17_438_199);
|
||||
vm.rollFork(17_486_841);
|
||||
|
||||
curveFeeOracle = new CurveFeeOracle(address(governance));
|
||||
|
||||
v3FeeOracle = new UniswapV3FeeOracle(address(governance));
|
||||
uniswapFeeOracle = new UniswapFeeOracle(address(governance), address(feeOracleManager));
|
||||
|
||||
vm.prank(address(governance));
|
||||
|
||||
curveFeeOracle.setUniswapV3FeeOracle(v3FeeOracle);
|
||||
curveFeeOracle.setUniswapFeeOracle(uniswapFeeOracle);
|
||||
|
||||
implInstanceRegisty = new InstanceRegistry(address(governance));
|
||||
|
||||
feeOracleManager = new FeeOracleManager(address(TORN), address(governance));
|
||||
implFeeOracleManager = new FeeOracleManager(address(TORN), address(governance));
|
||||
|
||||
router = new TornadoRouter(address(governance));
|
||||
router = TornadoRouter(vm.envAddress("TORNADO_ROUTER_ADDRESS"));
|
||||
}
|
||||
|
||||
function test_crvusdInstancesBasic() public {
|
||||
// Do the proposal first
|
||||
test_crvusdInstancesProposalBasic();
|
||||
|
||||
// Try to update crvusd 10000
|
||||
feeOracleManager.updateFee(cu10_000, false);
|
||||
|
||||
// Try to update all
|
||||
ITornadoInstance[] memory _toUpdate = new ITornadoInstance[](5);
|
||||
|
||||
_toUpdate[0] = cu100;
|
||||
_toUpdate[1] = cu1_000;
|
||||
_toUpdate[2] = cu10_000;
|
||||
_toUpdate[3] = cu100_000;
|
||||
_toUpdate[4] = cu1_000_000;
|
||||
|
||||
feeOracleManager.updateFees(_toUpdate, false);
|
||||
|
||||
// Print fees to console.
|
||||
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ LAST FEES ~~~~~~~~~~~~~~~~~~\n");
|
||||
console2.log("cu100: ", uint256(feeOracleManager.getLastFeeForInstance(cu100)));
|
||||
console2.log("cu1_000: ", uint256(feeOracleManager.getLastFeeForInstance(cu1_000)));
|
||||
console2.log("cu10_000: ", uint256(feeOracleManager.getLastFeeForInstance(cu10_000)));
|
||||
console2.log("cu100_000: ", uint256(feeOracleManager.getLastFeeForInstance(cu100_000)));
|
||||
console2.log("cu1_000_000: ", uint256(feeOracleManager.getLastFeeForInstance(cu1_000_000)));
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
delimit();
|
||||
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ UPDATED FEES ~~~~~~~~~~~~~~~~~~\n");
|
||||
console2.log("cu100: ", uint256(feeOracleManager.instanceFeeWithUpdate(cu100)));
|
||||
console2.log("cu1_000: ", uint256(feeOracleManager.instanceFeeWithUpdate(cu1_000)));
|
||||
console2.log("cu10_000: ", uint256(feeOracleManager.instanceFeeWithUpdate(cu10_000)));
|
||||
console2.log("cu100_000: ", uint256(feeOracleManager.instanceFeeWithUpdate(cu100_000)));
|
||||
console2.log("cu1_000_000: ", uint256(feeOracleManager.instanceFeeWithUpdate(cu1_000_000)));
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
delimit();
|
||||
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ DATA ~~~~~~~~~~~~~~~~~~\n");
|
||||
InstanceData memory data = instanceRegistry.getInstanceData(cu100);
|
||||
delimit();
|
||||
console2.log("cu100:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(cu1_000);
|
||||
console2.log("cu1_000:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(cu10_000);
|
||||
console2.log("cu10_000:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(cu100_000);
|
||||
console2.log("cu100_000:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(cu1_000_000);
|
||||
console2.log("cu1_000_000:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
// Some assertions
|
||||
|
||||
require(
|
||||
strcomp(curveFeeOracle.getChainedOracleNameForInstance(cu100), "ETH/CRVUSD"),
|
||||
"oracle name doesnt match"
|
||||
);
|
||||
require(
|
||||
strcomp(
|
||||
curveFeeOracle.getChainedOracleNameForOracleHash(
|
||||
curveFeeOracle.getChainedOracleHashForInstance(cu1_000)
|
||||
),
|
||||
"ETH/CRVUSD"
|
||||
),
|
||||
"oracle name doesnt match"
|
||||
);
|
||||
|
||||
// Should be able to update all fees
|
||||
feeOracleManager.updateAllFees(true);
|
||||
feeOracleManager.updateAllFees(false);
|
||||
|
||||
// Just for some gas values for this call
|
||||
instanceRegistry.instanceData(cu100);
|
||||
instanceRegistry.instanceData(cu1_000);
|
||||
}
|
||||
|
||||
function test_infrastructureBasic() public {
|
||||
// Do the proposal first
|
||||
test_infrastructureUpgradeProposalBasic();
|
||||
|
||||
// Try to update eth10
|
||||
feeOracleManager.updateFee(eth10, false);
|
||||
|
||||
// Try to update multiple
|
||||
ITornadoInstance[] memory _toUpdate = new ITornadoInstance[](3);
|
||||
|
||||
_toUpdate[0] = eth100;
|
||||
_toUpdate[1] = dai10000;
|
||||
_toUpdate[2] = usdt100;
|
||||
|
||||
feeOracleManager.updateFees(_toUpdate, false);
|
||||
|
||||
require(feeOracleManager.getLastUpdatedTimeForInstance(eth100) == now, "timeup");
|
||||
|
||||
// Must not be able to re-initialize the InstanceRegistry
|
||||
|
||||
vm.expectRevert();
|
||||
vm.prank(address(governance));
|
||||
instanceRegistry.initialize(_toUpdate, router);
|
||||
|
||||
vm.expectRevert();
|
||||
instanceRegistry.initialize(_toUpdate, router);
|
||||
|
||||
// Must not be able to re-initialize the FeeOracleManager
|
||||
|
||||
vm.expectRevert();
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.initialize(
|
||||
address(uniswapFeeOracle), address(instanceRegistry), _toUpdate, feeArrayForTesting_1()
|
||||
);
|
||||
|
||||
vm.expectRevert();
|
||||
feeOracleManager.initialize(
|
||||
address(uniswapFeeOracle), address(instanceRegistry), _toUpdate, feeArrayForTesting_1()
|
||||
);
|
||||
|
||||
// Must not be able to re-initialize the router... the contract addresses are unimportant
|
||||
|
||||
vm.expectRevert();
|
||||
vm.prank(address(governance));
|
||||
router.initialize(address(instanceRegistry), address(instanceRegistry));
|
||||
|
||||
vm.expectRevert();
|
||||
router.initialize(address(instanceRegistry), address(instanceRegistry));
|
||||
|
||||
// Check fee logic and print to console.
|
||||
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ LAST FEES ~~~~~~~~~~~~~~~~~~\n");
|
||||
console2.log("eth10: ", uint256(feeOracleManager.getLastFeeForInstance(eth10)));
|
||||
console2.log("eth100: ", uint256(feeOracleManager.getLastFeeForInstance(eth100)));
|
||||
console2.log("dai10000: ", uint256(feeOracleManager.getLastFeeForInstance(dai10000)));
|
||||
console2.log("usdt100: ", uint256(feeOracleManager.getLastFeeForInstance(usdt100)));
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
delimit();
|
||||
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ UPDATED FEES ~~~~~~~~~~~~~~~~~~\n");
|
||||
console2.log("eth10: ", uint256(feeOracleManager.instanceFeeWithUpdate(eth10)));
|
||||
console2.log("eth100: ", uint256(feeOracleManager.instanceFeeWithUpdate(eth100)));
|
||||
console2.log("dai10000: ", uint256(feeOracleManager.instanceFeeWithUpdate(dai10000)));
|
||||
console2.log("usdt100: ", uint256(feeOracleManager.instanceFeeWithUpdate(usdt100)));
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
delimit();
|
||||
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ DATA ~~~~~~~~~~~~~~~~~~\n");
|
||||
InstanceData memory data = instanceRegistry.getInstanceData(eth10);
|
||||
delimit();
|
||||
console2.log("eth10:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(eth100);
|
||||
console2.log("eth100:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(dai10000);
|
||||
console2.log("dai10000:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(usdt100);
|
||||
console2.log("usdt100:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
// Now should be able to rmemove an instance
|
||||
vm.prank(address(governance));
|
||||
instanceRegistry.removeInstanceByAddress(address(dai10000)); // DAI10k
|
||||
|
||||
vm.prank(address(governance));
|
||||
instanceRegistry.removeInstanceByIndex(2); // ETH10
|
||||
|
||||
// Now log again
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~ SHOULD HAVE CHANGED ~~~~~~~~~~~~~~~~~~\n");
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(2);
|
||||
console2.log("NOT eth10:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
delimit();
|
||||
data = instanceRegistry.getInstanceData(6);
|
||||
console2.log("NOT dai10000:");
|
||||
console2.log("token: ", address(data.token));
|
||||
console2.log("index: ", uint256(data.index));
|
||||
console2.log("iserc20: ", data.isERC20);
|
||||
console2.log("isenabled: ", data.isEnabled);
|
||||
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
|
||||
|
||||
// Now add them back and only do assertions
|
||||
vm.prank(address(governance));
|
||||
instanceRegistry.addInstance(dai10000);
|
||||
|
||||
vm.prank(address(governance));
|
||||
instanceRegistry.addInstance(eth10);
|
||||
|
||||
vm.prank(address(governance));
|
||||
data = instanceRegistry.getInstanceData(dai10000);
|
||||
require(data.token == DAI, "not dai");
|
||||
require(data.index == 15, "not 15");
|
||||
require(data.isERC20, "not token");
|
||||
require(data.isEnabled, "not enabled");
|
||||
|
||||
vm.prank(address(governance));
|
||||
data = instanceRegistry.getInstanceData(eth10);
|
||||
require(data.token == IERC20(0), "not eth");
|
||||
require(data.index == 16, "not last");
|
||||
require(!data.isERC20, "not not token");
|
||||
require(data.isEnabled, "not enabled");
|
||||
|
||||
// Some assertions to test getters
|
||||
require(instanceRegistry.getInstanceIndex(eth100) == 3, "Wrong index");
|
||||
require(instanceRegistry.getInstanceToken(eth100) == IERC20(address(0)), "Wrong token");
|
||||
require(instanceRegistry.getInstanceToken(dai10000) == DAI, "Wrong token");
|
||||
require(instanceRegistry.isEnabledInstance(dai10000) == true, "Wrong token");
|
||||
|
||||
// Just to get some gas values for this fn
|
||||
instanceRegistry.instanceData(eth10);
|
||||
instanceRegistry.instanceData(eth100);
|
||||
instanceRegistry.instanceData(dai10000);
|
||||
|
||||
// Expect no update if in inside the time limit interval
|
||||
|
||||
uint256 startTimestamp = block.timestamp;
|
||||
uint256 eth100Fee = feeOracleManager.getLastFeeForInstance(eth100);
|
||||
uint256 interval = feeOracleManager.getFeeUpdateIntervalForInstance(eth100);
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeePercentForInstance(eth100, 50);
|
||||
|
||||
feeOracleManager.updateFee(eth100, true);
|
||||
require(eth100Fee == feeOracleManager.getLastFeeForInstance(eth100), "wrong fee 1");
|
||||
|
||||
vm.warp(startTimestamp + interval + 5 days);
|
||||
|
||||
feeOracleManager.updateFee(eth100, true);
|
||||
uint256 eth100Fee2 = feeOracleManager.getLastFeeForInstance(eth100);
|
||||
require(eth100Fee != eth100Fee2, "wrong fee 2");
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeePercentForInstance(eth100, 30);
|
||||
|
||||
vm.warp(startTimestamp + 2 * interval + 10 days);
|
||||
|
||||
feeOracleManager.updateFee(eth100, true);
|
||||
require(eth100Fee == feeOracleManager.getLastFeeForInstance(eth100), "wrong fee 3");
|
||||
|
||||
vm.warp(startTimestamp);
|
||||
|
||||
// Should be able to update all fees
|
||||
feeOracleManager.updateAllFees(true);
|
||||
feeOracleManager.updateAllFees(false);
|
||||
|
||||
// Deviations don't revert
|
||||
|
||||
feeOracleManager.getAllFeeDeviations();
|
||||
|
||||
// There is a deviation
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeePercentForInstance(eth100, 10);
|
||||
|
||||
_toUpdate = new ITornadoInstance[](1);
|
||||
|
||||
_toUpdate[0] = eth100;
|
||||
|
||||
int256[] memory eth100Deviations = feeOracleManager.getFeeDeviationsForInstances(_toUpdate);
|
||||
|
||||
require(eth100Deviations[0] != 0, "deviations");
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeePercentForInstance(eth100, 30);
|
||||
|
||||
feeOracleManager.updateFee(eth100, false);
|
||||
|
||||
// Should be able to deposit via router
|
||||
|
||||
address depositor = address(bytes20(bytes32(keccak256("depositor"))));
|
||||
|
||||
vm.deal(depositor, 1 ether); // for gas
|
||||
|
||||
vm.prank(address(DAI));
|
||||
DAI.transfer(depositor, 100_000e18);
|
||||
|
||||
require(DAI.balanceOf(depositor) == 100_000e18, "not enough depo bal");
|
||||
|
||||
vm.prank(address(depositor));
|
||||
DAI.approve(address(router), 100_000e18);
|
||||
|
||||
bytes32 commitment = 0x00eb84375e4bfd042468fe624e6d322dba4f601b37dd0a44f741d3cb04127df0;
|
||||
|
||||
bytes memory empty;
|
||||
|
||||
vm.prank(address(depositor));
|
||||
|
||||
router.deposit(dai100000, commitment, empty);
|
||||
|
||||
// Let's toss in some functions for coverage
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeeUpdateIntervalForInstance(eth100, 60_000);
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeeUpdateIntervalForInstance(eth100, uint24(interval));
|
||||
|
||||
require(feeOracleManager.getFeePercentForInstance(eth100) == 30, "fee percent eth100");
|
||||
require(feeOracleManager.getLastUpdatedTimeForInstance(eth100) == now, "updated time eth100");
|
||||
|
||||
// Reverts
|
||||
|
||||
vm.expectRevert();
|
||||
feeOracleManager.setInstanceRegistry(address(0));
|
||||
vm.expectRevert();
|
||||
feeOracleManager.setFeeOracle(address(eth100), address(0));
|
||||
vm.expectRevert();
|
||||
feeOracleManager.setFeePercentForInstance(eth100, 40);
|
||||
vm.expectRevert();
|
||||
feeOracleManager.setFeeUpdateIntervalForInstance(eth100, uint24(interval - 3));
|
||||
|
||||
// Should be able to delete and set an oracle
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeeOracle(address(eth100), address(0));
|
||||
|
||||
require(address(feeOracleManager.instanceFeeOracles(eth100)) == address(0), "fee oracle deletion");
|
||||
|
||||
vm.prank(address(governance));
|
||||
feeOracleManager.setFeeOracle(address(eth100), address(uniswapFeeOracle));
|
||||
}
|
||||
|
||||
function test_crvusdInstancesProposalBasic() public {
|
||||
// First pass the former proposal
|
||||
test_infrastructureUpgradeProposalBasic();
|
||||
|
||||
// Then create the crvUSD proposal
|
||||
address proposal =
|
||||
address(new CRVUSDInstancesProposal(address(curveFeeOracle), address(uniswapFeeOracle)));
|
||||
|
||||
// Propose
|
||||
uint256 id = easyPropose(proposal);
|
||||
|
||||
// Wait
|
||||
waitUntilExecutable(id);
|
||||
|
||||
// Exec
|
||||
governance.execute(id);
|
||||
}
|
||||
|
||||
function test_infrastructureUpgradeProposalBasic() public {
|
||||
// Create proposal
|
||||
address proposal = address(
|
||||
new InfrastructureUpgradeProposal(
|
||||
address(v3FeeOracle),
|
||||
address(feeOracleManager),
|
||||
address(uniswapFeeOracle),
|
||||
address(implFeeOracleManager),
|
||||
address(implInstanceRegisty),
|
||||
address(router)
|
||||
)
|
||||
@ -87,22 +528,30 @@ contract ProposalTests is TornadoProposalTest {
|
||||
|
||||
// Exec
|
||||
governance.execute(id);
|
||||
|
||||
// Test whethe resolving names works... with the Governance contract
|
||||
|
||||
require(
|
||||
address(instanceRegistry.getInstanceByENSName("governance.contract.tornadocash.eth"))
|
||||
== address(governance),
|
||||
"ENSResolving doesn't work"
|
||||
);
|
||||
}
|
||||
|
||||
function test_crvusdInstancesProposalBasic() public {
|
||||
// First pass the former proposal
|
||||
test_infrastructureUpgradeProposalBasic();
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
// Then create the crvUSD proposal
|
||||
address proposal = address(new CRVUSDInstancesProposal(address(curveFeeOracle), address(v3FeeOracle)));
|
||||
function delimit() internal view {
|
||||
console2.log();
|
||||
}
|
||||
|
||||
// Propose
|
||||
uint256 id = easyPropose(proposal);
|
||||
function strcomp(string memory left, string memory right) internal pure returns (bool) {
|
||||
return keccak256(abi.encode(left)) == keccak256(abi.encode(right));
|
||||
}
|
||||
|
||||
// Wait
|
||||
waitUntilExecutable(id);
|
||||
|
||||
// Exec
|
||||
governance.execute(id);
|
||||
function feeArrayForTesting_1() internal pure returns (uint256[] memory fees) {
|
||||
fees = new uint256[](3);
|
||||
fees[0] = 30;
|
||||
fees[1] = 30;
|
||||
fees[2] = 30;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user