From 827e1d69912101e69a41031caf0c66d37f888136 Mon Sep 17 00:00:00 2001 From: AlienTornadosaurusHex <> Date: Sat, 10 Jun 2023 00:14:20 +0000 Subject: [PATCH] Start testing contracts Signed-off-by: AlienTornadosaurusHex <> --- .env.example | 1 + .gitignore | 4 + foundry.toml | 8 +- package.json | 10 +- src/base/TornadoProposal.sol | 27 +++++ src/common/TornadoAddresses.sol | 30 ++++++ src/common/interfaces/IGnosisSafe.sol | 85 +++++++++++++++ src/common/interfaces/IGovernance.sol | 56 ++++++++++ src/v2/CurveFeeOracle.sol | 7 +- test/OracleTests.sol | 145 ++++++++++++++++++++++++++ test/ProposalTests.sol | 75 +++++++++++++ test/TornadoProposalTest.sol | 139 ++++++++++++++++++++++++ yarn.lock | 7 +- 13 files changed, 579 insertions(+), 15 deletions(-) create mode 100644 .env.example create mode 100644 src/base/TornadoProposal.sol create mode 100644 src/common/TornadoAddresses.sol create mode 100644 src/common/interfaces/IGnosisSafe.sol create mode 100644 src/common/interfaces/IGovernance.sol create mode 100644 test/OracleTests.sol create mode 100644 test/ProposalTests.sol create mode 100644 test/TornadoProposalTest.sol diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..11c505b --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +MAINNET_RPC_URL= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3bd03ea..0aacacd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ node_modules/ .env .gitsigners .trash +yarn-error.log +yarn.lock +cache_hardhat +artifacts \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 0b46624..7580a3f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,10 +5,11 @@ out = 'out' libs = ["node_modules", "lib"] # Compiler +evm_version = 'shanghai' auto_detect_solc = true -via_ir = true -optimizer = true optimizer-runs = 1 +optimizer = true +via_ir = true # Network chain_id = 1 @@ -19,6 +20,9 @@ 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/', diff --git a/package.json b/package.json index 988c38b..583c583 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,7 @@ }, "scripts": { "postinstall": "chmod +x script/* && ./script/setup.sh", - "build": "chmod +x script/* && ./script/setup.sh", - "prettier": "prettier --write 'src/**/*.sol'", - "prettier:list": "prettier --list-different 'src/**/*.sol'", - "prettier:check": "prettier --check 'src/**/*.sol'", - "solhint": "solhint --config ./.solhint.json 'src/**/*.sol' --fix", - "solhint:check": "solhint --config ./.solhint.json 'src/**/*.sol'", - "lint": "npm run prettier && npm run solhint", - "lint:check": "npm run prettier:check && npm run solhint:check", - "test": "forge test" + "build": "chmod +x script/* && ./script/setup.sh" }, "devDependencies": { "prettier": "^2.5.1", diff --git a/src/base/TornadoProposal.sol b/src/base/TornadoProposal.sol new file mode 100644 index 0000000..2e2766f --- /dev/null +++ b/src/base/TornadoProposal.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { IGovernance } from "common/interfaces/IGovernance.sol"; +import { IGnosisSafe } from "common/interfaces/IGnosisSafe.sol"; + +import { TornadoAddresses } from "common/TornadoAddresses.sol"; + +abstract contract TornadoProposal is TornadoAddresses { + function executeProposal() public virtual; + + function getMultisig() internal pure returns (IGnosisSafe) { + return IGnosisSafe(getMultisigAddress()); + } + + function getTornToken() internal pure returns (IERC20) { + return IERC20(getTornTokenAddress()); + } + + function getGovernance() internal pure returns (IGovernance) { + return IGovernance(getGovernanceProxyAddress()); + } +} diff --git a/src/common/TornadoAddresses.sol b/src/common/TornadoAddresses.sol new file mode 100644 index 0000000..523341c --- /dev/null +++ b/src/common/TornadoAddresses.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +contract TornadoAddresses { + function getENSAddress() internal pure returns (address) { + return 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e; + } + + function getTornTokenAddress() internal pure returns (address) { + return 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; + } + + function getMultisigAddress() internal pure returns (address) { + return 0xb04E030140b30C27bcdfaafFFA98C57d80eDa7B4; + } + + function getGovernanceProxyAddress() internal pure returns (address) { + return 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce; + } + + function getStakingProxyAddress() internal pure returns (address) { + return 0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29; + } + + function getRegistryProxyAddress() internal pure returns (address) { + return 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2; + } +} diff --git a/src/common/interfaces/IGnosisSafe.sol b/src/common/interfaces/IGnosisSafe.sol new file mode 100644 index 0000000..7b08797 --- /dev/null +++ b/src/common/interfaces/IGnosisSafe.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +interface IGnosisSafe { + enum Operation { + Call, + DelegateCall + } + + function NAME() external view returns (string memory); + + function VERSION() external view returns (string memory); + + function nonce() external view returns (uint256); + + function domainSeparator() external view returns (bytes32); + + function signedMessages(bytes32) external view returns (uint256); + + function approvedHashes(address, bytes32) external view returns (uint256); + + function setup( + address[] calldata _owners, + uint256 _threshold, + address to, + bytes calldata data, + address fallbackHandler, + address paymentToken, + uint256 payment, + address payable paymentReceiver + ) external; + + function execTransaction( + address to, + uint256 value, + bytes calldata data, + Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address payable refundReceiver, + bytes calldata signatures + ) external returns (bool success); + + function requiredTxGas(address to, uint256 value, bytes calldata data, Operation operation) + external + returns (uint256); + + function approveHash(bytes32 hashToApprove) external; + + function signMessage(bytes calldata _data) external; + + function isValidSignature(bytes calldata _data, bytes calldata _signature) external returns (bytes4); + + function getMessageHash(bytes memory message) external view returns (bytes32); + + function encodeTransactionData( + address to, + uint256 value, + bytes memory data, + Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address refundReceiver, + uint256 _nonce + ) external view returns (bytes memory); + + function getTransactionHash( + address to, + uint256 value, + bytes memory data, + Operation operation, + uint256 safeTxGas, + uint256 baseGas, + uint256 gasPrice, + address gasToken, + address refundReceiver, + uint256 _nonce + ) external view returns (bytes32); +} diff --git a/src/common/interfaces/IGovernance.sol b/src/common/interfaces/IGovernance.sol new file mode 100644 index 0000000..0acd8e7 --- /dev/null +++ b/src/common/interfaces/IGovernance.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +enum ProposalState { + Pending, + Active, + Defeated, + Timelocked, + AwaitingExecution, + Executed, + Expired +} + +struct Proposal { + // Creator of the proposal + address proposer; + // target addresses for the call to be made + address target; + // The block at which voting begins + uint256 startTime; + // The block at which voting ends: votes must be cast prior to this block + uint256 endTime; + // Current number of votes in favor of this proposal + uint256 forVotes; + // Current number of votes in opposition to this proposal + uint256 againstVotes; + // Flag marking whether the proposal has been executed + bool executed; + // Flag marking whether the proposal voting time has been extended + // Voting time can be extended once, if the proposal outcome has changed during CLOSING_PERIOD + bool extended; +} + +interface IGovernance { + function initialized() external view returns (bool); + function initializing() external view returns (bool); + function EXECUTION_DELAY() external view returns (uint256); + function EXECUTION_EXPIRATION() external view returns (uint256); + function QUORUM_VOTES() external view returns (uint256); + function PROPOSAL_THRESHOLD() external view returns (uint256); + function VOTING_DELAY() external view returns (uint256); + function VOTING_PERIOD() external view returns (uint256); + function CLOSING_PERIOD() external view returns (uint256); + function VOTE_EXTEND_TIME() external view returns (uint256); + function torn() external view returns (address); + function proposals(uint256 index) external view returns (Proposal memory); + function lockedBalance(address account) external view returns (uint256); + function propose(address target, string memory description) external returns (uint256); + function castVote(uint256 proposalId, bool support) external; + function lock(address owner, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; + function lockWithApproval(uint256 amount) external; + function execute(uint256 proposalId) external payable; + function state(uint256 proposalId) external view returns (ProposalState); +} diff --git a/src/v2/CurveFeeOracle.sol b/src/v2/CurveFeeOracle.sol index 7b65834..9eff5d2 100644 --- a/src/v2/CurveFeeOracle.sol +++ b/src/v2/CurveFeeOracle.sol @@ -73,7 +73,7 @@ library CurveChainedOracles { bytes4 _selector = _selectors[o]; // Index of the coin in the curve pool for second UINT256 selector - uint8 _coin = _coins[0]; + uint8 _coin = _coins[o]; // Check whether the config actually works if (_selector == PRICE_ORACLE_SELECTOR) { @@ -106,7 +106,8 @@ library CurveChainedOracles { uint256 priceDivisor = 1e18; uint256 inverseNumerator = 1e36; - uint256 numOracles = _oracle.length; + // We know that each oracle addition encodes to exactly 26 bytes + uint256 numOracles = _oracle.length / 26; for (uint256 o = 0; o < numOracles; o++) { bytes32 chunk; @@ -175,7 +176,7 @@ contract CurveFeeOracle is IFeeOracle { /* @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 */ - mapping(ITornadoInstance => bytes) internal chainedPriceOracles; + mapping(ITornadoInstance => bytes) public chainedPriceOracles; /* @dev When setting, store the names as a historical record, key is keccak256(bytes) */ mapping(bytes32 => string) public chainedPriceOracleNames; diff --git a/test/OracleTests.sol b/test/OracleTests.sol new file mode 100644 index 0000000..4359c8d --- /dev/null +++ b/test/OracleTests.sol @@ -0,0 +1,145 @@ +// 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 { IGovernance, Proposal } from "common/interfaces/IGovernance.sol"; + +import { TornadoAddresses } from "common/TornadoAddresses.sol"; + +import { UniswapV3FeeOracle } from "src/v2/UniswapV3FeeOracle.sol"; + +import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol"; + +import { InstanceData } from "src/v2/InstanceRegistry.sol"; + +contract OracleTests is Test { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + address public constant crvUSDUSDCStableswap2Pool = 0x4DEcE678ceceb27446b35C672dC7d61F30bAD69E; + + address public constant tricryptoUSDCPool = 0x7F86Bf177Dd4F3494b841a37e810A34dD56c829B; + + ITornadoInstance public constant cu10_000 = ITornadoInstance(0x49f173CDAB99a2C3800F1255393DF9B7a17B82Bb); + + IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); + + UniswapV3FeeOracle v3FeeOracle; + + CurveFeeOracle feeOracle; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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); + + feeOracle = new CurveFeeOracle(address(this)); + feeOracle.setTornOracleIsUniswapV3(false); + feeOracle.setUniswapV3FeeOracle(v3FeeOracle); + } + + function test_curveFeeSingleTricrypto() public { + _setCurveFeeSimpleTricryptoOracleForInstance(feeOracle, cu10_000); // CRVUSD 10_000 + + console2.log( + "\nShould be 30 * (ETH/USD) ------------------------------------------------\n", + uint256( + feeOracle.getFee( + TORN, cu10_000, InstanceData(IERC20(cu10_000.token()), 0, true, true), 30, 10_000 + ) + ), + "\n------------------------------------------------------------------------\n" + ); + } + + function test_curveFeeChainedTORN() public { + feeOracle.setTornOracleIsUniswapV3(true); + + _setCurveFeeChainedOracleForInstance(feeOracle, cu10_000); // CRVUSD 10_000 + + console2.log( + "\nTORN Fee calculated ------------------------------------------------------\n", + uint256( + feeOracle.getFee( + TORN, cu10_000, InstanceData(IERC20(cu10_000.token()), 0, true, true), 30, 10_000 + ) + ), + "\n------------------------------------------------------------------------\n" + ); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 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" + ); + } +} diff --git a/test/ProposalTests.sol b/test/ProposalTests.sol new file mode 100644 index 0000000..55b07be --- /dev/null +++ b/test/ProposalTests.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +// OZ Imports + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +// Local imports + +import { InfrastructureUpgradeProposal } from "src/proposals/InfrastructureUpgradeProposal.sol"; + +import { UniswapV3FeeOracle } from "src/v2/UniswapV3FeeOracle.sol"; + +import { CurveFeeOracle, ICurvePriceOracle, CurveChainedOracles } from "src/v2/CurveFeeOracle.sol"; + +import { InstanceRegistry } from "src/v2/InstanceRegistry.sol"; + +import { FeeOracleManager } from "src/v2/FeeOracleManager.sol"; + +import { TornadoRouter } from "src/v2/TornadoRouter.sol"; + +// Test imports + +import { TornadoProposalTest } from "./TornadoProposalTest.sol"; + +contract ProposalTests is TornadoProposalTest { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); + + InstanceRegistry implInstanceRegisty; + + UniswapV3FeeOracle v3FeeOracle; + + FeeOracleManager feeOracleManager; + + TornadoRouter router; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TESTING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function setUp() public override { + super.setUp(); + + implInstanceRegisty = new InstanceRegistry(address(governance)); + + v3FeeOracle = new UniswapV3FeeOracle(address(governance)); + + feeOracleManager = new FeeOracleManager(address(TORN), address(governance)); + + router = new TornadoRouter(address(governance)); + } + + function test_infrastructureUpgradeProposalBasic() public { + // Create proposal + address proposal = address( + new InfrastructureUpgradeProposal( + address(v3FeeOracle), + address(feeOracleManager), + address(implInstanceRegisty), + address(router) + ) + ); + + // Propose + uint256 id = easyPropose(proposal); + + // Wait + waitUntilExecutable(id); + + // Exec + governance.execute(id); + } +} diff --git a/test/TornadoProposalTest.sol b/test/TornadoProposalTest.sol new file mode 100644 index 0000000..5e29b85 --- /dev/null +++ b/test/TornadoProposalTest.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +import { IGovernance, Proposal } from "common/interfaces/IGovernance.sol"; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import { Test } from "forge-std/Test.sol"; + +import { TornadoAddresses } from "common/TornadoAddresses.sol"; + +contract TornadoProposalTest is Test, TornadoAddresses { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PERMIT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + bytes32 public constant PERMIT_TYPEHASH = + keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); + + bytes32 public constant EIP712_DOMAIN = keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes("TornadoCash")), + keccak256(bytes("1")), + 1, + VERIFIER_ADDRESS + ) + ); + + uint16 public constant PERMIT_FUNC_SELECTOR = uint16(0x1901); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TEST DUMMIES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + address public constant TEST_REAL_ADDRESS_WITH_BALANCE = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58; + + address public constant TEST_RELAYER_ADDRESS = 0x30F96AEF199B399B722F8819c9b0723016CEAe6C; // moon-relayer.eth + // (just for testing) + + uint256 public constant TEST_PRIVATE_KEY_ONE = + 0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0; + uint256 public constant TEST_PRIVATE_KEY_TWO = + 0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064; + + address public constant TEST_ADDRESS_ONE = 0x118251976c65AFAf291f5255450ddb5b6A4d8B88; + address public constant TEST_ADDRESS_TWO = 0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ADDRESSES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + address public constant VERIFIER_ADDRESS = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GOVERNANCE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + string public constant PROPOSAL_DESCRIPTION = "{title:'Some proposal',description:''}"; + + uint256 public EXECUTION_DELAY; + uint256 public EXECUTION_EXPIRATION; + uint256 public QUORUM_VOTES; + uint256 public PROPOSAL_THRESHOLD; + uint256 public VOTING_DELAY; + uint256 public VOTING_PERIOD; + uint256 public CLOSING_PERIOD; + uint256 public VOTE_EXTEND_TIME; + + IGovernance public immutable governance = IGovernance(getGovernanceProxyAddress()); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TEST UTILS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function setUp() public virtual { + vm.createSelectFork(vm.envString("MAINNET_RPC_URL")); + _fetchConfiguration(); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function waitUntilExecutable(uint256 proposalId) internal { + uint256 proposalExecutableTime = getProposalExecutableTime(proposalId); + require(block.timestamp < proposalExecutableTime, "Too late to execute proposal"); + vm.warp(proposalExecutableTime); + } + + function easyPropose(address proposalAddress) public returns (uint256) { + retrieveAndLockBalance(TEST_PRIVATE_KEY_ONE, TEST_ADDRESS_ONE, QUORUM_VOTES); + retrieveAndLockBalance(TEST_PRIVATE_KEY_TWO, TEST_ADDRESS_TWO, 1 ether); + + /* ----------PROPOSER------------ */ + vm.startPrank(TEST_ADDRESS_ONE); + + uint256 proposalId = governance.propose(proposalAddress, PROPOSAL_DESCRIPTION); + + // TIME-TRAVEL + vm.warp(block.timestamp + 6 hours); + + governance.castVote(proposalId, true); + + vm.stopPrank(); + /* ------------------------------ */ + + /* -------------VOTER-------------*/ + vm.startPrank(TEST_ADDRESS_TWO); + governance.castVote(proposalId, true); + vm.stopPrank(); + /* ------------------------------ */ + + return proposalId; + } + + function retrieveAndLockBalance(uint256, address voter, uint256 amount) internal { + /* ----------GOVERNANCE------- */ + vm.startPrank(getGovernanceProxyAddress()); + IERC20(getTornTokenAddress()).transfer(voter, amount); + vm.stopPrank(); + /* ----------------------------*/ + + /* ----------VOTER------------ */ + vm.startPrank(voter); + IERC20(getTornTokenAddress()).approve(address(governance), amount); + governance.lockWithApproval(amount); + vm.stopPrank(); + /* ----------------------------*/ + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + function getProposalExecutableTime(uint256 proposalId) internal view returns (uint256) { + Proposal memory proposal = IGovernance(getGovernanceProxyAddress()).proposals(proposalId); + return proposal.endTime + EXECUTION_DELAY + 1 hours; + } + + function _fetchConfiguration() internal { + EXECUTION_DELAY = governance.EXECUTION_DELAY(); + EXECUTION_EXPIRATION = governance.EXECUTION_EXPIRATION(); + QUORUM_VOTES = governance.QUORUM_VOTES(); + PROPOSAL_THRESHOLD = governance.PROPOSAL_THRESHOLD(); + VOTING_DELAY = governance.VOTING_DELAY(); + VOTING_PERIOD = governance.VOTING_PERIOD(); + CLOSING_PERIOD = governance.CLOSING_PERIOD(); + VOTE_EXTEND_TIME = governance.VOTE_EXTEND_TIME(); + } +} diff --git a/yarn.lock b/yarn.lock index f60b0ff..d662258 100644 --- a/yarn.lock +++ b/yarn.lock @@ -360,7 +360,12 @@ prettier-plugin-solidity@^1.0.0-beta.19: semver "^7.3.8" solidity-comments-extractor "^0.0.7" -prettier@^2.5.1, prettier@^2.8.3: +prettier@^2.5.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +prettier@^2.8.3: version "2.8.4" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==