main #1

Merged
Theo merged 2 commits from anon/proposal-22-governance-and-rewards-patch:main into main 2023-05-27 18:43:27 +03:00
26 changed files with 671 additions and 6440 deletions

1
.gitignore vendored

@ -16,3 +16,4 @@ docs/
# yarn # yarn
yarn-error* yarn-error*
yarn.lock

@ -9,7 +9,7 @@ optimizer-runs = 10_000_000
[fmt] [fmt]
line_length = 140 line_length = 110
bracket_spacing = true bracket_spacing = true
multiline_func_header = 'attributes_first' multiline_func_header = 'attributes_first'
number_underscore = 'thousands' number_underscore = 'thousands'

@ -6,7 +6,7 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test": "forge test -vvv --fork-url https://rpc.mevblocker.io --fork-block-number 17336117 --no-match-contract TestRelayerBalance --gas-report", "test": "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 17304425 --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": { "dependencies": {
"@gnosis.pm/ido-contracts": "^0.5.0", "@gnosis.pm/ido-contracts": "^0.5.0",

@ -1,5 +1,5 @@
@proprietary/=src/proprietary/ @proprietary/=test/proprietary/
@interfaces/=src/interfaces/ @interfaces/=test/interfaces/
@root/=src/ @root/=src/
@forge-std/=lib/forge-std/src/ @forge-std/=lib/forge-std/src/

@ -13,7 +13,10 @@ abstract contract Delegation is Core {
function delegate(address to) external { function delegate(address to) external {
address previous = delegatedTo[msg.sender]; address previous = delegatedTo[msg.sender];
require(to != msg.sender && to != address(this) && to != address(0) && to != previous, "Governance: invalid delegatee"); require(
to != msg.sender && to != address(this) && to != address(0) && to != previous,
"Governance: invalid delegatee"
);
if (previous != address(0)) { if (previous != address(0)) {
emit Undelegated(msg.sender, previous); emit Undelegated(msg.sender, previous);
} }
@ -29,12 +32,18 @@ abstract contract Delegation is Core {
emit Undelegated(msg.sender, previous); emit Undelegated(msg.sender, previous);
} }
function proposeByDelegate(address from, address target, string memory description) external returns (uint256) { function proposeByDelegate(address from, address target, string memory description)
external
returns (uint256)
{
require(delegatedTo[from] == msg.sender, "Governance: not authorized"); require(delegatedTo[from] == msg.sender, "Governance: not authorized");
return _propose(from, target, description); return _propose(from, target, description);
} }
function _propose(address proposer, address target, string memory description) internal virtual returns (uint256); function _propose(address proposer, address target, string memory description)
internal
virtual
returns (uint256);
function castDelegatedVote(address[] memory from, uint256 proposalId, bool support) external virtual { function castDelegatedVote(address[] memory from, uint256 proposalId, bool support) external virtual {
for (uint256 i = 0; i < from.length; i++) { for (uint256 i = 0; i < from.length; i++) {

@ -68,7 +68,12 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
/// @notice An event emitted when a new proposal is created /// @notice An event emitted when a new proposal is created
event ProposalCreated( event ProposalCreated(
uint256 indexed id, address indexed proposer, address target, uint256 startTime, uint256 endTime, string description uint256 indexed id,
address indexed proposer,
address target,
uint256 startTime,
uint256 endTime,
string description
); );
/// @notice An event emitted when a vote has been cast on a proposal /// @notice An event emitted when a vote has been cast on a proposal
@ -102,7 +107,10 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
_initializeConfiguration(); _initializeConfiguration();
} }
function lock(address owner, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual { function lock(address owner, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
public
virtual
{
torn.permit(owner, address(this), amount, deadline, v, r, s); torn.permit(owner, address(this), amount, deadline, v, r, s);
_transferTokens(owner, amount); _transferTokens(owner, amount);
} }
@ -135,7 +143,9 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
returns (uint256) returns (uint256)
{ {
uint256 votingPower = lockedBalance[proposer]; uint256 votingPower = lockedBalance[proposer];
require(votingPower >= PROPOSAL_THRESHOLD, "Governance::propose: proposer votes below proposal threshold"); require(
votingPower >= PROPOSAL_THRESHOLD, "Governance::propose: proposer votes below proposal threshold"
);
// target should be a contract // target should be a contract
require(Address.isContract(target), "Governance::propose: not a contract"); require(Address.isContract(target), "Governance::propose: not a contract");
@ -143,7 +153,8 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
if (latestProposalId != 0) { if (latestProposalId != 0) {
ProposalState proposersLatestProposalState = state(latestProposalId); ProposalState proposersLatestProposalState = state(latestProposalId);
require( require(
proposersLatestProposalState != ProposalState.Active && proposersLatestProposalState != ProposalState.Pending, proposersLatestProposalState != ProposalState.Active
&& proposersLatestProposalState != ProposalState.Pending,
"Governance::propose: one live proposal per proposer, found an already active proposal" "Governance::propose: one live proposal per proposer, found an already active proposal"
); );
} }
@ -172,7 +183,10 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
} }
function execute(uint256 proposalId) public payable virtual { function execute(uint256 proposalId) public payable virtual {
require(state(proposalId) == ProposalState.AwaitingExecution, "Governance::execute: invalid proposal state"); require(
state(proposalId) == ProposalState.AwaitingExecution,
"Governance::execute: invalid proposal state"
);
Proposal storage proposal = proposals[proposalId]; Proposal storage proposal = proposals[proposalId];
proposal.executed = true; proposal.executed = true;
@ -226,7 +240,9 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
receipt.hasVoted = true; receipt.hasVoted = true;
receipt.support = support; receipt.support = support;
receipt.votes = votes; receipt.votes = votes;
_lockTokens(voter, proposal.endTime.add(VOTE_EXTEND_TIME).add(EXECUTION_EXPIRATION).add(EXECUTION_DELAY)); _lockTokens(
voter, proposal.endTime.add(VOTE_EXTEND_TIME).add(EXECUTION_EXPIRATION).add(EXECUTION_DELAY)
);
emit Voted(proposalId, voter, support, votes); emit Voted(proposalId, voter, support, votes);
} }
@ -252,7 +268,10 @@ contract Governance is Initializable, Configuration, Delegation, EnsResolve {
return ProposalState.Pending; return ProposalState.Pending;
} else if (getBlockTimestamp() <= proposal.endTime) { } else if (getBlockTimestamp() <= proposal.endTime) {
return ProposalState.Active; return ProposalState.Active;
} else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes + proposal.againstVotes < QUORUM_VOTES) { } else if (
proposal.forVotes <= proposal.againstVotes
|| proposal.forVotes + proposal.againstVotes < QUORUM_VOTES
) {
return ProposalState.Defeated; return ProposalState.Defeated;
} else if (proposal.executed) { } else if (proposal.executed) {
return ProposalState.Executed; return ProposalState.Executed;

@ -13,7 +13,11 @@ contract LoopbackProxy is TransparentUpgradeableProxy, EnsResolve {
/** /**
* @dev Initializes an upgradeable proxy backed by the implementation at `_logic`. * @dev Initializes an upgradeable proxy backed by the implementation at `_logic`.
*/ */
constructor(address _logic, bytes memory _data) public payable TransparentUpgradeableProxy(_logic, address(this), _data) { } constructor(address _logic, bytes memory _data)
public
payable
TransparentUpgradeableProxy(_logic, address(this), _data)
{ }
/** /**
* @dev Override to allow admin (itself) access the fallback function. * @dev Override to allow admin (itself) access the fallback function.

@ -1,13 +1 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "../LoopbackProxy.sol";
contract MockProxy is LoopbackProxy {
constructor(address _logic, bytes memory _data) public payable LoopbackProxy(_logic, _data) { }
function resolve(bytes32 addr) public view override returns (address) {
return address(uint160(uint256(addr) >> (12 * 8)));
}
}

@ -1,31 +1 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "torn-token/contracts/mocks/TORNMock.sol";
struct Recipient2 {
address to;
uint256 amount;
}
contract TORNMock2 is TORNMock {
constructor(address _governance, uint256 _pausePeriod, Recipient2[] memory vesting)
public
TORNMock(solve(_governance), _pausePeriod, solve2(vesting))
{ }
function solve(address x) private returns (bytes32) {
return bytes32(uint256(x) << 96);
}
function solve2(Recipient2[] memory vesting) private returns (Recipient[] memory) {
Recipient[] memory realVesting = new Recipient[](vesting.length);
for (uint256 i = 0; i < vesting.length; i++) {
realVesting[i].to = solve(vesting[i].to);
realVesting[i].amount = vesting[i].amount;
}
return realVesting;
}
}

@ -19,7 +19,11 @@ contract GovernanceGasUpgrade is GovernanceVaultUpgrade, GasCompensator {
* @param _userVault tornado vault address * @param _userVault tornado vault address
* *
*/ */
constructor(address _gasCompLogic, address _userVault) public GovernanceVaultUpgrade(_userVault) GasCompensator(_gasCompLogic) { } constructor(address _gasCompLogic, address _userVault)
public
GovernanceVaultUpgrade(_userVault)
GasCompensator(_gasCompLogic)
{ }
/// @notice check that msg.sender is multisig /// @notice check that msg.sender is multisig
modifier onlyMultisig() { modifier onlyMultisig() {
@ -40,7 +44,11 @@ contract GovernanceGasUpgrade is GovernanceVaultUpgrade, GasCompensator {
* *
*/ */
function setGasCompensations(uint256 gasCompensationsLimit) external virtual override onlyMultisig { function setGasCompensations(uint256 gasCompensationsLimit) external virtual override onlyMultisig {
require(payable(address(gasCompensationVault)).send(Math.min(gasCompensationsLimit, address(this).balance))); require(
payable(address(gasCompensationVault)).send(
Math.min(gasCompensationsLimit, address(this).balance)
)
);
} }
/** /**
@ -85,9 +93,18 @@ contract GovernanceGasUpgrade is GovernanceVaultUpgrade, GasCompensator {
* @param support true if yes false if no * @param support true if yes false if no
* *
*/ */
function castDelegatedVote(address[] memory from, uint256 proposalId, bool support) external virtual override { function castDelegatedVote(address[] memory from, uint256 proposalId, bool support)
external
virtual
override
{
require(from.length > 0, "Can not be empty"); require(from.length > 0, "Can not be empty");
_castDelegatedVote(from, proposalId, support, !hasAccountVoted(proposalId, msg.sender) && !checkIfQuorumReached(proposalId)); _castDelegatedVote(
from,
proposalId,
support,
!hasAccountVoted(proposalId, msg.sender) && !checkIfQuorumReached(proposalId)
);
} }
/// @notice checker for success on deployment /// @notice checker for success on deployment
@ -148,7 +165,9 @@ contract GovernanceGasUpgrade is GovernanceVaultUpgrade, GasCompensator {
{ {
for (uint256 i = 0; i < from.length; i++) { for (uint256 i = 0; i < from.length; i++) {
address delegator = from[i]; address delegator = from[i];
require(delegatedTo[delegator] == msg.sender || delegator == msg.sender, "Governance: not authorized"); require(
delegatedTo[delegator] == msg.sender || delegator == msg.sender, "Governance: not authorized"
);
require(!gasCompensated || !hasAccountVoted(proposalId, delegator), "Governance: voted already"); require(!gasCompensated || !hasAccountVoted(proposalId, delegator), "Governance: voted already");
_castVote(delegator, proposalId, support); _castVote(delegator, proposalId, support);
} }

@ -1,16 +1 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../../v1/Governance.sol";
contract MockProposal {
address public constant GovernanceAddress = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce;
function executeProposal() external {
Governance gov = Governance(GovernanceAddress);
gov.setVotingPeriod(27_000);
require(gov.VOTING_PERIOD() == 27_000, "Voting period change failed!");
}
}

@ -11,7 +11,11 @@ contract AdminUpgradeableProxy is TransparentUpgradeableProxy {
/** /**
* @dev Initializes an upgradeable proxy backed by the implementation at `_logic`. * @dev Initializes an upgradeable proxy backed by the implementation at `_logic`.
*/ */
constructor(address _logic, address _admin, bytes memory _data) public payable TransparentUpgradeableProxy(_logic, _admin, _data) { } constructor(address _logic, address _admin, bytes memory _data)
public
payable
TransparentUpgradeableProxy(_logic, _admin, _data)
{ }
/** /**
* @dev Override to allow admin access the fallback function. * @dev Override to allow admin access the fallback function.

@ -37,7 +37,10 @@ contract GovernancePatchUpgrade is GovernanceStakingUpgrade {
proposalCodehash := extcodehash(target) proposalCodehash := extcodehash(target)
} }
require(proposalCodehash == proposalCodehashes[proposalId], "Governance::propose: metamorphic contracts not allowed"); require(
proposalCodehash == proposalCodehashes[proposalId],
"Governance::propose: metamorphic contracts not allowed"
);
super.execute(proposalId); super.execute(proposalId);
} }

@ -24,7 +24,10 @@ contract PatchProposalContractsFactory {
* @param registry The address of the relayer registry. * @param registry The address of the relayer registry.
* @return The address of the new staking contract. * @return The address of the new staking contract.
*/ */
function createStakingRewards(address governance, address torn, address registry) external returns (address) { function createStakingRewards(address governance, address torn, address registry)
external
returns (address)
{
return address(new TornadoStakingRewards(governance, torn, registry)); return address(new TornadoStakingRewards(governance, torn, registry));
} }
@ -36,10 +39,13 @@ contract PatchProposalContractsFactory {
* @param staking The TornadoStakingRewards contract address. * @param staking The TornadoStakingRewards contract address.
* @return The address of the new registry contract. * @return The address of the new registry contract.
*/ */
function createRegistryContract(address torn, address governance, address ens, address staking, address feeManager) function createRegistryContract(
external address torn,
returns (address) address governance,
{ address ens,
address staking,
address feeManager
) external returns (address) {
return address(new RelayerRegistry(torn, governance, ens, staking, feeManager)); return address(new RelayerRegistry(torn, governance, ens, staking, feeManager));
} }
} }
@ -51,9 +57,9 @@ contract PatchProposal {
using SafeMath for uint256; using SafeMath for uint256;
using Address for address; using Address for address;
address public immutable feeManagerAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7; address public immutable feeManagerProxyAddress = 0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7;
address public immutable registryProxyAddress = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2;
address public immutable ensAddress = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e; address public immutable ensAddress = 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e;
address public immutable registry = 0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2;
IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C); IERC20 public constant TORN = IERC20(0x77777FeDdddFfC19Ff86DB637967013e6C6A116C);
@ -73,31 +79,39 @@ contract PatchProposal {
address vault = address(GovernancePatchUpgrade(governance).userVault()); address vault = address(GovernancePatchUpgrade(governance).userVault());
// Get the old staking contract // Get the old staking contract
TornadoStakingRewards oldStaking = TornadoStakingRewards(address(GovernancePatchUpgrade(governance).Staking())); TornadoStakingRewards oldStaking =
TornadoStakingRewards(address(GovernancePatchUpgrade(governance).Staking()));
// Get the small amount of TORN left // Get the small amount of TORN left
oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking))); oldStaking.withdrawTorn(TORN.balanceOf(address(oldStaking)));
// And create a new staking logic contract // And create a new staking logic contract
TornadoStakingRewards newStakingImplementation = TornadoStakingRewards newStakingImplementation = TornadoStakingRewards(
TornadoStakingRewards(patchProposalContractsFactory.createStakingRewards(address(governance), address(TORN), registry)); patchProposalContractsFactory.createStakingRewards(
address(governance), address(TORN), registryProxyAddress
)
);
// Create new staking proxy contract (without initialization value) // Create new staking proxy contract (without initialization value)
bytes memory empty; bytes memory empty;
AdminUpgradeableProxy newStaking = new AdminUpgradeableProxy(address(newStakingImplementation), address(governance), empty);
address newStaking =
address(new AdminUpgradeableProxy(address(newStakingImplementation), address(governance), empty));
// And a new registry implementation // And a new registry implementation
address newRegistryImplementationAddress = patchProposalContractsFactory.createRegistryContract( address newRegistryImplementationAddress = patchProposalContractsFactory.createRegistryContract(
address(TORN), address(governance), ensAddress, address(newStaking), feeManagerAddress address(TORN), address(governance), ensAddress, newStaking, feeManagerProxyAddress
); );
// Upgrade the registry proxy // Upgrade the registry proxy
AdminUpgradeableProxy(payable(registry)).upgradeTo(newRegistryImplementationAddress); AdminUpgradeableProxy(payable(registryProxyAddress)).upgradeTo(newRegistryImplementationAddress);
// Now upgrade the governance to the latest stuff // Now upgrade the governance to the latest stuff
LoopbackProxy(payable(governance)).upgradeTo(address(new GovernancePatchUpgrade(address(newStaking), gasComp, vault))); LoopbackProxy(payable(governance)).upgradeTo(
address(new GovernancePatchUpgrade(newStaking, gasComp, vault))
);
// Return TORNs, which were withdrawn by bug, to Governance Staking contract // Compensate TORN for staking
TORN.transfer(address(newStaking), 94_092 ether); TORN.transfer(newStaking, 94_092 ether);
} }
} }

@ -132,7 +132,9 @@ contract RelayerRegistry is Initializable, EnsResolve {
_; _;
} }
constructor(address _torn, address _governance, address _ens, address _staking, address _feeManager) public { constructor(address _torn, address _governance, address _ens, address _staking, address _feeManager)
public
{
torn = TORN(_torn); torn = TORN(_torn);
governance = _governance; governance = _governance;
ens = IENS(_ens); ens = IENS(_ens);
@ -157,7 +159,9 @@ contract RelayerRegistry is Initializable, EnsResolve {
* @param stake the initial amount of stake in TORN the relayer is depositing * @param stake the initial amount of stake in TORN the relayer is depositing
* *
*/ */
function register(string calldata ensName, uint256 stake, address[] calldata workersToRegister) external { function register(string calldata ensName, uint256 stake, address[] calldata workersToRegister)
external
{
_register(msg.sender, ensName, stake, workersToRegister); _register(msg.sender, ensName, stake, workersToRegister);
} }
@ -179,7 +183,12 @@ contract RelayerRegistry is Initializable, EnsResolve {
_register(relayer, ensName, stake, workersToRegister); _register(relayer, ensName, stake, workersToRegister);
} }
function _register(address relayer, string calldata ensName, uint256 stake, address[] calldata workersToRegister) internal { function _register(
address relayer,
string calldata ensName,
uint256 stake,
address[] calldata workersToRegister
) internal {
bytes32 ensHash = bytes(ensName).namehash(); bytes32 ensHash = bytes(ensName).namehash();
require(relayer == ens.owner(ensHash), "only ens owner"); require(relayer == ens.owner(ensHash), "only ens owner");
require(workers[relayer] == address(0), "cant register again"); require(workers[relayer] == address(0), "cant register again");
@ -249,9 +258,15 @@ contract RelayerRegistry is Initializable, EnsResolve {
* @param staker address from that stake is paid * @param staker address from that stake is paid
* *
*/ */
function stakeToRelayerPermit(address relayer, uint256 stake, address staker, uint256 deadline, uint8 v, bytes32 r, bytes32 s) function stakeToRelayerPermit(
external address relayer,
{ uint256 stake,
address staker,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
torn.permit(staker, address(this), stake, deadline, v, r, s); torn.permit(staker, address(this), stake, deadline, v, r, s);
_stakeToRelayer(staker, relayer, stake); _stakeToRelayer(staker, relayer, stake);
} }

@ -85,8 +85,9 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
*/ */
function addBurnRewards(uint256 amount) external { function addBurnRewards(uint256 amount) external {
require(msg.sender == address(Governance) || msg.sender == relayerRegistry, "unauthorized"); require(msg.sender == address(Governance) || msg.sender == relayerRegistry, "unauthorized");
accumulatedRewardPerTorn = accumulatedRewardPerTorn = accumulatedRewardPerTorn.add(
accumulatedRewardPerTorn.add(amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault())))); amount.mul(ratioConstant).div(torn.balanceOf(address(Governance.userVault())))
);
} }
/** /**
@ -95,7 +96,10 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
* @param amountLockedBeforehand the balance locked beforehand in the governance contract * @param amountLockedBeforehand the balance locked beforehand in the governance contract
* *
*/ */
function updateRewardsOnLockedBalanceChange(address account, uint256 amountLockedBeforehand) external onlyGovernance { function updateRewardsOnLockedBalanceChange(address account, uint256 amountLockedBeforehand)
external
onlyGovernance
{
uint256 claimed = _updateReward(account, amountLockedBeforehand); uint256 claimed = _updateReward(account, amountLockedBeforehand);
accumulatedRewards[account] = accumulatedRewards[account].add(claimed); accumulatedRewards[account] = accumulatedRewards[account].add(claimed);
} }
@ -119,10 +123,14 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
* @param amountLockedBeforehand the balance locked beforehand in the governance contract * @param amountLockedBeforehand the balance locked beforehand in the governance contract
* @return claimed the rewards attributed to user since the last update * @return claimed the rewards attributed to user since the last update
*/ */
function _updateReward(address account, uint256 amountLockedBeforehand) private returns (uint256 claimed) { function _updateReward(address account, uint256 amountLockedBeforehand)
private
returns (uint256 claimed)
{
if (amountLockedBeforehand != 0) { if (amountLockedBeforehand != 0) {
claimed = claimed = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(
(accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(amountLockedBeforehand).div(ratioConstant); amountLockedBeforehand
).div(ratioConstant);
} }
accumulatedRewardRateOnLastUpdate[account] = accumulatedRewardPerTorn; accumulatedRewardRateOnLastUpdate[account] = accumulatedRewardPerTorn;
emit RewardsUpdated(account, claimed); emit RewardsUpdated(account, claimed);
@ -135,7 +143,9 @@ contract TornadoStakingRewards is Initializable, EnsResolve {
function checkReward(address account) external view returns (uint256 rewards) { function checkReward(address account) external view returns (uint256 rewards) {
uint256 amountLocked = Governance.lockedBalance(account); uint256 amountLocked = Governance.lockedBalance(account);
if (amountLocked != 0) { if (amountLocked != 0) {
rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(amountLocked).div(ratioConstant); rewards = (accumulatedRewardPerTorn.sub(accumulatedRewardRateOnLastUpdate[account])).mul(
amountLocked
).div(ratioConstant);
} }
rewards = rewards.add(accumulatedRewards[account]); rewards = rewards.add(accumulatedRewards[account]);
} }

@ -3,7 +3,10 @@
pragma solidity 0.6.12; pragma solidity 0.6.12;
interface IMetamorphicContractFactory { interface IMetamorphicContractFactory {
function findMetamorphicContractAddress(bytes32 salt) external view returns (address metamorphicContractAddress); function findMetamorphicContractAddress(bytes32 salt)
external
view
returns (address metamorphicContractAddress);
function deployMetamorphicContractFromExistingImplementation( function deployMetamorphicContractFromExistingImplementation(
bytes32 salt, bytes32 salt,

@ -1,40 +1,42 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
contract Mock { contract Mock {
// Developer address with 22 staked TORN // Developer address with 22 staked TORN
address public constant TEST_REAL_ADDRESS_WITH_BALANCE = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58; address public constant TEST_REAL_ADDRESS_WITH_BALANCE = 0x9Ff3C1Bea9ffB56a78824FE29f457F066257DD58;
address public constant TEST_RELAYER_ADDRESS = 0x30F96AEF199B399B722F8819c9b0723016CEAe6C; // moon-relayer.eth (just for testing) 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_ONE =
uint256 public constant TEST_PRIVATE_KEY_TWO = 0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064; 0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0;
address public constant TEST_ADDRESS_ONE = 0x118251976c65AFAf291f5255450ddb5b6A4d8B88; uint256 public constant TEST_PRIVATE_KEY_TWO =
address public constant TEST_ADDRESS_TWO = 0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20; 0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064;
address public constant TEST_ADDRESS_ONE = 0x118251976c65AFAf291f5255450ddb5b6A4d8B88;
uint256 public constant ATTACKER_PROPOSAL_ID = 21; // Last attacker proposal (to restore Governance Vault balance) id address public constant TEST_ADDRESS_TWO = 0x63aE7d90Eb37ca39FC62dD9991DbEfeE70673a20;
uint256 public constant PROPOSAL_VOTING_DURATION = 5 days; uint256 public constant ATTACKER_PROPOSAL_ID = 21; // Last attacker proposal (to restore Governance Vault balance) id
uint256 public constant PROPOSAL_LOCKED_DURATION = 2 days;
uint256 public constant PROPOSAL_DURATION = PROPOSAL_VOTING_DURATION + PROPOSAL_LOCKED_DURATION; uint256 public constant PROPOSAL_VOTING_DURATION = 5 days;
uint256 public constant PROPOSAL_EXECUTION_MAX_DURATION = 3 days; uint256 public constant PROPOSAL_LOCKED_DURATION = 2 days;
uint256 public constant PROPOSAL_THRESHOLD = 25_000 ether; uint256 public constant PROPOSAL_DURATION = PROPOSAL_VOTING_DURATION + PROPOSAL_LOCKED_DURATION;
string public constant PROPOSAL_DESCRIPTION = "{title:'Some proposal',description:''}"; uint256 public constant PROPOSAL_EXECUTION_MAX_DURATION = 3 days;
uint256 public constant PROPOSAL_THRESHOLD = 25_000 ether;
address public constant VERIFIER_ADDRESS = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; string public constant PROPOSAL_DESCRIPTION = "{title:'Some proposal',description:''}";
bytes32 public constant PERMIT_TYPEHASH = address public constant VERIFIER_ADDRESS = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH =
bytes32 public constant EIP712_DOMAIN = keccak256( keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), bytes32 public constant EIP712_DOMAIN = keccak256(
keccak256(bytes("TornadoCash")), abi.encode(
keccak256(bytes("1")), keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
1, keccak256(bytes("TornadoCash")),
VERIFIER_ADDRESS keccak256(bytes("1")),
) 1,
); VERIFIER_ADDRESS
)
uint16 public constant PERMIT_FUNC_SELECTOR = uint16(0x1901); );
}
uint16 public constant PERMIT_FUNC_SELECTOR = uint16(0x1901);
}

@ -1,89 +1,91 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import { Test } from "@forge-std/Test.sol"; import { Test } from "@forge-std/Test.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC20Permit } from "torn-token/contracts/ERC20Permit.sol"; import { ERC20Permit } from "torn-token/contracts/ERC20Permit.sol";
import { console2 } from "@forge-std/console2.sol"; import { console2 } from "@forge-std/console2.sol";
import { Mock } from "./Mock.sol"; import { Mock } from "./Mock.sol";
import { Proposal, IGovernance } from "@interfaces/IGovernance.sol"; import { Proposal, IGovernance } from "@interfaces/IGovernance.sol";
import { Parameters } from "@proprietary/Parameters.sol"; import { Parameters } from "@proprietary/Parameters.sol";
import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol"; import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol";
contract ProposalUtils is Mock, Parameters, Test { contract ProposalUtils is Mock, Parameters, Test {
GovernancePatchUpgrade internal governance = GovernancePatchUpgrade(payable(_governanceAddress)); GovernancePatchUpgrade internal governance = GovernancePatchUpgrade(payable(_governanceAddress));
function getProposalExecutableTime(uint256 proposalId) internal view returns (uint256) { function getProposalExecutableTime(uint256 proposalId) internal view returns (uint256) {
Proposal memory proposal = IGovernance(_governanceAddress).proposals(proposalId); Proposal memory proposal = IGovernance(_governanceAddress).proposals(proposalId);
return proposal.endTime + PROPOSAL_LOCKED_DURATION + 1 hours; return proposal.endTime + PROPOSAL_LOCKED_DURATION + 1 hours;
} }
function waitUntilExecutable(uint256 proposalId) internal { function waitUntilExecutable(uint256 proposalId) internal {
uint256 proposalExecutableTime = getProposalExecutableTime(proposalId); uint256 proposalExecutableTime = getProposalExecutableTime(proposalId);
require(block.timestamp < proposalExecutableTime, "Too late to execute proposal"); require(block.timestamp < proposalExecutableTime, "Too late to execute proposal");
vm.warp(proposalExecutableTime); vm.warp(proposalExecutableTime);
} }
function proposeAndVote(address proposalAddress) public returns (uint256) { function proposeAndVote(address proposalAddress) public returns (uint256) {
retrieveAndLockBalance(TEST_PRIVATE_KEY_ONE, TEST_ADDRESS_ONE, PROPOSAL_THRESHOLD); retrieveAndLockBalance(TEST_PRIVATE_KEY_ONE, TEST_ADDRESS_ONE, PROPOSAL_THRESHOLD);
retrieveAndLockBalance(TEST_PRIVATE_KEY_TWO, TEST_ADDRESS_TWO, 1 ether); retrieveAndLockBalance(TEST_PRIVATE_KEY_TWO, TEST_ADDRESS_TWO, 1 ether);
/* ----------PROPOSER------------ */ /* ----------PROPOSER------------ */
vm.startPrank(TEST_ADDRESS_ONE); vm.startPrank(TEST_ADDRESS_ONE);
uint256 proposalId = governance.propose(proposalAddress, PROPOSAL_DESCRIPTION); uint256 proposalId = governance.propose(proposalAddress, PROPOSAL_DESCRIPTION);
// TIME-TRAVEL // TIME-TRAVEL
vm.warp(block.timestamp + 6 hours); vm.warp(block.timestamp + 6 hours);
governance.castVote(proposalId, true); governance.castVote(proposalId, true);
vm.stopPrank(); vm.stopPrank();
/* ------------------------------ */ /* ------------------------------ */
/* -------------VOTER-------------*/ /* -------------VOTER-------------*/
vm.startPrank(TEST_ADDRESS_TWO); vm.startPrank(TEST_ADDRESS_TWO);
governance.castVote(proposalId, true); governance.castVote(proposalId, true);
vm.stopPrank(); vm.stopPrank();
/* ------------------------------ */ /* ------------------------------ */
return proposalId; return proposalId;
} }
function retrieveAndLockBalance(uint256 privateKey, address voter, uint256 amount) internal { function retrieveAndLockBalance(uint256 privateKey, address voter, uint256 amount) internal {
uint256 lockTimestamp = block.timestamp + PROPOSAL_DURATION; uint256 lockTimestamp = block.timestamp + PROPOSAL_DURATION;
uint256 accountNonce = ERC20Permit(_tokenAddress).nonces(voter); uint256 accountNonce = ERC20Permit(_tokenAddress).nonces(voter);
bytes32 messageHash = keccak256( console2.log("Account nonce: %s", accountNonce);
abi.encodePacked(
PERMIT_FUNC_SELECTOR, bytes32 messageHash = keccak256(
EIP712_DOMAIN, abi.encodePacked(
keccak256(abi.encode(PERMIT_TYPEHASH, voter, _governanceAddress, amount, accountNonce, lockTimestamp)) PERMIT_FUNC_SELECTOR,
) EIP712_DOMAIN,
); keccak256(abi.encode(PERMIT_TYPEHASH, voter, _governanceAddress, amount, accountNonce, lockTimestamp))
)
/* ----------GOVERNANCE------- */ );
vm.startPrank(_governanceAddress);
IERC20(_tokenAddress).transfer(voter, amount); /* ----------GOVERNANCE------- */
vm.stopPrank(); vm.startPrank(_governanceAddress);
/* ----------------------------*/ IERC20(_tokenAddress).transfer(voter, amount);
vm.stopPrank();
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, messageHash); /* ----------------------------*/
/* ----------VOTER------------ */ (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, messageHash);
vm.startPrank(voter);
governance.lock(voter, amount, lockTimestamp, v, r, s); /* ----------VOTER------------ */
vm.stopPrank(); vm.startPrank(voter);
/* ----------------------------*/ governance.lock(voter, amount, lockTimestamp, v, r, s);
} vm.stopPrank();
/* ----------------------------*/
function proposeAndExecute(address proposalAddress) public { }
uint256 proposalId = proposeAndVote(proposalAddress);
function proposeAndExecute(address proposalAddress) public {
waitUntilExecutable(proposalId); uint256 proposalId = proposeAndVote(proposalAddress);
governance.execute(proposalId);
} waitUntilExecutable(proposalId);
} governance.execute(proposalId);
}
}

@ -1,69 +1,85 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import { MockProposal } from "./MockProposal.sol"; import { MockProposal } from "./MockProposal.sol";
import { console2 } from "@forge-std/console2.sol"; import { console2 } from "@forge-std/console2.sol";
import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol"; import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol";
import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol"; import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol";
import { TornadoStakingRewards } from "@root/v4-patch/TornadoStakingRewards.sol"; import { TornadoStakingRewards } from "@root/v4-patch/TornadoStakingRewards.sol";
contract TestContractsState is MockProposal { contract TestContractsState is MockProposal {
function testLockedBalanceSaved() public { function testLockedBalanceSaved() public {
uint256 lockedBalanceBeforeExecution = governance.lockedBalance(TEST_REAL_ADDRESS_WITH_BALANCE); uint256 lockedBalanceBeforeExecution = governance.lockedBalance(TEST_REAL_ADDRESS_WITH_BALANCE);
console2.log("User locked balance before execution: %s TORN", lockedBalanceBeforeExecution / _tornDecimals); console2.log("User locked balance before execution: %s TORN", lockedBalanceBeforeExecution / 10 ** 18);
createAndExecuteProposal(); createAndExecuteProposal();
uint256 lockedBalanceAfterExecution = governance.lockedBalance(TEST_REAL_ADDRESS_WITH_BALANCE); uint256 lockedBalanceAfterExecution = governance.lockedBalance(TEST_REAL_ADDRESS_WITH_BALANCE);
console2.log("User locked balance before execution: %s TORN", lockedBalanceAfterExecution / _tornDecimals); console2.log("User locked balance before execution: %s TORN", lockedBalanceAfterExecution / 10 ** 18);
require(lockedBalanceBeforeExecution == lockedBalanceAfterExecution, "Wrong locked balance after execution"); require(
} lockedBalanceBeforeExecution == lockedBalanceAfterExecution,
"Wrong locked balance after execution"
function testGovernanceStakingStateChanged() public { );
TornadoStakingRewards oldStaking = TornadoStakingRewards(getStakingProxyAddress()); }
uint256 accumulatedRewardsBeforeExecution = oldStaking.checkReward(TEST_REAL_ADDRESS_WITH_BALANCE);
console2.log("User rewards balance (bugged) before execution: %s TORN", accumulatedRewardsBeforeExecution / _tornDecimals); function testGovernanceStakingStateChanged() public {
console2.log("Bugged value of accumulated rewards per TORN: %s", oldStaking.accumulatedRewardPerTorn()); TornadoStakingRewards oldStaking = TornadoStakingRewards(getStakingProxyAddress());
uint256 accumulatedRewardsBeforeExecution = oldStaking.checkReward(TEST_REAL_ADDRESS_WITH_BALANCE);
createAndExecuteProposal(); console2.log(
"User rewards balance (bugged) before execution: %s TORN",
TornadoStakingRewards newStaking = TornadoStakingRewards(getStakingProxyAddress()); accumulatedRewardsBeforeExecution / 10 ** 18
uint256 accumulatedRewardsPerTORNAfterExecution = newStaking.accumulatedRewardPerTorn(); );
uint256 accumulatedRewardsAfterExecution = newStaking.checkReward(TEST_REAL_ADDRESS_WITH_BALANCE); console2.log(
"Bugged value of accumulated rewards per TORN: %s", oldStaking.accumulatedRewardPerTorn()
console2.log("User rewards balance before execution: %s TORN", accumulatedRewardsAfterExecution / _tornDecimals); );
console2.log("Value of accumulated rewards per TORN after contract redeployment: %s", accumulatedRewardsPerTORNAfterExecution);
createAndExecuteProposal();
require(accumulatedRewardsBeforeExecution >= accumulatedRewardsAfterExecution, "Wtf");
require(accumulatedRewardsAfterExecution == 0, "Accumulated rewards isn't nullified"); TornadoStakingRewards newStaking = TornadoStakingRewards(getStakingProxyAddress());
require(accumulatedRewardsPerTORNAfterExecution == 0, "Accumulated rewards per TORN isn't nullified"); uint256 accumulatedRewardsPerTORNAfterExecution = newStaking.accumulatedRewardPerTorn();
} uint256 accumulatedRewardsAfterExecution = newStaking.checkReward(TEST_REAL_ADDRESS_WITH_BALANCE);
function testRelayerRegistryStateSaved() public { console2.log(
RelayerRegistry registry = RelayerRegistry(getRelayerRegistryProxyAddress()); "User rewards balance before execution: %s TORN", accumulatedRewardsAfterExecution / 10 ** 18
);
bool isRelayerRegisteredBeforeExecution = registry.isRelayer(TEST_RELAYER_ADDRESS); console2.log(
uint256 relayerStakedBalanceBeforeExecution = registry.getRelayerBalance(TEST_RELAYER_ADDRESS); "Value of accumulated rewards per TORN after contract redeployment: %s",
console2.log( accumulatedRewardsPerTORNAfterExecution
"Relayer balance in relayer registry contract before proposal execution: %s TORN", );
relayerStakedBalanceBeforeExecution / _tornDecimals
); require(accumulatedRewardsBeforeExecution >= accumulatedRewardsAfterExecution, "Wtf");
require(accumulatedRewardsAfterExecution == 0, "Accumulated rewards isn't nullified");
require(isRelayerRegisteredBeforeExecution, "Relayer not registered"); require(accumulatedRewardsPerTORNAfterExecution == 0, "Accumulated rewards per TORN isn't nullified");
}
createAndExecuteProposal();
function testRelayerRegistryStateSaved() public {
bool isRelayerRegisteredAfterExecution = registry.isRelayer(TEST_RELAYER_ADDRESS); RelayerRegistry registry = RelayerRegistry(getRelayerRegistryProxyAddress());
uint256 relayerStakedBalanceAfterExecution = registry.getRelayerBalance(TEST_RELAYER_ADDRESS);
bool isRelayerRegisteredBeforeExecution = registry.isRelayer(TEST_RELAYER_ADDRESS);
console2.log( uint256 relayerStakedBalanceBeforeExecution = registry.getRelayerBalance(TEST_RELAYER_ADDRESS);
"Relayer balance in relayer registry contract after proposal execution: %s TORN", console2.log(
relayerStakedBalanceAfterExecution / _tornDecimals "Relayer balance in relayer registry contract before proposal execution: %s TORN",
); relayerStakedBalanceBeforeExecution / 10 ** 18
);
require(isRelayerRegisteredAfterExecution, "Relayer isn't registered after proposal execution");
require(relayerStakedBalanceBeforeExecution == relayerStakedBalanceAfterExecution, "Relayer stake balance differs after execution"); require(isRelayerRegisteredBeforeExecution, "Relayer not registered");
}
} createAndExecuteProposal();
bool isRelayerRegisteredAfterExecution = registry.isRelayer(TEST_RELAYER_ADDRESS);
uint256 relayerStakedBalanceAfterExecution = registry.getRelayerBalance(TEST_RELAYER_ADDRESS);
console2.log(
"Relayer balance in relayer registry contract after proposal execution: %s TORN",
relayerStakedBalanceAfterExecution / 10 ** 18
);
require(isRelayerRegisteredAfterExecution, "Relayer isn't registered after proposal execution");
require(
relayerStakedBalanceBeforeExecution == relayerStakedBalanceAfterExecution,
"Relayer stake balance differs after execution"
);
}
}

@ -21,7 +21,8 @@ contract TestProxyUpdating is MockProposal {
} }
function getUpgradeableProxyImplementationAddress(address proxy) internal view returns (address) { function getUpgradeableProxyImplementationAddress(address proxy) internal view returns (address) {
bytes32 proxyImplementationSlot = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc); bytes32 proxyImplementationSlot =
bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
return getAddressFromSlot(proxy, proxyImplementationSlot); return getAddressFromSlot(proxy, proxyImplementationSlot);
} }
@ -36,25 +37,36 @@ contract TestProxyUpdating is MockProposal {
function testGovernanceStakingProxyUpdated() public { function testGovernanceStakingProxyUpdated() public {
address stakingProxyAddressBeforeExecution = getStakingProxyAddress(); address stakingProxyAddressBeforeExecution = getStakingProxyAddress();
console2.log("Staking proxy address before proposal execution: %s", stakingProxyAddressBeforeExecution); console2.log(
"Staking proxy address before proposal execution: %s", stakingProxyAddressBeforeExecution
);
createAndExecuteProposal(); createAndExecuteProposal();
address stakingProxyAddressAfterExecution = getStakingProxyAddress(); address stakingProxyAddressAfterExecution = getStakingProxyAddress();
console2.log("Staking proxy address after proposal execution: %s", stakingProxyAddressAfterExecution); console2.log("Staking proxy address after proposal execution: %s", stakingProxyAddressAfterExecution);
require(stakingProxyAddressBeforeExecution != stakingProxyAddressAfterExecution, "Staking proxy address didn't changed"); require(
stakingProxyAddressBeforeExecution != stakingProxyAddressAfterExecution,
"Staking proxy address didn't changed"
);
require(isContract(stakingProxyAddressAfterExecution)); require(isContract(stakingProxyAddressAfterExecution));
} }
function testGovernanceStakingImplementationUpdated() public { function testGovernanceStakingImplementationUpdated() public {
address stakingImplementationAddressBeforeExecution = getStakingImplementationAddress(); address stakingImplementationAddressBeforeExecution = getStakingImplementationAddress();
console2.log("Staking implementation address before proposal execution: %s", stakingImplementationAddressBeforeExecution); console2.log(
"Staking implementation address before proposal execution: %s",
stakingImplementationAddressBeforeExecution
);
createAndExecuteProposal(); createAndExecuteProposal();
address stakingImplementationAddressAfterExecution = getStakingImplementationAddress(); address stakingImplementationAddressAfterExecution = getStakingImplementationAddress();
console2.log("Staking implementation address after proposal execution: %s", stakingImplementationAddressAfterExecution); console2.log(
"Staking implementation address after proposal execution: %s",
stakingImplementationAddressAfterExecution
);
require( require(
stakingImplementationAddressBeforeExecution != stakingImplementationAddressAfterExecution, stakingImplementationAddressBeforeExecution != stakingImplementationAddressAfterExecution,
@ -64,33 +76,45 @@ contract TestProxyUpdating is MockProposal {
} }
function testRelayerRegistryImplementationUpdated() public { function testRelayerRegistryImplementationUpdated() public {
address relayerRegistryImplementationAddressBeforeExecution = getRelayerRegistryImplementationAddress(); address relayerRegistryImplementationAddressBeforeExecution =
getRelayerRegistryImplementationAddress();
console2.log( console2.log(
"Relayer registry implementation address before proposal execution: %s", relayerRegistryImplementationAddressBeforeExecution "Relayer registry implementation address before proposal execution: %s",
relayerRegistryImplementationAddressBeforeExecution
); );
createAndExecuteProposal(); createAndExecuteProposal();
address relayerRegistryImplementationAddressAfterExecution = getRelayerRegistryImplementationAddress(); address relayerRegistryImplementationAddressAfterExecution = getRelayerRegistryImplementationAddress();
console2.log( console2.log(
"Relayer registry implementation address after proposal execution: %s", relayerRegistryImplementationAddressAfterExecution "Relayer registry implementation address after proposal execution: %s",
relayerRegistryImplementationAddressAfterExecution
); );
require( require(
relayerRegistryImplementationAddressBeforeExecution != relayerRegistryImplementationAddressAfterExecution, relayerRegistryImplementationAddressBeforeExecution
!= relayerRegistryImplementationAddressAfterExecution,
"Relayer registry implementation address didn't changed" "Relayer registry implementation address didn't changed"
); );
require(isContract(relayerRegistryImplementationAddressAfterExecution)); require(isContract(relayerRegistryImplementationAddressAfterExecution));
} }
function testGovernanceImplementationUpdated() public { function testGovernanceImplementationUpdated() public {
address governanceImplementationAddressBeforeExecution = getUpgradeableProxyImplementationAddress(_governanceAddress); address governanceImplementationAddressBeforeExecution =
console2.log("Governance implementation address before proposal execution: %s", governanceImplementationAddressBeforeExecution); getUpgradeableProxyImplementationAddress(_governanceAddress);
console2.log(
"Governance implementation address before proposal execution: %s",
governanceImplementationAddressBeforeExecution
);
createAndExecuteProposal(); createAndExecuteProposal();
address governanceImplementationAddressAfterExecution = getUpgradeableProxyImplementationAddress(_governanceAddress); address governanceImplementationAddressAfterExecution =
console2.log("Governance implementation address after proposal execution: %s", governanceImplementationAddressAfterExecution); getUpgradeableProxyImplementationAddress(_governanceAddress);
console2.log(
"Governance implementation address after proposal execution: %s",
governanceImplementationAddressAfterExecution
);
require( require(
governanceImplementationAddressBeforeExecution != governanceImplementationAddressAfterExecution, governanceImplementationAddressBeforeExecution != governanceImplementationAddressAfterExecution,
@ -101,12 +125,16 @@ contract TestProxyUpdating is MockProposal {
function testRelayerRegistryProxyNotUpdated() public { function testRelayerRegistryProxyNotUpdated() public {
address relayerRegistryProxyAddressBeforeExecution = getRelayerRegistryProxyAddress(); address relayerRegistryProxyAddressBeforeExecution = getRelayerRegistryProxyAddress();
console2.log("Relayer registry proxy before proposal execution: %s", relayerRegistryProxyAddressBeforeExecution); console2.log(
"Relayer registry proxy before proposal execution: %s", relayerRegistryProxyAddressBeforeExecution
);
createAndExecuteProposal(); createAndExecuteProposal();
address relayerRegistryProxyAddressAfterExecution = getRelayerRegistryProxyAddress(); address relayerRegistryProxyAddressAfterExecution = getRelayerRegistryProxyAddress();
console2.log("Relayer registry proxyafter proposal execution: %s", relayerRegistryProxyAddressAfterExecution); console2.log(
"Relayer registry proxyafter proposal execution: %s", relayerRegistryProxyAddressAfterExecution
);
require( require(
relayerRegistryProxyAddressBeforeExecution == relayerRegistryProxyAddressAfterExecution, relayerRegistryProxyAddressBeforeExecution == relayerRegistryProxyAddressAfterExecution,

@ -1,123 +1,127 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import { console2 } from "@forge-std/console2.sol"; import { console2 } from "@forge-std/console2.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { MockProposal } from "./MockProposal.sol"; import { MockProposal } from "./MockProposal.sol";
import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol"; import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol";
contract TestRelayerBalance is MockProposal { contract TestRelayerBalance is MockProposal {
address[] internal allRelayersAddresses = [ address[] internal allRelayersAddresses = [
0x20BB3095a4852F4c97d7A188E9f7183C85AcfC49, 0x20BB3095a4852F4c97d7A188E9f7183C85AcfC49,
0x47b03dF2145CC9Eed6d8819E02D25590F297C603, 0x47b03dF2145CC9Eed6d8819E02D25590F297C603,
0xBe4d1e137A24af091be80Ae58d652279665e3A27, 0xBe4d1e137A24af091be80Ae58d652279665e3A27,
0x18F516dD6D5F46b2875Fd822B994081274be2a8b, 0x18F516dD6D5F46b2875Fd822B994081274be2a8b,
0x49136693081f2c18E2cF14428dD78cd90A22dC1f, 0x49136693081f2c18E2cF14428dD78cd90A22dC1f,
0xA0F0287683E820FF4211e67C03cf46a87431f4E1, 0xA0F0287683E820FF4211e67C03cf46a87431f4E1,
0xD6187b4a0f51355A36764558D39b2C21aC12393D, 0xD6187b4a0f51355A36764558D39b2C21aC12393D,
0x2ca1a9D6c79367EA1eA481FC0A5e8C5BD6C62d25, 0x2ca1a9D6c79367EA1eA481FC0A5e8C5BD6C62d25,
0x9f9f98e28456EEEFC4Af1c990a170e2B0D2d6027, 0x9f9f98e28456EEEFC4Af1c990a170e2B0D2d6027,
0xb326d1F0837E14Ad265397800eF3Bf7a538335E4, 0xb326d1F0837E14Ad265397800eF3Bf7a538335E4,
0xB5cD48dD89C063B5a3Fe1BCC325364be62fc0f00, 0xB5cD48dD89C063B5a3Fe1BCC325364be62fc0f00,
0x0F75C6BFAF436Eba0cB977Dcdfb0F30b57ff9D05, 0x0F75C6BFAF436Eba0cB977Dcdfb0F30b57ff9D05,
0x6a2D058890ccA15BEaEe5050caaAd56B2aB54DD4, 0x6a2D058890ccA15BEaEe5050caaAd56B2aB54DD4,
0x62E142F218585827436f59997C301F7040396AD4, 0x62E142F218585827436f59997C301F7040396AD4,
0x85972458dfBf9269567b2a27C4ffC958A4f24761, 0x85972458dfBf9269567b2a27C4ffC958A4f24761,
0x078AD5DB2151083Ec16eCA1b26e2C98f79034DA8, 0x078AD5DB2151083Ec16eCA1b26e2C98f79034DA8,
0x3514Cfd42E4DDe9E65e283EbdBfa2888117823A6, 0x3514Cfd42E4DDe9E65e283EbdBfa2888117823A6,
0x550c9288310482F593602dD3e426603ae00BC352, 0x550c9288310482F593602dD3e426603ae00BC352,
0x3884e9b1E2b0f8D00666c9767B5602B709EeEE06, 0x3884e9b1E2b0f8D00666c9767B5602B709EeEE06,
0xc6C86Aa348Eaa0ef1b6F8Da90C279b670e67A55D, 0xc6C86Aa348Eaa0ef1b6F8Da90C279b670e67A55D,
0x9f340Bf3791809293DC50321Bd7F4c19120a98B6, 0x9f340Bf3791809293DC50321Bd7F4c19120a98B6,
0xF6CB46F9c2E34cD4B7f374D225e0aE5F474DdB32, 0xF6CB46F9c2E34cD4B7f374D225e0aE5F474DdB32,
0x076D4E32C6A5D888fC4658281539c94E778C796d, 0x076D4E32C6A5D888fC4658281539c94E778C796d,
0x28907F21F43B419F34226d6f10aCbCf1832b1D4d, 0x28907F21F43B419F34226d6f10aCbCf1832b1D4d,
0x6289C8a70EE2Ed6914834CaEa431F9a82c7eAf70, 0x6289C8a70EE2Ed6914834CaEa431F9a82c7eAf70,
0xE6B23CBae6a62f4b52A021B76E7811522eb82055, 0xE6B23CBae6a62f4b52A021B76E7811522eb82055,
0xAF02873D7dF5f3e6D5fF42F622F4e138A68208e7, 0xAF02873D7dF5f3e6D5fF42F622F4e138A68208e7,
0xb9C612760dC5456e5979393Cfe4AB1fF270AE9e5, 0xb9C612760dC5456e5979393Cfe4AB1fF270AE9e5,
0xa56963fe9F46C758B2D0616A754346A8F9eba30b, 0xa56963fe9F46C758B2D0616A754346A8F9eba30b,
0x56be1F8196cC4AefCe3348E679a2008496D14473, 0x56be1F8196cC4AefCe3348E679a2008496D14473,
0x63606C4011e97a73BCd844Cde6a38D45a728BC0E, 0x63606C4011e97a73BCd844Cde6a38D45a728BC0E,
0xE939c61Acd8bD30366435C6B1033251117851b03, 0xE939c61Acd8bD30366435C6B1033251117851b03,
0x3e9979106DA74AFB64b866218AeB47F224A312bb, 0x3e9979106DA74AFB64b866218AeB47F224A312bb,
0x28f1a9b8e3941C0909059eB84E5834154A99E0fC, 0x28f1a9b8e3941C0909059eB84E5834154A99E0fC,
0x9c8C81f3F5C19DFfeE7257Dd7477b8ef6E405e82, 0x9c8C81f3F5C19DFfeE7257Dd7477b8ef6E405e82,
0x644D4f3b293a7fc86eB4EFB6Bd2439f7603C991D, 0x644D4f3b293a7fc86eB4EFB6Bd2439f7603C991D,
0x25De357c61c9f2711A605b66E83887BA5Fd22ac1, 0x25De357c61c9f2711A605b66E83887BA5Fd22ac1,
0x78c88fF43cd503316e8A15B6d92b2EBFa73802B2, 0x78c88fF43cd503316e8A15B6d92b2EBFa73802B2,
0x2C42550Ff1Bdc139b54C5042a9a86A56398E9d83, 0x2C42550Ff1Bdc139b54C5042a9a86A56398E9d83,
0xf18673Ab6Eb72937607aA8388b8f7aa0AC3a0D32, 0xf18673Ab6Eb72937607aA8388b8f7aa0AC3a0D32,
0x3a1d526D09b7E59Fd88De4726f68A8246dDC2742, 0x3a1d526D09b7E59Fd88De4726f68A8246dDC2742,
0x7Ba6781620c91676B070D319E7E894BFd4A9eC81, 0x7Ba6781620c91676B070D319E7E894BFd4A9eC81,
0x9Ffbd3f9eE795A4fDa880ED553A2A4BD6D45CE5B, 0x9Ffbd3f9eE795A4fDa880ED553A2A4BD6D45CE5B,
0xe6184DA55174Cc0263a17eA2fc24E48511766505, 0xe6184DA55174Cc0263a17eA2fc24E48511766505,
0x36989535F0290eaC96692675cbf15a3BD2f42E46, 0x36989535F0290eaC96692675cbf15a3BD2f42E46,
0x12D92FeD171F16B3a05ACB1542B40648E7CEd384, 0x12D92FeD171F16B3a05ACB1542B40648E7CEd384,
0x08657a1f4C1F06d657F31767831421EE7FaDf549, 0x08657a1f4C1F06d657F31767831421EE7FaDf549,
0x42FecB4137aFF76E0E85702ff4F339DbFe6D859E, 0x42FecB4137aFF76E0E85702ff4F339DbFe6D859E,
0xc6e531CF18afE3a64bE19e40ac410f39FC9738da, 0xc6e531CF18afE3a64bE19e40ac410f39FC9738da,
0x9Ee26a4bFd731E8e742B65bF955814EADdd7F151, 0x9Ee26a4bFd731E8e742B65bF955814EADdd7F151,
0x7E3893725d4e238B4c8c83375bBAd024a66Ffa42, 0x7E3893725d4e238B4c8c83375bBAd024a66Ffa42,
0x465403d43f48Dfaa3F9385B60F0fEa36c360C18A, 0x465403d43f48Dfaa3F9385B60F0fEa36c360C18A,
0xc7285e85a6D11C762A7D9C57aC38E31A671E9777, 0xc7285e85a6D11C762A7D9C57aC38E31A671E9777,
0x74b6ea6B2EeFd3eF4da5E8c4C0480776035029c2, 0x74b6ea6B2EeFd3eF4da5E8c4C0480776035029c2,
0x14812AE927e2BA5aA0c0f3C0eA016b3039574242, 0x14812AE927e2BA5aA0c0f3C0eA016b3039574242,
0xdc957b6a3F630bEf2E6104C1a22dAeF9650b5349, 0xdc957b6a3F630bEf2E6104C1a22dAeF9650b5349,
0x1247749d7E28D357B4279110af0802603AC526cE, 0x1247749d7E28D357B4279110af0802603AC526cE,
0x1036AF02bCDb2e3A4db2d3D40b29e5054EDc79BA, 0x1036AF02bCDb2e3A4db2d3D40b29e5054EDc79BA,
0x3665B1E938Ce90c48502303ACB5049Fb065D3a85, 0x3665B1E938Ce90c48502303ACB5049Fb065D3a85,
0x87BeDf6AD81A2907633Ab68D02c44f0415bc68C1, 0x87BeDf6AD81A2907633Ab68D02c44f0415bc68C1,
0x0B45840cCEE39aeEfFDF621633d24AA8930B834c, 0x0B45840cCEE39aeEfFDF621633d24AA8930B834c,
0xcBD78860218160F4b463612f30806807Fe6E804C, 0xcBD78860218160F4b463612f30806807Fe6E804C,
0xa42303EE9B2eC1DB7E2a86Ed6C24AF7E49E9e8B9, 0xa42303EE9B2eC1DB7E2a86Ed6C24AF7E49E9e8B9,
0xa0109274F53609f6Be97ec5f3052C659AB80f012, 0xa0109274F53609f6Be97ec5f3052C659AB80f012,
0xb578603D3fB9216158c29488c1A902Dd0300c115, 0xb578603D3fB9216158c29488c1A902Dd0300c115,
0x7b81b8680b1abd1e2E983a1589DeB5468B50A544, 0x7b81b8680b1abd1e2E983a1589DeB5468B50A544,
0x4750BCfcC340AA4B31be7e71fa072716d28c29C5, 0x4750BCfcC340AA4B31be7e71fa072716d28c29C5,
0x36DD7b862746fdD3eDd3577c8411f1B76FDC2Af5, 0x36DD7b862746fdD3eDd3577c8411f1B76FDC2Af5,
0x0D13F55BA1509352F9e36190d948D7c45B854Be2, 0x0D13F55BA1509352F9e36190d948D7c45B854Be2,
0x4803c6ec3E61cD1bb1735bBDdB21732100AA13cc, 0x4803c6ec3E61cD1bb1735bBDdB21732100AA13cc,
0x1ee815AD4a914c2C2f4650b3ED34978F8Fe2fcC4, 0x1ee815AD4a914c2C2f4650b3ED34978F8Fe2fcC4,
0x04843E2C74018c8d94f1834a7ccB94c16691E451, 0x04843E2C74018c8d94f1834a7ccB94c16691E451,
0x0000208a6cC0299dA631C08fE8c2EDe435Ea83B8, 0x0000208a6cC0299dA631C08fE8c2EDe435Ea83B8,
0x853281B7676DFB66B87e2f26c9cB9D10Ce883F37, 0x853281B7676DFB66B87e2f26c9cB9D10Ce883F37,
0xaaaaD0b504B4CD22348C4Db1071736646Aa314C6, 0xaaaaD0b504B4CD22348C4Db1071736646Aa314C6,
0x5a0cB6505B3b99dD4035bb1Ac43cC51202d4e29F, 0x5a0cB6505B3b99dD4035bb1Ac43cC51202d4e29F,
0x7171717171866B60cc1A76A058ae20C8F703AE05, 0x7171717171866B60cc1A76A058ae20C8F703AE05,
0x30F96AEF199B399B722F8819c9b0723016CEAe6C, 0x30F96AEF199B399B722F8819c9b0723016CEAe6C,
0xEFa22d23de9f293B11e0c4aC865d7b440647587a, 0xEFa22d23de9f293B11e0c4aC865d7b440647587a,
0xC0F12799B8D3FA8810DfE1616095170C72117F8F, 0xC0F12799B8D3FA8810DfE1616095170C72117F8F,
0x996ad81FD83eD7A87FD3D03694115dff19db0B3b, 0x996ad81FD83eD7A87FD3D03694115dff19db0B3b,
0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe, 0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe,
0x15980A3Bd6ed317f42d2eD0DCf3d3D730b6Bc0C5, 0x15980A3Bd6ed317f42d2eD0DCf3d3D730b6Bc0C5,
0x7853E027F37830790685622cdd8685fF0c8255A2, 0x7853E027F37830790685622cdd8685fF0c8255A2,
0xf0D9b969925116074eF43e7887Bcf035Ff1e7B19, 0xf0D9b969925116074eF43e7887Bcf035Ff1e7B19,
0x97096F56B09F6aaA4230eec3BA33249995690B0E, 0x97096F56B09F6aaA4230eec3BA33249995690B0E,
0x5555555731006f71f121144534Ca7C8799F66AA3, 0x5555555731006f71f121144534Ca7C8799F66AA3,
0x5007565e69E5c23C278c2e976beff38eF4D27B3d, 0x5007565e69E5c23C278c2e976beff38eF4D27B3d,
0x2ffAc4D796261ba8964d859867592B952b9FC158, 0x2ffAc4D796261ba8964d859867592B952b9FC158,
0xCEdac436cEA98E93F471331eCC693fF41D730921, 0xCEdac436cEA98E93F471331eCC693fF41D730921,
0x94596B6A626392F5D972D6CC4D929a42c2f0008c, 0x94596B6A626392F5D972D6CC4D929a42c2f0008c,
0x065f2A0eF62878e8951af3c387E4ddC944f1B8F4, 0x065f2A0eF62878e8951af3c387E4ddC944f1B8F4,
0xe7c490986FC34248F77b813eD6C8971e76e0384C, 0xe7c490986FC34248F77b813eD6C8971e76e0384C,
0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c 0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c
]; ];
function testRelayerBalancesSum() public view { function testRelayerBalancesSum() public view {
RelayerRegistry registry = RelayerRegistry(getRelayerRegistryProxyAddress()); RelayerRegistry registry = RelayerRegistry(getRelayerRegistryProxyAddress());
uint256 relayersBalancesSum; uint256 relayersBalancesSum;
for (uint256 i = 0; i < allRelayersAddresses.length; i++) { for (uint256 i = 0; i < allRelayersAddresses.length; i++) {
uint256 currentRelayerBalance = registry.getRelayerBalance(allRelayersAddresses[i]); uint256 currentRelayerBalance = registry.getRelayerBalance(allRelayersAddresses[i]);
relayersBalancesSum += currentRelayerBalance; relayersBalancesSum += currentRelayerBalance;
console2.log("Relayer %s, relayer balance: %s TORN", allRelayersAddresses[i], currentRelayerBalance / _tornDecimals); console2.log(
} "Relayer %s, relayer balance: %s TORN", allRelayersAddresses[i], currentRelayerBalance / 10e17
);
console2.log("\nSum of relayer balances on block %s: %s TORN", block.number, relayersBalancesSum / _tornDecimals); }
require(relayersBalancesSum > 0); console2.log(
} "\nSum of relayer balances on block %s: %s TORN", block.number, relayersBalancesSum / 10e17
} );
require(relayersBalancesSum > 0);
}
}

@ -1,100 +1,130 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.6.12; pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import { console2 } from "@forge-std/console2.sol"; import { console2 } from "@forge-std/console2.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { MockProposal } from "./MockProposal.sol"; import { MockProposal } from "./MockProposal.sol";
import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol"; import { GovernancePatchUpgrade } from "@root/v4-patch/GovernancePatchUpgrade.sol";
import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol"; import { RelayerRegistry } from "@root/v4-patch/RelayerRegistry.sol";
import { TornadoStakingRewards } from "@root/v4-patch/TornadoStakingRewards.sol"; import { TornadoStakingRewards } from "@root/v4-patch/TornadoStakingRewards.sol";
contract TestGovernanceStakingRewards is MockProposal { contract TestGovernanceStakingRewards is MockProposal {
function burnTokens(address caller, uint256 amount, TornadoStakingRewards staking) internal { modifier replenishGovernanceStakingBalanceBefore() {
vm.startPrank(caller); vm.startPrank(_governanceAddress);
staking.addBurnRewards(amount); IERC20(_tokenAddress).transfer(getStakingProxyAddress(), 100_000 ether);
vm.stopPrank(); vm.stopPrank();
} _;
}
function testAccumulatedRewardCanBeUpdated() public executeAttackerProposalBefore executeCurrentProposalBefore {
TornadoStakingRewards staking = TornadoStakingRewards(getStakingProxyAddress()); function burnTokens(address caller, uint256 amount, TornadoStakingRewards staking) internal {
vm.startPrank(caller);
uint256 accumulatedRewardPerTornBeforeBurning = staking.accumulatedRewardPerTorn() / _tornMaximumSupply; staking.addBurnRewards(amount);
vm.stopPrank();
console2.log( }
"Accumulated reward per TORN right after proposal execution: %s TORN", accumulatedRewardPerTornBeforeBurning / _tornDecimals
); function testAccumulatedRewardCanBeUpdated()
public
burnTokens(_governanceAddress, 10_000_000 ether, staking); executeAttackerProposalBefore
executeCurrentProposalBefore
uint256 accumulatedRewardPerTornAfterBurning = staking.accumulatedRewardPerTorn() / _tornMaximumSupply; {
TornadoStakingRewards staking = TornadoStakingRewards(getStakingProxyAddress());
console2.log(
"Accumulated reward per TORN after burning 10 000 000 TORN: ~ %s TORN", accumulatedRewardPerTornAfterBurning / _tornDecimals uint256 accumulatedRewardPerTornBeforeBurning =
); staking.accumulatedRewardPerTorn() / _tornMaximumSupply;
require(accumulatedRewardPerTornAfterBurning > accumulatedRewardPerTornBeforeBurning, "Staking rewards isn't updated"); console2.log(
} "Accumulated reward per TORN right after proposal execution: %s TORN",
accumulatedRewardPerTornBeforeBurning / 10e17
function testStakerCanGetRewards() public executeAttackerProposalBefore executeCurrentProposalBefore { );
TornadoStakingRewards staking = TornadoStakingRewards(getStakingProxyAddress());
IERC20 TORN = IERC20(_tokenAddress); burnTokens(_governanceAddress, 10_000_000 ether, staking);
uint256 toBurn = 10_000 ether; uint256 accumulatedRewardPerTornAfterBurning = staking.accumulatedRewardPerTorn() / _tornMaximumSupply;
// Remind that we have locked in Governance 25 000 TORN for TEST_ADDRESS_ONE while voting console2.log(
uint256 stakerLockedBalance = governance.lockedBalance(TEST_ADDRESS_ONE); "Accumulated reward per TORN after burning 10 000 000 TORN: ~ %s TORN",
require(stakerLockedBalance == 25_000 ether, "Invalid test staker locked balance"); accumulatedRewardPerTornAfterBurning / 10e17
);
uint256 stakerRewardsBeforeBurning = staking.checkReward(TEST_ADDRESS_ONE);
console2.log("Staking rewards before burning: %s TORN", stakerRewardsBeforeBurning / _tornDecimals); require(
accumulatedRewardPerTornAfterBurning > accumulatedRewardPerTornBeforeBurning,
burnTokens(_governanceAddress, toBurn, staking); "Staking rewards isn't updated"
);
uint256 stakerRewardsAfterBurning = staking.checkReward(TEST_ADDRESS_ONE); }
console2.log("Staking rewards after burning 10 000 TORN: %s TORN\n", stakerRewardsAfterBurning / _tornDecimals);
require(stakerRewardsAfterBurning > stakerRewardsBeforeBurning, "Rewards isn't changed after burning"); function testStakerCanGetRewards()
public
// All TORN, locked by users in Governance, is on the userVault contract balance executeAttackerProposalBefore
uint256 governanceLockedAmount = TORN.balanceOf(address(governance.userVault())); executeCurrentProposalBefore
uint256 receivedReward = stakerRewardsAfterBurning - stakerRewardsBeforeBurning; replenishGovernanceStakingBalanceBefore
uint256 expectedRewards = stakerLockedBalance * toBurn / governanceLockedAmount; {
TornadoStakingRewards staking = TornadoStakingRewards(getStakingProxyAddress());
console2.log("Expected staking rewards: %s TORN", expectedRewards / _tornDecimals); IERC20 TORN = IERC20(_tokenAddress);
console2.log("Staker received rewards: %s TORN\n", receivedReward / _tornDecimals);
uint256 toBurn = 10_000 ether;
require(receivedReward == expectedRewards, "Expected and received rewards don't match");
// Remind that we have locked in Governance 25 000 TORN for TEST_ADDRESS_ONE while voting
uint256 stakerTORNbalanceBeforeGettingRewards = TORN.balanceOf(TEST_ADDRESS_ONE); uint256 stakerLockedBalance = governance.lockedBalance(TEST_ADDRESS_ONE);
console2.log( require(stakerLockedBalance == 25_000 ether, "Invalid test staker locked balance");
"Staker balance before getting (withdrawal) collected rewards: %s TORN", stakerTORNbalanceBeforeGettingRewards / _tornDecimals
); uint256 stakerRewardsBeforeBurning = staking.checkReward(TEST_ADDRESS_ONE);
console2.log("Staking rewards before burning: %s TORN", stakerRewardsBeforeBurning / 10e17);
vm.startPrank(TEST_ADDRESS_ONE);
staking.getReward(); burnTokens(_governanceAddress, toBurn, staking);
vm.stopPrank();
uint256 stakerRewardsAfterBurning = staking.checkReward(TEST_ADDRESS_ONE);
uint256 stakerTORNbalanceAfterGettingRewards = TORN.balanceOf(TEST_ADDRESS_ONE); console2.log("Staking rewards after burning 10 000 TORN: %s TORN", stakerRewardsAfterBurning / 10e17);
console2.log( require(stakerRewardsAfterBurning > stakerRewardsBeforeBurning, "Rewards isn't changed after burning");
"Staker balance after getting (withdrawal) collected rewards: %s TORN", stakerTORNbalanceAfterGettingRewards / _tornDecimals
); // All TORN, locked by users in Governance, is on the userVault contract balance
uint256 governanceLockedAmount = TORN.balanceOf(address(governance.userVault()));
require(stakerTORNbalanceAfterGettingRewards > stakerTORNbalanceBeforeGettingRewards, "Rewards isn't withdrawed"); uint256 receivedReward = stakerRewardsAfterBurning - stakerRewardsBeforeBurning;
require( uint256 expectedRewards = stakerLockedBalance * toBurn / governanceLockedAmount;
stakerTORNbalanceAfterGettingRewards - stakerTORNbalanceBeforeGettingRewards == receivedReward,
"Incorrect rewards amount withdrawed" console2.log("Expected staking rewards: %s TORN", expectedRewards / 10e17);
); console2.log("Staker received rewards: %s TORN", receivedReward / 10e17);
}
require(receivedReward == expectedRewards, "Expected and received rewards don't match");
function testCanBurnFromRelayerRegistry() public executeAttackerProposalBefore executeCurrentProposalBefore {
TornadoStakingRewards staking = TornadoStakingRewards(getStakingProxyAddress()); uint256 stakerTORNbalanceBeforeGettingRewards = TORN.balanceOf(TEST_ADDRESS_ONE);
address relayerRegistryAddress = getRelayerRegistryProxyAddress(); console2.log(
"Staker balance before getting (withdrawal) collected rewards: %s TORN",
uint256 accumulatedRewardPerTornBeforeBurning = staking.accumulatedRewardPerTorn(); stakerTORNbalanceBeforeGettingRewards / 10e17
);
burnTokens(relayerRegistryAddress, 10_000 ether, staking);
vm.startPrank(TEST_ADDRESS_ONE);
require(staking.accumulatedRewardPerTorn() > accumulatedRewardPerTornBeforeBurning, "Burn from relayer registry failed"); staking.getReward();
} vm.stopPrank();
}
uint256 stakerTORNbalanceAfterGettingRewards = TORN.balanceOf(TEST_ADDRESS_ONE);
console2.log(
"Staker balance after getting (withdrawal) collected rewards: %s TORN",
stakerTORNbalanceAfterGettingRewards / 10e17
);
require(
stakerTORNbalanceAfterGettingRewards > stakerTORNbalanceBeforeGettingRewards,
"Rewards isn't withdrawed"
);
require(
stakerTORNbalanceAfterGettingRewards - stakerTORNbalanceBeforeGettingRewards == receivedReward,
"Incorrect rewards amount withdrawed"
);
}
function testCanBurnFromRelayerRegistry() public {
TornadoStakingRewards staking = TornadoStakingRewards(getStakingProxyAddress());
address relayerRegistryAddress = getRelayerRegistryProxyAddress();
uint256 accumulatedRewardPerTornBeforeBurning = staking.accumulatedRewardPerTorn();
burnTokens(relayerRegistryAddress, 10_000 ether, staking);
require(
staking.accumulatedRewardPerTorn() > accumulatedRewardPerTornBeforeBurning,
"Burn from relayer registry failed"
);
}
}

@ -7,7 +7,5 @@ contract Parameters {
address constant _governanceAddress = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce; address constant _governanceAddress = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce;
address constant _governanceVaultAddress = 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185; address constant _governanceVaultAddress = 0x2F50508a8a3D323B91336FA3eA6ae50E55f32185;
address constant _tokenAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; address constant _tokenAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
uint256 constant _tornMaximumSupply = 10_000_000; uint256 constant _tornMaximumSupply = 10_000_000;
uint256 constant _tornDecimals = 1e18;
} }

5893
yarn.lock

File diff suppressed because it is too large Load Diff