// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; // OZ Imports import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // Tornado imports import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol"; // STD Imports import { Test } from "forge-std/Test.sol"; import { console2 } from "forge-std/console2.sol"; // Local imports import { TornadoProposalTest } from "./TornadoProposalTest.sol"; import { IGovernance, Proposal } from "common/interfaces/IGovernance.sol"; import { IUniswapV2Pair } from "src/v2/interfaces/IUniswapV2Pair.sol"; import { TornadoAddresses } from "common/TornadoAddresses.sol"; import { FeeOracleManager, FeeDataForOracle, InstanceWithFee } from "src/v2/FeeOracleManager.sol"; import { UniswapFeeOracle } from "src/v2/UniswapFeeOracle.sol"; import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol"; import { InstanceState } from "src/v2/InstanceRegistry.sol"; interface IWethDepositable { function deposit() external payable; } contract Instances { /* ETH instances */ ITornadoInstance public constant eth01 = ITornadoInstance(0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc); ITornadoInstance public constant eth1 = ITornadoInstance(0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936); ITornadoInstance public constant eth10 = ITornadoInstance(0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF); ITornadoInstance public constant eth100 = ITornadoInstance(0xA160cdAB225685dA1d56aa342Ad8841c3b53f291); /* DAI instances */ ITornadoInstance public constant dai100 = ITornadoInstance(0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3); ITornadoInstance public constant dai1000 = ITornadoInstance(0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144); ITornadoInstance public constant dai10000 = ITornadoInstance(0x07687e702b410Fa43f4cB4Af7FA097918ffD2730); ITornadoInstance public constant dai100000 = ITornadoInstance(0x23773E65ed146A459791799d01336DB287f25334); /* cDAI instances */ ITornadoInstance public constant cdai100 = ITornadoInstance(0x22aaA7720ddd5388A3c0A3333430953C68f1849b); ITornadoInstance public constant cdai1000 = ITornadoInstance(0x03893a7c7463AE47D46bc7f091665f1893656003); ITornadoInstance public constant cdai10000 = ITornadoInstance(0x2717c5e28cf931547B621a5dddb772Ab6A35B701); ITornadoInstance public constant cdai100000 = ITornadoInstance(0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af); /* USDT instances */ ITornadoInstance public constant usdt100 = ITornadoInstance(0x169AD27A470D064DEDE56a2D3ff727986b15D52B); ITornadoInstance public constant usdt1000 = ITornadoInstance(0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f); /* WBTC instances */ ITornadoInstance public constant wbtc01 = ITornadoInstance(0x178169B423a011fff22B9e3F3abeA13414dDD0F1); ITornadoInstance public constant wbtc1 = ITornadoInstance(0x610B717796ad172B316836AC95a2ffad065CeaB4); ITornadoInstance public constant wbtc10 = ITornadoInstance(0xbB93e510BbCD0B7beb5A853875f9eC60275CF498); /* CRVUSD instances */ ITornadoInstance public constant cu100 = ITornadoInstance(0x913a73486Dc4AA3832A56d461542836C1eeB93be); ITornadoInstance public constant cu1_000 = ITornadoInstance(0x5A6b3C829dB3e938C885000c6E93CF35E74876a4); ITornadoInstance public constant cu10_000 = ITornadoInstance(0x49f173CDAB99a2C3800F1255393DF9B7a17B82Bb); ITornadoInstance public constant cu100_000 = ITornadoInstance(0x4640Dffc9fD0B113B983e3A350b070a119CA143C); ITornadoInstance public constant cu1_000_000 = ITornadoInstance(0xc4eA8Bd3Fd76f3c255395793B47F7c55aD59d991); } contract OracleTests is Instances, TornadoProposalTest { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ // Tokens - constant IERC20 internal constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); // Contracts - constant FeeOracleManager public constant feeOracleManager = FeeOracleManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7); IUniswapV2Pair public constant uniTornPool = IUniswapV2Pair(0x0C722a487876989Af8a05FFfB6e32e45cc23FB3A); address public constant crvUSDUSDCStableswap2Pool = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E; address public constant tricryptoUSDCPool = 0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B; // Contracts - mutable UniswapFeeOracle uniswapFeeOracle; CurveFeeOracle feeOracle; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ function setUp() public virtual override { vm.createSelectFork(vm.envString("MAINNET_RPC_URL")); vm.rollFork(17_493_778); uniswapFeeOracle = new UniswapFeeOracle(address(this), address(this)); uniswapFeeOracle.setMinObservationCardinality(10); uniswapFeeOracle.setPoolFeeForToken(WETH, 0); feeOracle = new CurveFeeOracle(address(this)); feeOracle.setTornOracleIsUniswap(false); feeOracle.setUniswapFeeOracle(uniswapFeeOracle); } function test_curveFeeSingleTricrypto() public { _setCurveFeeSimpleTricryptoOracleForInstance(feeOracle, cu10_000); // CRVUSD 10_000 console2.log( "\nShould be 30 * (ETH/USD) ------------------------------------------------\n", uint256( feeOracle.getFee( TORN, InstanceWithFee({ logic: cu10_000, state: InstanceState(IERC20(cu10_000.token()), 0, true, true), fee: FeeDataForOracle(0, 30, 10_000, 2 days, 0) }) ) ), "\n------------------------------------------------------------------------\n" ); } function test_curveFeeChainedTORN() public { uint256 timeBeforeSwap = now; feeOracle.setTornOracleIsUniswap(true); _setCurveFeeChainedOracleForInstance(feeOracle, cu10_000); // CRVUSD 10_000 _advanceTORNETHMarket(); uniswapFeeOracle.update( TORN, InstanceWithFee({ logic: cu10_000, state: InstanceState(IERC20(address(0)), 0, false, false), fee: FeeDataForOracle(0, 0, 0, 0, uint32(timeBeforeSwap)) }) ); console2.log( "\nTORN Fee calculated ------------------------------------------------------\n", uint256( feeOracle.getFee( TORN, InstanceWithFee({ logic: cu10_000, state: InstanceState(IERC20(cu10_000.token()), 0, true, true), fee: FeeDataForOracle(0, 30, 10_000, 2 days, uint32(timeBeforeSwap)) }) ) ), "\n------------------------------------------------------------------------\n" ); } function test_UniswapTORNOracle() public { uint256 timeBeforeSwap = now; _advanceTORNETHMarket(); InstanceWithFee memory feepool = InstanceWithFee({ logic: eth100, state: InstanceState(WETH, 0, false, true), fee: FeeDataForOracle(0, 30, 10_000, 2 days, uint32(timeBeforeSwap)) }); uniswapFeeOracle.update(TORN, feepool); console2.log( "\nTORN PER ETH TWAP (1e9) ------------------------------------------------\n", uniswapFeeOracle.getAverageTORNPerETH(), "\n------------------------------------------------------------------------\n" ); console2.log( "\nTORN Fee for ETH100 ----------------------------------------------------\n", uniswapFeeOracle.getFee(TORN, feepool), "\n------------------------------------------------------------------------\n" ); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ function _advanceTORNETHMarket() internal { console2.log("\ntrader is now going to move the price\n"); uint256 lastPriceAvg0 = uniTornPool.price0CumulativeLast(); uint256 lastPriceAvg1 = uniTornPool.price0CumulativeLast(); uint256 rn = now; address trader = address(bytes20(keccak256("trader"))); vm.deal(trader, 20 ether); vm.prank(trader); IWethDepositable(address(WETH)).deposit{ value: 19 ether }(); require(WETH.balanceOf(trader) == 19 ether, "deposit for weth"); vm.warp(rn + 2 days + 1 hours); vm.prank(trader); WETH.transfer(address(uniTornPool), 19 ether); uniTornPool.swap(6_600_000_000_000_000_000_000, 0, trader, new bytes(0)); console2.log("\ntrader received =>", TORN.balanceOf(trader), "TORN\n"); require(lastPriceAvg0 != uniTornPool.price0CumulativeLast(), "twap moving 0 fail"); require(lastPriceAvg1 != uniTornPool.price1CumulativeLast(), "twap moving 1 fail"); } function _setCurveFeeSimpleTricryptoOracleForInstance( CurveFeeOracle _feeOracle, ITornadoInstance _instance ) internal { ICurvePriceOracle[] memory _oracles = new ICurvePriceOracle[](1); _oracles[0] = ICurvePriceOracle(tricryptoUSDCPool); bytes4[] memory _selectors = new bytes4[](1); _selectors[0] = CurveChainedOracles.PRICE_ORACLE_UINT256_SELECTOR; uint8[] memory _coins = new uint8[](1); _coins[0] = 1; // ETHER bool[] memory _invert = new bool[](1); _invert[0] = true; _feeOracle.modifyChainedOracleForInstance( _instance, _oracles, _selectors, _coins, _invert, "ETH/CRVUSD" ); } function _setCurveFeeChainedOracleForInstance(CurveFeeOracle _feeOracle, ITornadoInstance _instance) internal { ICurvePriceOracle[] memory _oracles = new ICurvePriceOracle[](2); _oracles[0] = ICurvePriceOracle(crvUSDUSDCStableswap2Pool); _oracles[1] = ICurvePriceOracle(tricryptoUSDCPool); bytes4[] memory _selectors = new bytes4[](2); _selectors[0] = CurveChainedOracles.PRICE_ORACLE_SELECTOR; _selectors[1] = CurveChainedOracles.PRICE_ORACLE_UINT256_SELECTOR; uint8[] memory _coins = new uint8[](2); _coins[1] = 1; bool[] memory _invert = new bool[](2); _invert[0] = false; _invert[1] = true; _feeOracle.modifyChainedOracleForInstance( _instance, _oracles, _selectors, _coins, _invert, "ETH/CRVUSD" ); } }