Compare commits
2 Commits
bca8932686
...
04ba45b2c1
Author | SHA1 | Date | |
---|---|---|---|
04ba45b2c1 | |||
f0f5424edf |
@ -9,6 +9,7 @@
|
||||
|
||||
### Requirements
|
||||
|
||||
- Rust ([Any system](https://doc.rust-lang.org/cargo/getting-started/installation.html))
|
||||
- Foundryup ([Windows](https://github.com/altugbakan/foundryup-windows), [Linux](https://book.getfoundry.sh/getting-started/installation))
|
||||
- Node 14 or higher ([Windows](https://github.com/coreybutler/nvm-windows), [Linux](https://github.com/nvm-sh/nvm))
|
||||
|
||||
|
4963
package-lock.json
generated
Normal file
4963
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,8 +5,11 @@
|
||||
"author": "Theo",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17336117 --no-match-contract TestRelayerBalance",
|
||||
"testWithGas": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17336117 --no-match-contract TestRelayerBalance --gas-report",
|
||||
"test": "npm run test:all",
|
||||
"test:all": "npm run test:beforeProposed && npm run test:afterProposed",
|
||||
"test:beforeProposed": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17336117 --no-match-contract TestRelayerBalance",
|
||||
"test:afterProposed": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17387853 --no-match-contract TestRelayerBalance",
|
||||
"test:gas": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17336117 --no-match-contract TestRelayerBalance --gas-report",
|
||||
"relayerBalancesSum": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17304722 --match-contract TestRelayerBalance --gas-report"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -7,6 +7,11 @@ contract Mock {
|
||||
address public constant TEST_REAL_ADDRESS_WITH_BALANCE = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58;
|
||||
address public constant TEST_RELAYER_ADDRESS = 0x30F96AEF199B399B722F8819c9b0723016CEAe6C; // moon-relayer.eth (just for testing)
|
||||
|
||||
// Address and private key to test staking, Governance lock and rewards accruals
|
||||
address public constant TEST_STAKER_ADDRESS = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
|
||||
uint256 public constant TEST_STAKER_PRIVATE_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
|
||||
|
||||
// Two test accounts to create proposal and vote for it
|
||||
uint256 public constant TEST_PRIVATE_KEY_ONE =
|
||||
0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0;
|
||||
uint256 public constant TEST_PRIVATE_KEY_TWO =
|
||||
@ -14,6 +19,8 @@ contract Mock {
|
||||
address public constant TEST_ADDRESS_ONE = 0x118251976c65AFAf291f5255450ddb5b6A4d8B88;
|
||||
address public constant TEST_ADDRESS_TWO = 0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20;
|
||||
|
||||
|
||||
uint256 public constant STAKING_FIX_PROPOSAL_ID = 22;
|
||||
uint256 public constant ATTACKER_PROPOSAL_ID = 21; // Last attacker proposal (to restore Governance Vault balance) id
|
||||
|
||||
uint256 public constant PROPOSAL_VOTING_DURATION = 5 days;
|
||||
|
@ -8,6 +8,7 @@ import { TornadoStakingRewards } from "@root/v4-patch/TornadoStakingRewards.sol"
|
||||
import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol";
|
||||
import { AdminUpgradeableProxy } from "@root/v4-patch/AdminUpgradeableProxy.sol";
|
||||
import { ProposalUtils } from "./ProposalUtils.sol";
|
||||
import { Proposal, IGovernance } from "@interfaces/IGovernance.sol";
|
||||
|
||||
import { Test } from "@forge-std/Test.sol";
|
||||
|
||||
@ -18,12 +19,21 @@ contract MockProposal is Test, ProposalUtils {
|
||||
}
|
||||
|
||||
modifier executeAttackerProposalBefore() {
|
||||
if(!getProposal(ATTACKER_PROPOSAL_ID).executed){
|
||||
waitUntilExecutable(ATTACKER_PROPOSAL_ID);
|
||||
governance.execute(ATTACKER_PROPOSAL_ID);
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
function createAndExecuteProposal() public {
|
||||
// If current proposal already proposed, just wait until executable and execute it
|
||||
if(hasProposal(STAKING_FIX_PROPOSAL_ID)) {
|
||||
waitUntilExecutable(STAKING_FIX_PROPOSAL_ID);
|
||||
governance.execute(STAKING_FIX_PROPOSAL_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
TornadoStakingRewards governanceStakingImplementation =
|
||||
new TornadoStakingRewards(_governanceAddress, _tokenAddress, _relayerRegistryAddress);
|
||||
|
||||
|
@ -2,26 +2,29 @@
|
||||
pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { Test } from "@forge-std/Test.sol";
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { ERC20Permit } from "torn-token/contracts/ERC20Permit.sol";
|
||||
|
||||
import { Mock } from "./Mock.sol";
|
||||
import { Utils } from "./Utils.sol";
|
||||
import { Proposal, IGovernance } from "@interfaces/IGovernance.sol";
|
||||
import { Parameters } from "@proprietary/Parameters.sol";
|
||||
import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol";
|
||||
|
||||
contract ProposalUtils is Mock, Parameters, Test {
|
||||
contract ProposalUtils is Utils {
|
||||
GovernancePatchUpgrade internal governance = GovernancePatchUpgrade(payable(_governanceAddress));
|
||||
|
||||
function getProposalExecutableTime(uint256 proposalId) internal view returns (uint256) {
|
||||
Proposal memory proposal = IGovernance(_governanceAddress).proposals(proposalId);
|
||||
return proposal.endTime + PROPOSAL_LOCKED_DURATION + 1 hours;
|
||||
Proposal memory proposal = getProposal(proposalId);
|
||||
return proposal.endTime + PROPOSAL_LOCKED_DURATION + 1 seconds;
|
||||
}
|
||||
|
||||
function getProposal(uint256 proposalId) internal view returns (Proposal memory){
|
||||
return IGovernance(_governanceAddress).proposals(proposalId);
|
||||
}
|
||||
|
||||
function hasProposal(uint256 proposalId) internal view returns (bool){
|
||||
return governance.proposalCount() >= proposalId;
|
||||
}
|
||||
|
||||
function waitUntilExecutable(uint256 proposalId) internal {
|
||||
uint256 proposalExecutableTime = getProposalExecutableTime(proposalId);
|
||||
require(block.timestamp < proposalExecutableTime, "Too late to execute proposal");
|
||||
require(block.timestamp < proposalExecutableTime + PROPOSAL_EXECUTION_MAX_DURATION, "Too late to execute proposal");
|
||||
|
||||
vm.warp(proposalExecutableTime);
|
||||
}
|
||||
@ -52,41 +55,10 @@ contract ProposalUtils is Mock, Parameters, Test {
|
||||
return proposalId;
|
||||
}
|
||||
|
||||
function retrieveAndLockBalance(uint256 privateKey, address voter, uint256 amount) internal {
|
||||
uint256 lockTimestamp = block.timestamp + PROPOSAL_DURATION;
|
||||
uint256 accountNonce = ERC20Permit(_tokenAddress).nonces(voter);
|
||||
|
||||
bytes32 messageHash = keccak256(
|
||||
abi.encodePacked(
|
||||
PERMIT_FUNC_SELECTOR,
|
||||
EIP712_DOMAIN,
|
||||
keccak256(
|
||||
abi.encode(
|
||||
PERMIT_TYPEHASH, voter, _governanceAddress, amount, accountNonce, lockTimestamp
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/* ----------GOVERNANCE------- */
|
||||
vm.startPrank(_governanceAddress);
|
||||
IERC20(_tokenAddress).transfer(voter, amount);
|
||||
vm.stopPrank();
|
||||
/* ----------------------------*/
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, messageHash);
|
||||
|
||||
/* ----------VOTER------------ */
|
||||
vm.startPrank(voter);
|
||||
governance.lock(voter, amount, lockTimestamp, v, r, s);
|
||||
vm.stopPrank();
|
||||
/* ----------------------------*/
|
||||
}
|
||||
|
||||
function proposeAndExecute(address proposalAddress) public {
|
||||
uint256 proposalId = proposeAndVote(proposalAddress);
|
||||
|
||||
waitUntilExecutable(proposalId);
|
||||
governance.execute(proposalId);
|
||||
IGovernance(_governanceAddress).execute(proposalId);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ contract TestGovernanceStakingRewards is MockProposal {
|
||||
|
||||
console2.log(
|
||||
"Accumulated reward per TORN right after proposal execution: %s TORN",
|
||||
accumulatedRewardPerTornBeforeBurning / 10e17
|
||||
accumulatedRewardPerTornBeforeBurning / _tornDecimals
|
||||
);
|
||||
|
||||
burnTokens(_governanceAddress, 10_000_000 ether, staking);
|
||||
@ -45,7 +45,7 @@ contract TestGovernanceStakingRewards is MockProposal {
|
||||
|
||||
console2.log(
|
||||
"Accumulated reward per TORN after burning 10 000 000 TORN: ~ %s TORN",
|
||||
accumulatedRewardPerTornAfterBurning / 10e17
|
||||
accumulatedRewardPerTornAfterBurning / _tornDecimals
|
||||
);
|
||||
|
||||
require(
|
||||
@ -65,18 +65,18 @@ contract TestGovernanceStakingRewards is MockProposal {
|
||||
|
||||
uint256 toBurn = 10_000 ether;
|
||||
|
||||
// Remind that we have locked in Governance 25 000 TORN for TEST_ADDRESS_ONE while voting
|
||||
uint256 stakerLockedBalance = governance.lockedBalance(TEST_ADDRESS_ONE);
|
||||
require(stakerLockedBalance == 25_000 ether, "Invalid test staker locked balance");
|
||||
retrieveAndLockBalance(TEST_STAKER_PRIVATE_KEY, TEST_STAKER_ADDRESS, PROPOSAL_THRESHOLD);
|
||||
uint256 stakerLockedBalance = governance.lockedBalance(TEST_STAKER_ADDRESS);
|
||||
require(stakerLockedBalance == PROPOSAL_THRESHOLD, "Invalid test staker locked balance");
|
||||
|
||||
uint256 stakerRewardsBeforeBurning = staking.checkReward(TEST_ADDRESS_ONE);
|
||||
console2.log("Staking rewards before burning: %s TORN", stakerRewardsBeforeBurning / 10e17);
|
||||
uint256 stakerRewardsBeforeBurning = staking.checkReward(TEST_STAKER_ADDRESS);
|
||||
console2.log("Staking rewards before burning: %s TORN", stakerRewardsBeforeBurning / _tornDecimals);
|
||||
|
||||
burnTokens(_governanceAddress, toBurn, staking);
|
||||
|
||||
uint256 stakerRewardsAfterBurning = staking.checkReward(TEST_ADDRESS_ONE);
|
||||
uint256 stakerRewardsAfterBurning = staking.checkReward(TEST_STAKER_ADDRESS);
|
||||
console2.log(
|
||||
"Staking rewards after burning 10 000 TORN: %s TORN\n", stakerRewardsAfterBurning / 10e17
|
||||
"Staking rewards after burning 10 000 TORN: %s TORN\n", stakerRewardsAfterBurning / _tornDecimals
|
||||
);
|
||||
require(stakerRewardsAfterBurning > stakerRewardsBeforeBurning, "Rewards isn't changed after burning");
|
||||
|
||||
@ -85,25 +85,25 @@ contract TestGovernanceStakingRewards is MockProposal {
|
||||
uint256 receivedReward = stakerRewardsAfterBurning - stakerRewardsBeforeBurning;
|
||||
uint256 expectedRewards = stakerLockedBalance * toBurn / governanceLockedAmount;
|
||||
|
||||
console2.log("Expected staking rewards: %s TORN", expectedRewards / 10e17);
|
||||
console2.log("Staker received rewards: %s TORN\n", receivedReward / 10e17);
|
||||
console2.log("Expected staking rewards: %s TORN", expectedRewards / _tornDecimals);
|
||||
console2.log("Staker received rewards: %s TORN\n", receivedReward / _tornDecimals);
|
||||
|
||||
require(receivedReward == expectedRewards, "Expected and received rewards don't match");
|
||||
|
||||
uint256 stakerTORNbalanceBeforeGettingRewards = TORN.balanceOf(TEST_ADDRESS_ONE);
|
||||
uint256 stakerTORNbalanceBeforeGettingRewards = TORN.balanceOf(TEST_STAKER_ADDRESS);
|
||||
console2.log(
|
||||
"Staker balance before getting (withdrawal) collected rewards: %s TORN",
|
||||
stakerTORNbalanceBeforeGettingRewards / 10e17
|
||||
stakerTORNbalanceBeforeGettingRewards / _tornDecimals
|
||||
);
|
||||
|
||||
vm.startPrank(TEST_ADDRESS_ONE);
|
||||
vm.startPrank(TEST_STAKER_ADDRESS);
|
||||
staking.getReward();
|
||||
vm.stopPrank();
|
||||
|
||||
uint256 stakerTORNbalanceAfterGettingRewards = TORN.balanceOf(TEST_ADDRESS_ONE);
|
||||
uint256 stakerTORNbalanceAfterGettingRewards = TORN.balanceOf(TEST_STAKER_ADDRESS);
|
||||
console2.log(
|
||||
"Staker balance after getting (withdrawal) collected rewards: %s TORN",
|
||||
stakerTORNbalanceAfterGettingRewards / 10e17
|
||||
stakerTORNbalanceAfterGettingRewards / _tornDecimals
|
||||
);
|
||||
|
||||
require(
|
||||
|
44
test/forge/Utils.sol
Normal file
44
test/forge/Utils.sol
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import { ERC20Permit } from "torn-token/contracts/ERC20Permit.sol";
|
||||
import { Test } from "@forge-std/Test.sol";
|
||||
|
||||
import { Parameters } from "@proprietary/Parameters.sol";
|
||||
import { Mock } from "./Mock.sol";
|
||||
import { IGovernance } from "@interfaces/IGovernance.sol";
|
||||
|
||||
contract Utils is Parameters, Mock, Test {
|
||||
function retrieveAndLockBalance(uint256 privateKey, address voter, uint256 amount) public {
|
||||
uint256 lockTimestamp = block.timestamp + PROPOSAL_DURATION;
|
||||
uint256 accountNonce = ERC20Permit(_tokenAddress).nonces(voter);
|
||||
|
||||
bytes32 messageHash = keccak256(
|
||||
abi.encodePacked(
|
||||
PERMIT_FUNC_SELECTOR,
|
||||
EIP712_DOMAIN,
|
||||
keccak256(
|
||||
abi.encode(
|
||||
PERMIT_TYPEHASH, voter, _governanceAddress, amount, accountNonce, lockTimestamp
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/* ----------GOVERNANCE------- */
|
||||
vm.startPrank(_governanceAddress);
|
||||
IERC20(_tokenAddress).transfer(voter, amount);
|
||||
vm.stopPrank();
|
||||
/* ----------------------------*/
|
||||
|
||||
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, messageHash);
|
||||
|
||||
/* ----------VOTER------------ */
|
||||
vm.startPrank(voter);
|
||||
IGovernance(_governanceAddress).lock(voter, amount, lockTimestamp, v, r, s);
|
||||
vm.stopPrank();
|
||||
/* ----------------------------*/
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@ struct Proposal {
|
||||
interface IGovernance {
|
||||
function proposals(uint256 index) external view returns (Proposal memory);
|
||||
|
||||
function proposalCount() external view returns (uint256);
|
||||
|
||||
function lockedBalance(address account) external view returns (uint256);
|
||||
|
||||
function propose(address target, string memory description) external returns (uint256);
|
||||
|
Loading…
Reference in New Issue
Block a user