Start testing contracts
Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
parent
a8e09194ec
commit
827e1d6991
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
MAINNET_RPC_URL=
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,3 +4,7 @@ node_modules/
|
|||||||
.env
|
.env
|
||||||
.gitsigners
|
.gitsigners
|
||||||
.trash
|
.trash
|
||||||
|
yarn-error.log
|
||||||
|
yarn.lock
|
||||||
|
cache_hardhat
|
||||||
|
artifacts
|
@ -5,10 +5,11 @@ out = 'out'
|
|||||||
libs = ["node_modules", "lib"]
|
libs = ["node_modules", "lib"]
|
||||||
|
|
||||||
# Compiler
|
# Compiler
|
||||||
|
evm_version = 'shanghai'
|
||||||
auto_detect_solc = true
|
auto_detect_solc = true
|
||||||
via_ir = true
|
|
||||||
optimizer = true
|
|
||||||
optimizer-runs = 1
|
optimizer-runs = 1
|
||||||
|
optimizer = true
|
||||||
|
via_ir = true
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
chain_id = 1
|
chain_id = 1
|
||||||
@ -19,6 +20,9 @@ verbosity = 2
|
|||||||
|
|
||||||
# Remappings
|
# Remappings
|
||||||
remappings = [
|
remappings = [
|
||||||
|
'src/=src/',
|
||||||
|
'base/=src/base/',
|
||||||
|
'common/=src/common/',
|
||||||
'ds-test/=lib/ds-test/src/',
|
'ds-test/=lib/ds-test/src/',
|
||||||
'solmate/=lib/solmate/src/',
|
'solmate/=lib/solmate/src/',
|
||||||
'forge-std/=lib/forge-std/src/',
|
'forge-std/=lib/forge-std/src/',
|
||||||
|
10
package.json
10
package.json
@ -9,15 +9,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "chmod +x script/* && ./script/setup.sh",
|
"postinstall": "chmod +x script/* && ./script/setup.sh",
|
||||||
"build": "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"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
|
27
src/base/TornadoProposal.sol
Normal file
27
src/base/TornadoProposal.sol
Normal file
@ -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());
|
||||||
|
}
|
||||||
|
}
|
30
src/common/TornadoAddresses.sol
Normal file
30
src/common/TornadoAddresses.sol
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
85
src/common/interfaces/IGnosisSafe.sol
Normal file
85
src/common/interfaces/IGnosisSafe.sol
Normal file
@ -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);
|
||||||
|
}
|
56
src/common/interfaces/IGovernance.sol
Normal file
56
src/common/interfaces/IGovernance.sol
Normal file
@ -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);
|
||||||
|
}
|
@ -73,7 +73,7 @@ library CurveChainedOracles {
|
|||||||
bytes4 _selector = _selectors[o];
|
bytes4 _selector = _selectors[o];
|
||||||
|
|
||||||
// Index of the coin in the curve pool for second UINT256 selector
|
// 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
|
// Check whether the config actually works
|
||||||
if (_selector == PRICE_ORACLE_SELECTOR) {
|
if (_selector == PRICE_ORACLE_SELECTOR) {
|
||||||
@ -106,7 +106,8 @@ library CurveChainedOracles {
|
|||||||
uint256 priceDivisor = 1e18;
|
uint256 priceDivisor = 1e18;
|
||||||
uint256 inverseNumerator = 1e36;
|
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++) {
|
for (uint256 o = 0; o < numOracles; o++) {
|
||||||
bytes32 chunk;
|
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
|
/* @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
|
this data "chainedPriceOracles" because it encodes all data necessary to execute the chain and calc the
|
||||||
price */
|
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) */
|
/* @dev When setting, store the names as a historical record, key is keccak256(bytes) */
|
||||||
mapping(bytes32 => string) public chainedPriceOracleNames;
|
mapping(bytes32 => string) public chainedPriceOracleNames;
|
||||||
|
145
test/OracleTests.sol
Normal file
145
test/OracleTests.sol
Normal file
@ -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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
75
test/ProposalTests.sol
Normal file
75
test/ProposalTests.sol
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
139
test/TornadoProposalTest.sol
Normal file
139
test/TornadoProposalTest.sol
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -360,7 +360,12 @@ prettier-plugin-solidity@^1.0.0-beta.19:
|
|||||||
semver "^7.3.8"
|
semver "^7.3.8"
|
||||||
solidity-comments-extractor "^0.0.7"
|
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"
|
version "2.8.4"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3"
|
||||||
integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==
|
integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==
|
||||||
|
Loading…
Reference in New Issue
Block a user