infrastructure-upgrade/test/ProposalTests.sol
AlienTornadosaurusHex 806aa103e8 Modularize external function calls on RelayerRegistry
Signed-off-by: AlienTornadosaurusHex <>
2023-06-22 18:01:25 +00:00

1008 lines
38 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// OZ Imports
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// STD Imports
import { console2 } from "forge-std/console2.sol";
// Tornado imports
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
// Local imports
import { IUniswapV2Pair } from "src/v2/interfaces/IUniswapV2Pair.sol";
import { UniswapFeeOracle } from "src/v2/UniswapFeeOracle.sol";
import { TornadoRouter } from "src/v2/TornadoRouter.sol";
import { RelayerRegistry } from "src/v2/RelayerRegistry.sol";
import { TornadoStakingRewards } from "src/v2/TornadoStakingRewards.sol";
import { InstanceRegistry, InstanceState } from "src/v2/InstanceRegistry.sol";
import { FeeOracleManager, FeeDataForOracle, InstanceWithFee } from "src/v2/FeeOracleManager.sol";
import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol";
import { InfrastructureUpgradeProposal } from "src/proposals/InfrastructureUpgradeProposal.sol";
import { CRVUSDInstancesProposal } from "src/proposals/CRVUSDInstancesProposal.sol";
// Test imports
import { TornadoProposalTest, ProposalState } from "./TornadoProposalTest.sol";
interface IWethDepositable {
function deposit() external payable;
}
interface IENS {
function setOwner(bytes32 node, address owner) external;
function owner(bytes32 node) external view returns (address);
}
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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Tokens
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
IERC20 public constant DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
IERC20 public constant CDAI = IERC20(0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643);
IERC20 public constant USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
IERC20 internal constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 internal constant WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
IERC20 internal constant CRVUSD = IERC20(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
// Constant vars
IENS public constant ENS = IENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);
FeeOracleManager public constant feeOracleManager =
FeeOracleManager(0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7);
InstanceRegistry public constant instanceRegistry =
InstanceRegistry(0xB20c66C4DE72433F3cE747b58B86830c459CA911);
RelayerRegistry public constant relayerRegistry =
RelayerRegistry(0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2);
IUniswapV2Pair public constant uniTornPool = IUniswapV2Pair(0x0C722a487876989Af8a05FFfB6e32e45cc23FB3A);
address payable public constant stakingProxyAddress = 0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29;
address payable public constant relayerRegistryProxyAddress = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2;
// Implementations
InstanceRegistry implInstanceRegistry;
FeeOracleManager implFeeOracleManager;
RelayerRegistry implRegistry;
TornadoStakingRewards implStaking;
// Infra contracts mutable
CurveFeeOracle curveFeeOracle;
UniswapFeeOracle uniswapFeeOracle;
TornadoRouter router;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function setUp() public override {
TornadoProposalTest.setUp();
vm.rollFork(17_522_762);
curveFeeOracle = new CurveFeeOracle(address(governance));
uniswapFeeOracle = new UniswapFeeOracle(address(governance), address(feeOracleManager));
vm.prank(address(governance));
curveFeeOracle.setUniswapFeeOracle(uniswapFeeOracle);
router = new TornadoRouter(address(governance));
implRegistry = new RelayerRegistry(address(governance), address(TORN));
implInstanceRegistry = new InstanceRegistry(address(governance));
implFeeOracleManager = new FeeOracleManager(address(TORN), address(governance));
implStaking = new TornadoStakingRewards(address(governance), address(TORN), address(router));
_advanceTORNETHMarket();
}
function test_crvusdInstancesBasic() public {
// Do the proposal first
test_crvusdInstancesProposalBasic();
// We have to set to address this again
_feeManagerShouldRevertAndSetFeeUpdater();
// Advance
_advanceTORNETHMarket();
// 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;
// We will need to prank an update on the uniswapFeeOracle
// this should otherwise happen automatically though other tokens being withdrawn
vm.startPrank(address(feeOracleManager));
for (uint256 i = 0; i < _toUpdate.length; i++) {
uniswapFeeOracle.update(TORN, feeOracleManager.populateInstanceWithFeeData(_toUpdate[i]));
}
vm.stopPrank();
// Now update all fees
uint160[] memory fees = 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~~~~~~~~~~~~~~~~~~ DATA ~~~~~~~~~~~~~~~~~~\n");
InstanceState memory data = instanceRegistry.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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");
// No access
curveFeeOracle.update(TORN, feeOracleManager.populateInstanceWithFeeData(cu10_000));
// No access view
curveFeeOracle.getFee(TORN, feeOracleManager.populateInstanceWithFeeData(cu10_000));
// 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"
);
// Now since we will test updating all fees... the oracle will get updated
// but first, warp forward
vm.warp(now + 2 days);
// Should be able to update all fees
fees = feeOracleManager.updateAllFees(true);
feeOracleManager.version();
delimit();
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 💱 I would like to note that I have verified via calculation that post-price move, the fees work.\n Basically: (1000000*30/10000) == fee * 9 / 10^18, should be constant because block is fix. \n"
);
// Just for some gas values for this call
instanceRegistry.instanceData(cu100);
instanceRegistry.instanceData(cu1_000);
instanceRegistry.version();
router.version();
// Let's just check all instances one by one
InstanceState[] memory states = instanceRegistry.getAllInstanceStates();
for (uint256 i = 0; i < states.length; i++) {
if (i <= 3) {
require(!states[i].isERC20);
require(states[i].isEnabled);
} else if (i <= 7) {
require(states[i].token == DAI);
require(states[i].isERC20);
require(states[i].isEnabled);
} else if (i <= 11) {
require(states[i].token == CDAI);
require(states[i].isERC20);
require(states[i].isEnabled);
} else if (i <= 13) {
require(states[i].token == USDT);
require(states[i].isERC20);
require(states[i].isEnabled);
} else if (i <= 16) {
require(states[i].token == WBTC);
require(states[i].isERC20);
require(states[i].isEnabled);
} else if (i <= 21) {
require(states[i].token == CRVUSD);
require(states[i].isERC20);
require(states[i].isEnabled);
}
}
// Now do a specific interval
states = instanceRegistry.getInstanceState(8, 11);
for (uint256 i = 0; i < 4; i++) {
require(states[i].token == CDAI);
require(states[i].isERC20);
require(states[i].isEnabled);
}
// Other for coverage
require(instanceRegistry.isRegisteredInstance(cu10_000));
vm.prank(address(governance));
instanceRegistry.setTornadoRouter(address(0));
require(address(instanceRegistry.router()) == address(0));
}
function test_infrastructureBasic() public {
// Do the proposal first
test_infrastructureUpgradeProposalBasic();
// No re-initializing
_contractsNotReinitializable();
// The instance registry must resolve ENS names
_instanceRegistryShouldResolveENSNames();
// Setup and test fee oracle manager update revert
_feeManagerShouldRevertAndSetFeeUpdater();
// Registry should be able to resolve wrapped names
_registryShouldResolveWrappedENSNames();
// Proxy admin slot
_relayerRegistyProxyAdminSlotShouldBeDead();
// Some leftover tests not included in others
_leftoverRelayerRegistryTests();
// Test router deposits and withdrawals
_shouldBeAbleToDepositAndWithdrawFromRouter();
// Router trivial for coverage
_trivialRouterCoverage();
// Instance reg trivial
_trivialInstanceRegistryCoverage();
// 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");
// 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.updateFee(eth10, true)));
console2.log("eth100: ", uint256(feeOracleManager.updateFee(eth100, true)));
console2.log("dai10000: ", uint256(feeOracleManager.updateFee(dai10000, true)));
console2.log("usdt100: ", uint256(feeOracleManager.updateFee(usdt100, true)));
console2.log("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
delimit();
console2.log("\n~~~~~~~~~~~~~~~~~~ DATA ~~~~~~~~~~~~~~~~~~\n");
InstanceState memory data = instanceRegistry.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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.getInstanceState(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 these fns
instanceRegistry.instanceData(eth10);
instanceRegistry.instanceData(eth100);
instanceRegistry.instanceData(dai10000);
uniswapFeeOracle.getTWAPData();
uniswapFeeOracle.getLastUpdatedTime();
vm.expectRevert();
uniswapFeeOracle.update(
TORN,
InstanceWithFee({
logic: eth100,
state: InstanceState(WETH, 0, false, false),
fee: FeeDataForOracle(0, 0, 0, 2 days, 0)
})
);
vm.expectRevert();
uniswapFeeOracle.setFeeOracleManagerAddress(address(0));
vm.expectRevert();
uniswapFeeOracle.setMinObservationCardinality(0);
vm.expectRevert();
uniswapFeeOracle.setPoolFeeForToken(WETH, 0);
// Expect no update if in inside the time limit interval
uint256 startTimestamp = block.timestamp;
uint256 interval = feeOracleManager.getFeeUpdateIntervalForInstance(eth100);
vm.warp(startTimestamp + interval + 5 days);
feeOracleManager.updateFee(eth100, true);
uint256 eth100Fee = feeOracleManager.getLastFeeForInstance(eth100);
vm.prank(address(governance));
feeOracleManager.setFeePercentForInstance(eth100, 50);
// both shouldnt update
feeOracleManager.updateFee(eth100, true);
uint32 lastUpdatedTimeHere = feeOracleManager.getLastUpdatedTimeForInstance(eth100);
vm.prank(address(feeOracleManager));
uniswapFeeOracle.update(
TORN,
InstanceWithFee({
logic: eth100,
state: InstanceState(WETH, 0, false, false),
fee: FeeDataForOracle(0, 0, 0, 2 days, lastUpdatedTimeHere)
})
);
require(eth100Fee == feeOracleManager.getLastFeeForInstance(eth100), "wrong fee 1");
vm.warp(startTimestamp + 2 * interval + 10 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 + 3 * interval + 15 days);
feeOracleManager.updateFee(eth100, true);
require(eth100Fee == feeOracleManager.getLastFeeForInstance(eth100), "wrong fee 3");
require(eth100Fee == feeOracleManager.getUpdatedFeeForInstance(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);
// 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));
// For coverage
vm.prank(address(governance));
uniswapFeeOracle.setFeeOracleManagerAddress(address(feeOracleManager));
vm.prank(address(governance));
feeOracleManager.setInstanceRegistry(address(instanceRegistry));
}
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(uniswapFeeOracle),
address(implRegistry),
address(implStaking),
address(implFeeOracleManager),
address(implInstanceRegistry),
address(router)
)
);
// Propose
uint256 id = easyPropose(proposal);
// Wait
waitUntilExecutable(id);
// Exec
governance.execute(id);
}
function _contractsNotReinitializable() internal {
ITornadoInstance[] memory _toUpdate = new ITornadoInstance[](3);
_toUpdate[0] = eth100;
_toUpdate[1] = dai10000;
_toUpdate[2] = usdt100;
// Must not be able to reinit router, contract addresses are not important
vm.expectRevert();
vm.prank(address(governance));
router.initialize(
address(instanceRegistry), address(instanceRegistry), address(feeOracleManager), address(0)
);
vm.expectRevert();
router.initialize(
address(instanceRegistry), address(instanceRegistry), address(feeOracleManager), address(0)
);
// 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),
address(router),
6 hours,
_toUpdate,
feeArrayForTesting_1()
);
vm.expectRevert();
feeOracleManager.initialize(
address(uniswapFeeOracle),
address(instanceRegistry),
address(router),
6 hours,
_toUpdate,
feeArrayForTesting_1()
);
}
function _leftoverRelayerRegistryTests() internal {
relayerRegistry.version();
require(relayerRegistry.minimumTornStake() == 2000 ether, "reg min st");
MinimumStakeOracle oracle = new MinimumStakeOracle(relayerRegistry);
vm.prank(address(governance));
relayerRegistry.setMinimumTornStake(1000 ether);
require(relayerRegistry.minimumTornStake() == 1000 ether, "reg min st2");
vm.expectRevert();
relayerRegistry.setMinimumStakeOracle(address(oracle));
vm.prank(address(governance));
relayerRegistry.setMinimumStakeOracle(address(oracle));
oracle.setAmount();
require(relayerRegistry.minimumTornStake() == 2000 ether, "reg min st3");
}
function _trivialInstanceRegistryCoverage() internal view {
instanceRegistry.version();
require(instanceRegistry.isRegisteredInstance(dai100000), "dai100k not reg");
}
function _trivialRouterCoverage() internal {
// This is just to fill up router coverage so I can have an overview
// I am aware of the pros and cons of coverage
router.version();
bytes[] memory fakenotes = new bytes[](1);
fakenotes[0] = bytes("anydata");
router.backupNotes(fakenotes);
vm.deal(address(router), 1 ether);
vm.prank(address(governance));
router.rescueTokens(IERC20(0), payable(address(governance)), 1 ether);
vm.prank(address(DAI));
DAI.transfer(address(router), 1 ether);
vm.prank(address(governance));
router.rescueTokens(DAI, payable(address(governance)), 1 ether);
vm.expectRevert();
router.setFeeOracleManager(address(0));
vm.expectRevert();
router.setInstanceRegistry(address(0));
vm.expectRevert();
router.setStakingRewards(address(0));
vm.prank(address(governance));
router.setFeeOracleManager(address(feeOracleManager));
vm.prank(address(governance));
router.setInstanceRegistry(address(instanceRegistry));
vm.prank(address(governance));
router.setStakingRewards(stakingProxyAddress);
}
function _instanceRegistryShouldResolveENSNames() internal view {
require(
address(instanceRegistry.getInstanceByENSName("governance.contract.tornadocash.eth"))
== address(governance),
"ENSResolving doesn't work"
);
}
function _feeManagerShouldRevertAndSetFeeUpdater() internal {
vm.expectRevert();
feeOracleManager.updateFee(eth100, false);
vm.prank(address(governance));
feeOracleManager.setFeeUpdater(address(this));
}
function _registryShouldResolveWrappedENSNames() internal view {
require(
relayerRegistry.ownerByName("wrappedtestname.eth") == 0x895CB4C75e9be8ABA117bF4E044416C855018ea0,
"ens name wrapper resolution"
);
}
function _relayerRegistyProxyAdminSlotShouldBeDead() internal view {
require(
address(
bytes20(
vm.load(
address(relayerRegistry),
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
)
)
) == address(0),
"admin slot"
);
}
function _shouldBeAbleToDepositAndWithdrawFromRouter() internal {
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~ DEPOSITING ~~~~~~~~~~~~~~~~~~~~~~~ */
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);
// This and other precomputed data you will see here
// I've had to generate with the SDK for testing
bytes32 commitment = 0x023fe5da10c6b6f3748e69c9b4bb83ee3c4de6fb7a9e174c69b34580fccebd11;
bytes memory empty;
vm.prank(address(depositor));
router.deposit(dai100000, commitment, empty);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~ WITHDRAWING ~~~~~~~~~~~~~~~~~~~~~~~ */
// This and other precomputed data you will see here
// I've had to generate with the SDK for testing
address withdrawer = 0xc02D1C9620481387a211Bdcfe168f0653164AAf6;
address[] memory workers = new address[](3);
workers[0] = address(bytes20(keccak256(abi.encode("worker1"))));
workers[1] = address(bytes20(keccak256(abi.encode("worker2"))));
workers[2] = address(bytes20(keccak256(abi.encode("worker3"))));
vm.deal(withdrawer, 1 ether);
/* ~~~~~~~~~~~~~~~ HAVE TO REGISTER RELAYER FIRST ~~~~~~~~~~~~~~~ */
// vitalik.eth
vm.prank(ENS.owner(0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835));
ENS.setOwner(0xee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835, withdrawer);
_registerRelayer("vitalik.eth", withdrawer, workers);
/* ~~~~~~~~~~~~~~~ DO WITHDRAW ~~~~~~~~~~~~~~~ */
vm.prank(address(governance));
feeOracleManager.setFeeUpdater(address(router));
vm.prank(address(workers[1]));
router.withdraw(
dai100000, // Instance
// Proof
hex"13d5278f72487a02217b02fa8533964103d0033b601498303a86d2896f2f44ec198716465105a255bed58cf8ddea2884d740cc71a0b5170777829c1ab3ee8ed21e0588b3a00d2109f92f0daed5a1ef6207f699650252ef8111cd2d8f8688f01603410dcfd4df3627bed16a600a3e15e3fa8443e6e931cb5405c4efbf0dcaff4014b6006964e9edff0039c3cef1dbaa043c8e51e947a3ab756d3a5c293c4707370ef218c427678700cbf61ce8cc2572363b4f6b0e7c50750e875d3e61485b4eaf14dd78c7d7e594f74e8e809cfebd6b3cb6c2e42573f5be96c726bc8eff3599002b1d397f9d208367545a3540736c5e95757b4365b1a3992ea3f75fee7257c70a",
bytes32(0x29432414abc7ac658bbf2aa080ebac45774bd802b2ce727f0ae45840f587df00), // Merkle root
bytes32(0x0ee7243a3a650bd3aa48f445597846275064207d0a3ff749e11f1ab8edfc630e), // Nullifier
payable(0x0520fF8CF7824FCb4308300469D90688e267DC25), // Recipient address
payable(0xc02D1C9620481387a211Bdcfe168f0653164AAf6), // Relayer address, should work
uint256(0x0000000000000000000000000000000000000000000000d8f2a11699e9c607bc), // Fee
0 // Refund
);
vm.prank(address(governance));
feeOracleManager.setFeeUpdater(address(this));
/* ~~~~~~~~~~~~~~~~ POST ASSERTIONS ~~~~~~~~~~~~~~~~~~~~ */
uint256 fee = uint256(0xd8f2a11699e9c607bc);
require(DAI.balanceOf(0x0520fF8CF7824FCb4308300469D90688e267DC25) == 100_000 ether - fee, "recipient");
require(DAI.balanceOf(0xc02D1C9620481387a211Bdcfe168f0653164AAf6) == fee, "relayer fee");
require(relayerRegistry.getRelayerBalanceByName("vitalik.eth") < 2000 ether, "vitalik bal");
/* ~~~~~~~~~~~~~~~~ BONUS: UNREGISTER WORKERS ~~~~~~~~~~~~~~~~~~~~ */
vm.prank(withdrawer);
relayerRegistry.unregisterWorker(workers[1]);
vm.prank(workers[2]);
vm.expectRevert();
relayerRegistry.unregisterWorker(workers[0]);
vm.prank(workers[0]);
relayerRegistry.unregisterWorker(workers[0]);
vm.prank(workers[2]);
relayerRegistry.unregisterWorker(workers[2]);
/* ~~~~~~~~~~~~~~~~ BONUS: ADD STAKE ~~~~~~~~~~~~~~~~~~~~ */
address donator = address(bytes20(keccak256(abi.encode("donator to vitalik"))));
giveTorn(donator, 1000 ether);
vm.prank(donator);
TORN.approve(address(relayerRegistry), 1000 ether);
vm.prank(donator);
relayerRegistry.stakeToRelayer(withdrawer, 1000 ether);
require(2000 ether < relayerRegistry.getRelayerBalanceByName("vitalik.eth"), "vitalik bal");
}
function _registerRelayer(string memory _name, address _relayer, address[] memory _workers) internal {
giveTorn(_relayer, 2000 ether);
vm.startPrank(_relayer);
TORN.approve(address(relayerRegistry), 2000 ether);
relayerRegistry.register(_name, 2000 ether, _workers);
vm.stopPrank();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function _advanceTORNETHMarket() internal {
console2.log("\n ♻️ Trader 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);
uint256 expected = 5_600_000_000_000_000_000_000;
bool worked;
while (!worked) {
try uniTornPool.swap(expected, 0, trader, new bytes(0)) {
worked = true;
} catch {
expected /= 2;
}
}
require(lastPriceAvg0 != uniTornPool.price0CumulativeLast(), "twap moving 0 fail");
require(lastPriceAvg1 != uniTornPool.price1CumulativeLast(), "twap moving 1 fail");
console2.log(
"\n 💰 TORN in ETH after market op: 💰 ",
WETH.balanceOf(address(uniTornPool)) * 1e18 / TORN.balanceOf(address(uniTornPool)),
"\n"
);
}
function delimit() internal view {
console2.log();
}
function strcomp(string memory left, string memory right) internal pure returns (bool) {
return keccak256(abi.encode(left)) == keccak256(abi.encode(right));
}
function feeArrayForTesting_1() internal pure returns (uint256[] memory fees) {
fees = new uint256[](3);
fees[0] = 30;
fees[1] = 30;
fees[2] = 30;
}
}
contract MinimumStakeOracle {
RelayerRegistry public registry;
constructor(RelayerRegistry _registry) public {
registry = _registry;
}
function setAmount() public {
registry.setMinimumTornStake(2000 ether);
}
}