156 lines
6.2 KiB
Solidity
156 lines
6.2 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.6.12;
|
|
pragma experimental ABIEncoderV2;
|
|
|
|
import { GovernanceVaultUpgrade } from "./GovernanceVaultUpgrade.sol";
|
|
import { GasCompensator } from "./GasCompensator.sol";
|
|
import { Math } from "@openzeppelin/contracts/math/Math.sol";
|
|
|
|
/**
|
|
* @notice This contract should upgrade governance to be able to compensate gas for certain actions.
|
|
* These actions are set to castVote, castDelegatedVote in this contract.
|
|
* */
|
|
contract GovernanceGasUpgrade is GovernanceVaultUpgrade, GasCompensator {
|
|
/**
|
|
* @notice constructor
|
|
* @param _gasCompLogic gas compensation vault address
|
|
* @param _userVault tornado vault address
|
|
* */
|
|
constructor(address _gasCompLogic, address _userVault)
|
|
public
|
|
GovernanceVaultUpgrade(_userVault)
|
|
GasCompensator(_gasCompLogic)
|
|
{}
|
|
|
|
/// @notice check that msg.sender is multisig
|
|
modifier onlyMultisig() {
|
|
require(msg.sender == returnMultisigAddress(), "only multisig");
|
|
_;
|
|
}
|
|
|
|
/**
|
|
* @notice receive ether function, does nothing but receive ether
|
|
* */
|
|
receive() external payable {}
|
|
|
|
/**
|
|
* @notice function to add a certain amount of ether for gas compensations
|
|
* @dev send ether is used in the logic as we don't expect multisig to make a reentrancy attack on governance
|
|
* @param gasCompensationsLimit the amount of gas to be compensated
|
|
* */
|
|
function setGasCompensations(uint256 gasCompensationsLimit) external virtual override onlyMultisig {
|
|
require(payable(address(gasCompensationVault)).send(Math.min(gasCompensationsLimit, address(this).balance)));
|
|
}
|
|
|
|
/**
|
|
* @notice function to withdraw funds from the gas compensator
|
|
* @dev send ether is used in the logic as we don't expect multisig to make a reentrancy attack on governance
|
|
* @param amount the amount of ether to withdraw
|
|
* */
|
|
function withdrawFromHelper(uint256 amount) external virtual override onlyMultisig {
|
|
gasCompensationVault.withdrawToGovernance(amount);
|
|
}
|
|
|
|
/**
|
|
* @notice function to cast callers votes on a proposal
|
|
* @dev IMPORTANT: This function uses the gasCompensation modifier.
|
|
* as such this function can trigger a payable fallback.
|
|
It is not possible to vote without revert more than once,
|
|
without hasAccountVoted being true, eliminating gas refunds in this case.
|
|
Gas compensation is also using the low level send(), forwarding 23000 gas
|
|
as to disallow further logic execution above that threshold.
|
|
* @param proposalId id of proposal account is voting on
|
|
* @param support true if yes false if no
|
|
* */
|
|
function castVote(uint256 proposalId, bool support)
|
|
external
|
|
virtual
|
|
override
|
|
gasCompensation(
|
|
msg.sender,
|
|
!hasAccountVoted(proposalId, msg.sender) && !checkIfQuorumReached(proposalId),
|
|
(msg.sender == tx.origin ? 21e3 : 0)
|
|
)
|
|
{
|
|
_castVote(msg.sender, proposalId, support);
|
|
}
|
|
|
|
/**
|
|
* @notice function to cast callers votes and votes delegated to the caller
|
|
* @param from array of addresses that should have delegated to voter
|
|
* @param proposalId id of proposal account is voting on
|
|
* @param support true if yes false if no
|
|
* */
|
|
function castDelegatedVote(
|
|
address[] memory from,
|
|
uint256 proposalId,
|
|
bool support
|
|
) external virtual override {
|
|
require(from.length > 0, "Can not be empty");
|
|
_castDelegatedVote(from, proposalId, support, !hasAccountVoted(proposalId, msg.sender) && !checkIfQuorumReached(proposalId));
|
|
}
|
|
|
|
/// @notice checker for success on deployment
|
|
/// @return returns precise version of governance
|
|
function version() external pure virtual override returns (string memory) {
|
|
return "2.lottery-and-gas-upgrade";
|
|
}
|
|
|
|
/**
|
|
* @notice function to check if quorum has been reached on a given proposal
|
|
* @param proposalId id of proposal
|
|
* @return true if quorum has been reached
|
|
* */
|
|
function checkIfQuorumReached(uint256 proposalId) public view returns (bool) {
|
|
return (proposals[proposalId].forVotes + proposals[proposalId].againstVotes >= QUORUM_VOTES);
|
|
}
|
|
|
|
/**
|
|
* @notice function to check if account has voted on a proposal
|
|
* @param proposalId id of proposal account should have voted on
|
|
* @param account address of the account
|
|
* @return true if acc has voted
|
|
* */
|
|
function hasAccountVoted(uint256 proposalId, address account) public view returns (bool) {
|
|
return proposals[proposalId].receipts[account].hasVoted;
|
|
}
|
|
|
|
/**
|
|
* @notice function to retrieve the multisig address
|
|
* @dev reasoning: if multisig changes we need governance to approve the next multisig address,
|
|
* so simply inherit in a governance upgrade from this function and set the new address
|
|
* @return the multisig address
|
|
* */
|
|
function returnMultisigAddress() public pure virtual returns (address) {
|
|
return 0xb04E030140b30C27bcdfaafFFA98C57d80eDa7B4;
|
|
}
|
|
|
|
/**
|
|
* @notice This should handle the logic of the external function
|
|
* @dev IMPORTANT: This function uses the gasCompensation modifier.
|
|
* as such this function can trigger a payable fallback.
|
|
* It is not possible to vote without revert more than once,
|
|
* without hasAccountVoted being true, eliminating gas refunds in this case.
|
|
* Gas compensation is also using the low level send(), forwarding 23000 gas
|
|
* as to disallow further logic execution above that threshold.
|
|
* @param from array of addresses that should have delegated to voter
|
|
* @param proposalId id of proposal account is voting on
|
|
* @param support true if yes false if no
|
|
* @param gasCompensated true if gas should be compensated (given all internal checks pass)
|
|
* */
|
|
function _castDelegatedVote(
|
|
address[] memory from,
|
|
uint256 proposalId,
|
|
bool support,
|
|
bool gasCompensated
|
|
) internal gasCompensation(msg.sender, gasCompensated, (msg.sender == tx.origin ? 21e3 : 0)) {
|
|
for (uint256 i = 0; i < from.length; i++) {
|
|
address delegator = from[i];
|
|
require(delegatedTo[delegator] == msg.sender || delegator == msg.sender, "Governance: not authorized");
|
|
require(!gasCompensated || !hasAccountVoted(proposalId, delegator), "Governance: voted already");
|
|
_castVote(delegator, proposalId, support);
|
|
}
|
|
}
|
|
}
|