mirror of
https://github.com/Instadapp/dsa-governance.git
synced 2024-07-29 22:27:52 +00:00
Add governance contracts (Bravo)
This commit is contained in:
parent
c2fa854c7b
commit
f6596fd68a
393
contracts/GovernorBravoDelegate.sol
Normal file
393
contracts/GovernorBravoDelegate.sol
Normal file
|
|
@ -0,0 +1,393 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import {
|
||||||
|
GovernorBravoDelegateStorageV1,
|
||||||
|
GovernorBravoEvents,
|
||||||
|
TimelockInterface,
|
||||||
|
TokenInterface
|
||||||
|
} from "./GovernorBravoInterfaces.sol";
|
||||||
|
import { SafeMath } from "./SafeMath.sol";
|
||||||
|
|
||||||
|
contract GovernorBravoDelegate is GovernorBravoDelegateStorageV1, GovernorBravoEvents {
|
||||||
|
/// @notice The name of this contract
|
||||||
|
string public constant name = "DSL Governor Bravo";
|
||||||
|
|
||||||
|
/// @notice The minimum setable proposal threshold
|
||||||
|
uint public constant MIN_PROPOSAL_THRESHOLD = 50000e18; // TODO - Update this
|
||||||
|
|
||||||
|
/// @notice The maximum setable proposal threshold
|
||||||
|
uint public constant MAX_PROPOSAL_THRESHOLD = 100000e18; // TODO - Update this
|
||||||
|
|
||||||
|
/// @notice The minimum setable voting period
|
||||||
|
uint public constant MIN_VOTING_PERIOD = 5760; // About 24 hours
|
||||||
|
|
||||||
|
/// @notice The max setable voting period
|
||||||
|
uint public constant MAX_VOTING_PERIOD = 80640; // About 2 weeks
|
||||||
|
|
||||||
|
/// @notice The min setable voting delay
|
||||||
|
uint public constant MIN_VOTING_DELAY = 1;
|
||||||
|
|
||||||
|
/// @notice The max setable voting delay
|
||||||
|
uint public constant MAX_VOTING_DELAY = 40320; // About 1 week
|
||||||
|
|
||||||
|
/// @notice The number of votes in support of a proposal required in order for a quorum to be reached and for a vote to succeed
|
||||||
|
uint public constant quorumVotes = 400000e18; // TODO - Update this
|
||||||
|
|
||||||
|
/// @notice The maximum number of actions that can be included in a proposal
|
||||||
|
uint public constant proposalMaxOperations = 10; // 10 actions
|
||||||
|
|
||||||
|
/// @notice The EIP-712 typehash for the contract's domain
|
||||||
|
bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
|
||||||
|
|
||||||
|
/// @notice The EIP-712 typehash for the ballot struct used by the contract
|
||||||
|
bytes32 public constant BALLOT_TYPEHASH = keccak256("Ballot(uint256 proposalId,uint8 support)");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Used to initialize the contract during delegator contructor
|
||||||
|
* @param timelock_ The address of the Timelock
|
||||||
|
* @param token_ The address of the DSL Governance Token
|
||||||
|
* @param votingPeriod_ The initial voting period
|
||||||
|
* @param votingDelay_ The initial voting delay
|
||||||
|
* @param proposalThreshold_ The initial proposal threshold
|
||||||
|
*/
|
||||||
|
function initialize(address timelock_, address token_, uint votingPeriod_, uint votingDelay_, uint proposalThreshold_) public {
|
||||||
|
require(address(timelock) == address(0), "GovernorBravo::initialize: can only initialize once");
|
||||||
|
require(msg.sender == admin, "GovernorBravo::initialize: admin only");
|
||||||
|
require(timelock_ != address(0), "GovernorBravo::initialize: invalid timelock address");
|
||||||
|
require(token_ != address(0), "GovernorBravo::initialize: invalid comp address");
|
||||||
|
require(votingPeriod_ >= MIN_VOTING_PERIOD && votingPeriod_ <= MAX_VOTING_PERIOD, "GovernorBravo::initialize: invalid voting period");
|
||||||
|
require(votingDelay_ >= MIN_VOTING_DELAY && votingDelay_ <= MAX_VOTING_DELAY, "GovernorBravo::initialize: invalid voting delay");
|
||||||
|
require(proposalThreshold_ >= MIN_PROPOSAL_THRESHOLD && proposalThreshold_ <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::initialize: invalid proposal threshold");
|
||||||
|
|
||||||
|
timelock = TimelockInterface(timelock_);
|
||||||
|
token = TokenInterface(token_);
|
||||||
|
votingPeriod = votingPeriod_;
|
||||||
|
votingDelay = votingDelay_;
|
||||||
|
proposalThreshold = proposalThreshold_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Function used to propose a new proposal. Sender must have delegates above the proposal threshold
|
||||||
|
* @param targets Target addresses for proposal calls
|
||||||
|
* @param values Eth values for proposal calls
|
||||||
|
* @param signatures Function signatures for proposal calls
|
||||||
|
* @param calldatas Calldatas for proposal calls
|
||||||
|
* @param description String description of the proposal
|
||||||
|
* @return Proposal id of new proposal
|
||||||
|
*/
|
||||||
|
function propose(address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description) public returns (uint) {
|
||||||
|
require(token.getPriorVotes(msg.sender, SafeMath.sub(block.number, 1)) > proposalThreshold, "GovernorBravo::propose: proposer votes below proposal threshold");
|
||||||
|
require(targets.length == values.length && targets.length == signatures.length && targets.length == calldatas.length, "GovernorBravo::propose: proposal function information arity mismatch");
|
||||||
|
require(targets.length != 0, "GovernorBravo::propose: must provide actions");
|
||||||
|
require(targets.length <= proposalMaxOperations, "GovernorBravo::propose: too many actions");
|
||||||
|
|
||||||
|
uint latestProposalId = latestProposalIds[msg.sender];
|
||||||
|
if (latestProposalId != 0) {
|
||||||
|
ProposalState proposersLatestProposalState = state(latestProposalId);
|
||||||
|
require(proposersLatestProposalState != ProposalState.Active, "GovernorBravo::propose: one live proposal per proposer, found an already active proposal");
|
||||||
|
require(proposersLatestProposalState != ProposalState.Pending, "GovernorBravo::propose: one live proposal per proposer, found an already pending proposal");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint startBlock = SafeMath.add(block.number, votingDelay);
|
||||||
|
uint endBlock = SafeMath.add(startBlock, votingPeriod);
|
||||||
|
|
||||||
|
proposalCount++;
|
||||||
|
// Proposal memory newProposal = Proposal({
|
||||||
|
// id: proposalCount,
|
||||||
|
// proposer: msg.sender,
|
||||||
|
// eta: 0,
|
||||||
|
// targets: targets,
|
||||||
|
// values: values,
|
||||||
|
// signatures: signatures,
|
||||||
|
// calldatas: calldatas,
|
||||||
|
// startBlock: startBlock,
|
||||||
|
// endBlock: endBlock,
|
||||||
|
// forVotes: 0,
|
||||||
|
// againstVotes: 0,
|
||||||
|
// abstainVotes: 0,
|
||||||
|
// canceled: false,
|
||||||
|
// executed: false
|
||||||
|
// });
|
||||||
|
|
||||||
|
Proposal storage newProposal = proposals[proposalCount];
|
||||||
|
|
||||||
|
newProposal.id = proposalCount;
|
||||||
|
newProposal.proposer = msg.sender;
|
||||||
|
newProposal.targets = targets;
|
||||||
|
newProposal.values = values;
|
||||||
|
newProposal.signatures = signatures;
|
||||||
|
newProposal.calldatas = calldatas;
|
||||||
|
newProposal.startBlock = startBlock;
|
||||||
|
newProposal.endBlock = endBlock;
|
||||||
|
|
||||||
|
latestProposalIds[newProposal.proposer] = proposalCount;
|
||||||
|
|
||||||
|
emit ProposalCreated(proposalCount, msg.sender, targets, values, signatures, calldatas, startBlock, endBlock, description);
|
||||||
|
return proposalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Queues a proposal of state succeeded
|
||||||
|
* @param proposalId The id of the proposal to queue
|
||||||
|
*/
|
||||||
|
function queue(uint proposalId) external {
|
||||||
|
require(state(proposalId) == ProposalState.Succeeded, "GovernorBravo::queue: proposal can only be queued if it is succeeded");
|
||||||
|
Proposal storage proposal = proposals[proposalId];
|
||||||
|
uint eta = SafeMath.add(block.timestamp, timelock.delay());
|
||||||
|
for (uint i = 0; i < proposal.targets.length; i++) {
|
||||||
|
queueOrRevertInternal(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], eta);
|
||||||
|
}
|
||||||
|
proposal.eta = eta;
|
||||||
|
emit ProposalQueued(proposalId, eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueOrRevertInternal(address target, uint value, string memory signature, bytes memory data, uint eta) internal {
|
||||||
|
require(!timelock.queuedTransactions(keccak256(abi.encode(target, value, signature, data, eta))), "GovernorBravo::queueOrRevertInternal: identical proposal action already queued at eta");
|
||||||
|
timelock.queueTransaction(target, value, signature, data, eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Executes a queued proposal if eta has passed
|
||||||
|
* @param proposalId The id of the proposal to execute
|
||||||
|
*/
|
||||||
|
function execute(uint proposalId) external payable {
|
||||||
|
require(state(proposalId) == ProposalState.Queued, "GovernorBravo::execute: proposal can only be executed if it is queued");
|
||||||
|
Proposal storage proposal = proposals[proposalId];
|
||||||
|
proposal.executed = true;
|
||||||
|
for (uint i = 0; i < proposal.targets.length; i++) {
|
||||||
|
timelock.executeTransaction{value: proposal.values[i]}(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);
|
||||||
|
}
|
||||||
|
emit ProposalExecuted(proposalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Cancels a proposal only if sender is the proposer, or proposer delegates dropped below proposal threshold
|
||||||
|
* @param proposalId The id of the proposal to cancel
|
||||||
|
*/
|
||||||
|
function cancel(uint proposalId) external {
|
||||||
|
require(state(proposalId) != ProposalState.Executed, "GovernorBravo::cancel: cannot cancel executed proposal");
|
||||||
|
|
||||||
|
Proposal storage proposal = proposals[proposalId];
|
||||||
|
require(msg.sender == proposal.proposer || token.getPriorVotes(proposal.proposer, SafeMath.sub(block.number, 1)) < proposalThreshold, "GovernorBravo::cancel: proposer above threshold");
|
||||||
|
|
||||||
|
proposal.canceled = true;
|
||||||
|
for (uint i = 0; i < proposal.targets.length; i++) {
|
||||||
|
timelock.cancelTransaction(proposal.targets[i], proposal.values[i], proposal.signatures[i], proposal.calldatas[i], proposal.eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit ProposalCanceled(proposalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Gets actions of a proposal
|
||||||
|
* @param proposalId the id of the proposal
|
||||||
|
* @return targets
|
||||||
|
* @return values
|
||||||
|
* @return signatures
|
||||||
|
* @return calldatas
|
||||||
|
*/
|
||||||
|
function getActions(uint proposalId) external view returns (address[] memory targets, uint[] memory values, string[] memory signatures, bytes[] memory calldatas) {
|
||||||
|
Proposal storage p = proposals[proposalId];
|
||||||
|
return (p.targets, p.values, p.signatures, p.calldatas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Gets the receipt for a voter on a given proposal
|
||||||
|
* @param proposalId the id of proposal
|
||||||
|
* @param voter The address of the voter
|
||||||
|
* @return The voting receipt
|
||||||
|
*/
|
||||||
|
function getReceipt(uint proposalId, address voter) external view returns (Receipt memory) {
|
||||||
|
return proposals[proposalId].receipts[voter];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Gets the state of a proposal
|
||||||
|
* @param proposalId The id of the proposal
|
||||||
|
* @return Proposal state
|
||||||
|
*/
|
||||||
|
function state(uint proposalId) public view returns (ProposalState) {
|
||||||
|
require(proposalCount >= proposalId, "GovernorBravo::state: invalid proposal id");
|
||||||
|
Proposal storage proposal = proposals[proposalId];
|
||||||
|
if (proposal.canceled) {
|
||||||
|
return ProposalState.Canceled;
|
||||||
|
} else if (block.number <= proposal.startBlock) {
|
||||||
|
return ProposalState.Pending;
|
||||||
|
} else if (block.number <= proposal.endBlock) {
|
||||||
|
return ProposalState.Active;
|
||||||
|
} else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
|
||||||
|
return ProposalState.Defeated;
|
||||||
|
} else if (proposal.eta == 0) {
|
||||||
|
return ProposalState.Succeeded;
|
||||||
|
} else if (proposal.executed) {
|
||||||
|
return ProposalState.Executed;
|
||||||
|
} else if (block.timestamp >= SafeMath.add(proposal.eta, timelock.GRACE_PERIOD())) {
|
||||||
|
return ProposalState.Expired;
|
||||||
|
} else {
|
||||||
|
return ProposalState.Queued;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Cast a vote for a proposal
|
||||||
|
* @param proposalId The id of the proposal to vote on
|
||||||
|
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
||||||
|
*/
|
||||||
|
function castVote(uint proposalId, uint8 support) external {
|
||||||
|
emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Cast a vote for a proposal with a reason
|
||||||
|
* @param proposalId The id of the proposal to vote on
|
||||||
|
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
||||||
|
* @param reason The reason given for the vote by the voter
|
||||||
|
*/
|
||||||
|
function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external {
|
||||||
|
emit VoteCast(msg.sender, proposalId, support, castVoteInternal(msg.sender, proposalId, support), reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Cast a vote for a proposal by signature
|
||||||
|
* @dev External function that accepts EIP-712 signatures for voting on proposals.
|
||||||
|
*/
|
||||||
|
function castVoteBySig(uint proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s) external {
|
||||||
|
bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), getChainIdInternal(), address(this)));
|
||||||
|
bytes32 structHash = keccak256(abi.encode(BALLOT_TYPEHASH, proposalId, support));
|
||||||
|
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
|
||||||
|
address signatory = ecrecover(digest, v, r, s);
|
||||||
|
require(signatory != address(0), "GovernorBravo::castVoteBySig: invalid signature");
|
||||||
|
emit VoteCast(signatory, proposalId, support, castVoteInternal(signatory, proposalId, support), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Internal function that caries out voting logic
|
||||||
|
* @param voter The voter that is casting their vote
|
||||||
|
* @param proposalId The id of the proposal to vote on
|
||||||
|
* @param support The support value for the vote. 0=against, 1=for, 2=abstain
|
||||||
|
* @return The number of votes cast
|
||||||
|
*/
|
||||||
|
function castVoteInternal(address voter, uint proposalId, uint8 support) internal returns (uint96) {
|
||||||
|
require(state(proposalId) == ProposalState.Active, "GovernorBravo::castVoteInternal: voting is closed");
|
||||||
|
require(support <= 2, "GovernorBravo::castVoteInternal: invalid vote type");
|
||||||
|
Proposal storage proposal = proposals[proposalId];
|
||||||
|
Receipt storage receipt = proposal.receipts[voter];
|
||||||
|
require(receipt.hasVoted == false, "GovernorBravo::castVoteInternal: voter already voted");
|
||||||
|
uint96 votes = token.getPriorVotes(voter, proposal.startBlock);
|
||||||
|
|
||||||
|
if (support == 0) {
|
||||||
|
proposal.againstVotes = SafeMath.add(proposal.againstVotes, votes);
|
||||||
|
} else if (support == 1) {
|
||||||
|
proposal.forVotes = SafeMath.add(proposal.forVotes, votes);
|
||||||
|
} else if (support == 2) {
|
||||||
|
proposal.abstainVotes = SafeMath.add(proposal.abstainVotes, votes);
|
||||||
|
}
|
||||||
|
|
||||||
|
receipt.hasVoted = true;
|
||||||
|
receipt.support = support;
|
||||||
|
receipt.votes = votes;
|
||||||
|
|
||||||
|
return votes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Admin function for setting the voting delay
|
||||||
|
* @param newVotingDelay new voting delay, in blocks
|
||||||
|
*/
|
||||||
|
function _setVotingDelay(uint newVotingDelay) external {
|
||||||
|
require(msg.sender == admin, "GovernorBravo::_setVotingDelay: admin only");
|
||||||
|
require(newVotingDelay >= MIN_VOTING_DELAY && newVotingDelay <= MAX_VOTING_DELAY, "GovernorBravo::_setVotingDelay: invalid voting delay");
|
||||||
|
uint oldVotingDelay = votingDelay;
|
||||||
|
votingDelay = newVotingDelay;
|
||||||
|
|
||||||
|
emit VotingDelaySet(oldVotingDelay,votingDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Admin function for setting the voting period
|
||||||
|
* @param newVotingPeriod new voting period, in blocks
|
||||||
|
*/
|
||||||
|
function _setVotingPeriod(uint newVotingPeriod) external {
|
||||||
|
require(msg.sender == admin, "GovernorBravo::_setVotingPeriod: admin only");
|
||||||
|
require(newVotingPeriod >= MIN_VOTING_PERIOD && newVotingPeriod <= MAX_VOTING_PERIOD, "GovernorBravo::_setVotingPeriod: invalid voting period");
|
||||||
|
uint oldVotingPeriod = votingPeriod;
|
||||||
|
votingPeriod = newVotingPeriod;
|
||||||
|
|
||||||
|
emit VotingPeriodSet(oldVotingPeriod, votingPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Admin function for setting the proposal threshold
|
||||||
|
* @dev newProposalThreshold must be greater than the hardcoded min
|
||||||
|
* @param newProposalThreshold new proposal threshold
|
||||||
|
*/
|
||||||
|
function _setProposalThreshold(uint newProposalThreshold) external {
|
||||||
|
require(msg.sender == admin, "GovernorBravo::_setProposalThreshold: admin only");
|
||||||
|
require(newProposalThreshold >= MIN_PROPOSAL_THRESHOLD && newProposalThreshold <= MAX_PROPOSAL_THRESHOLD, "GovernorBravo::_setProposalThreshold: invalid proposal threshold");
|
||||||
|
uint oldProposalThreshold = proposalThreshold;
|
||||||
|
proposalThreshold = newProposalThreshold;
|
||||||
|
|
||||||
|
emit ProposalThresholdSet(oldProposalThreshold, proposalThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Initiate the GovernorBravo contract
|
||||||
|
* @dev Admin only. Accepts timelock admin
|
||||||
|
*/
|
||||||
|
function _initiate() external {
|
||||||
|
require(msg.sender == admin, "GovernorBravo::_initiate: admin only");
|
||||||
|
require(proposalCount == 0, "GovernorBravo::_initiate: can only initiate once");
|
||||||
|
timelock.acceptAdmin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
|
||||||
|
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
|
||||||
|
* @param newPendingAdmin New pending admin.
|
||||||
|
*/
|
||||||
|
function _setPendingAdmin(address newPendingAdmin) external {
|
||||||
|
// Check caller = admin
|
||||||
|
require(msg.sender == admin, "GovernorBravo:_setPendingAdmin: admin only");
|
||||||
|
|
||||||
|
// Save current value, if any, for inclusion in log
|
||||||
|
address oldPendingAdmin = pendingAdmin;
|
||||||
|
|
||||||
|
// Store pendingAdmin with value newPendingAdmin
|
||||||
|
pendingAdmin = newPendingAdmin;
|
||||||
|
|
||||||
|
// Emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin)
|
||||||
|
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
|
||||||
|
* @dev Admin function for pending admin to accept role and update admin
|
||||||
|
*/
|
||||||
|
function _acceptAdmin() external {
|
||||||
|
// Check caller is pendingAdmin and pendingAdmin ≠ address(0)
|
||||||
|
require(msg.sender == pendingAdmin && msg.sender != address(0), "GovernorBravo:_acceptAdmin: pending admin only");
|
||||||
|
|
||||||
|
// Save current values for inclusion in log
|
||||||
|
address oldAdmin = admin;
|
||||||
|
address oldPendingAdmin = pendingAdmin;
|
||||||
|
|
||||||
|
// Store admin with value pendingAdmin
|
||||||
|
admin = pendingAdmin;
|
||||||
|
|
||||||
|
// Clear the pending value
|
||||||
|
pendingAdmin = address(0);
|
||||||
|
|
||||||
|
emit NewAdmin(oldAdmin, admin);
|
||||||
|
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getChainIdInternal() internal pure returns (uint) {
|
||||||
|
uint chainId;
|
||||||
|
assembly { chainId := chainid() }
|
||||||
|
return chainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
83
contracts/GovernorBravoDelegator.sol
Normal file
83
contracts/GovernorBravoDelegator.sol
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import { GovernorBravoDelegatorStorage, GovernorBravoEvents } from "./GovernorBravoInterfaces.sol";
|
||||||
|
|
||||||
|
contract GovernorBravoDelegator is GovernorBravoDelegatorStorage, GovernorBravoEvents {
|
||||||
|
constructor(
|
||||||
|
address timelock_,
|
||||||
|
address admin_,
|
||||||
|
address token_,
|
||||||
|
address implementation_,
|
||||||
|
uint votingPeriod_,
|
||||||
|
uint votingDelay_,
|
||||||
|
uint proposalThreshold_
|
||||||
|
) {
|
||||||
|
// Admin set to msg.sender for initialization
|
||||||
|
admin = msg.sender;
|
||||||
|
|
||||||
|
delegateTo(
|
||||||
|
implementation_,
|
||||||
|
abi.encodeWithSignature(
|
||||||
|
"initialize(address,address,uint256,uint256,uint256)",
|
||||||
|
timelock_,
|
||||||
|
token_,
|
||||||
|
votingPeriod_,
|
||||||
|
votingDelay_,
|
||||||
|
proposalThreshold_
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
_setImplementation(implementation_);
|
||||||
|
|
||||||
|
admin = admin_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Called by the admin to update the implementation of the delegator
|
||||||
|
* @param implementation_ The address of the new implementation for delegation
|
||||||
|
*/
|
||||||
|
function _setImplementation(address implementation_) public {
|
||||||
|
require(msg.sender == admin, "GovernorBravoDelegator::_setImplementation: admin only");
|
||||||
|
require(implementation_ != address(0), "GovernorBravoDelegator::_setImplementation: invalid implementation address");
|
||||||
|
|
||||||
|
address oldImplementation = implementation;
|
||||||
|
implementation = implementation_;
|
||||||
|
|
||||||
|
emit NewImplementation(oldImplementation, implementation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Internal method to delegate execution to another contract
|
||||||
|
* @dev It returns to the external caller whatever the implementation returns or forwards reverts
|
||||||
|
* @param callee The contract to delegatecall
|
||||||
|
* @param data The raw data to delegatecall
|
||||||
|
*/
|
||||||
|
function delegateTo(address callee, bytes memory data) internal {
|
||||||
|
(bool success, bytes memory returnData) = callee.delegatecall(data);
|
||||||
|
assembly {
|
||||||
|
if eq(success, 0) {
|
||||||
|
revert(add(returnData, 0x20), returndatasize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Delegates execution to an implementation contract.
|
||||||
|
* It returns to the external caller whatever the implementation returns
|
||||||
|
* or forwards reverts.
|
||||||
|
*/
|
||||||
|
fallback () external payable {
|
||||||
|
// delegate all other functions to current implementation
|
||||||
|
(bool success, ) = implementation.delegatecall(msg.data);
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
let free_mem_ptr := mload(0x40)
|
||||||
|
returndatacopy(free_mem_ptr, 0, returndatasize())
|
||||||
|
|
||||||
|
switch success
|
||||||
|
case 0 { revert(free_mem_ptr, returndatasize()) }
|
||||||
|
default { return(free_mem_ptr, returndatasize()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
180
contracts/GovernorBravoInterfaces.sol
Normal file
180
contracts/GovernorBravoInterfaces.sol
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
interface TimelockInterface {
|
||||||
|
function delay() external view returns (uint);
|
||||||
|
function GRACE_PERIOD() external view returns (uint);
|
||||||
|
function acceptAdmin() external;
|
||||||
|
function queuedTransactions(bytes32 hash) external view returns (bool);
|
||||||
|
function queueTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external returns (bytes32);
|
||||||
|
function cancelTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external;
|
||||||
|
function executeTransaction(address target, uint value, string calldata signature, bytes calldata data, uint eta) external payable returns (bytes memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TokenInterface {
|
||||||
|
function getPriorVotes(address account, uint blockNumber) external view returns (uint96);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract GovernorBravoEvents {
|
||||||
|
/// @notice An event emitted when a new proposal is created
|
||||||
|
event ProposalCreated(
|
||||||
|
uint id,
|
||||||
|
address proposer,
|
||||||
|
address[] targets,
|
||||||
|
uint[] values,
|
||||||
|
string[] signatures,
|
||||||
|
bytes[] calldatas,
|
||||||
|
uint startBlock,
|
||||||
|
uint endBlock,
|
||||||
|
string description
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @notice An event emitted when a vote has been cast on a proposal
|
||||||
|
/// @param voter The address which casted a vote
|
||||||
|
/// @param proposalId The proposal id which was voted on
|
||||||
|
/// @param support Support value for the vote. 0=against, 1=for, 2=abstain
|
||||||
|
/// @param votes Number of votes which were cast by the voter
|
||||||
|
/// @param reason The reason given for the vote by the voter
|
||||||
|
event VoteCast(address indexed voter, uint proposalId, uint8 support, uint votes, string reason);
|
||||||
|
|
||||||
|
/// @notice An event emitted when a proposal has been canceled
|
||||||
|
event ProposalCanceled(uint id);
|
||||||
|
|
||||||
|
/// @notice An event emitted when a proposal has been queued in the Timelock
|
||||||
|
event ProposalQueued(uint id, uint eta);
|
||||||
|
|
||||||
|
/// @notice An event emitted when a proposal has been executed in the Timelock
|
||||||
|
event ProposalExecuted(uint id);
|
||||||
|
|
||||||
|
/// @notice An event emitted when the voting delay is set
|
||||||
|
event VotingDelaySet(uint oldVotingDelay, uint newVotingDelay);
|
||||||
|
|
||||||
|
/// @notice An event emitted when the voting period is set
|
||||||
|
event VotingPeriodSet(uint oldVotingPeriod, uint newVotingPeriod);
|
||||||
|
|
||||||
|
/// @notice Emitted when implementation is changed
|
||||||
|
event NewImplementation(address oldImplementation, address newImplementation);
|
||||||
|
|
||||||
|
/// @notice Emitted when proposal threshold is set
|
||||||
|
event ProposalThresholdSet(uint oldProposalThreshold, uint newProposalThreshold);
|
||||||
|
|
||||||
|
/// @notice Emitted when pendingAdmin is changed
|
||||||
|
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
|
||||||
|
|
||||||
|
/// @notice Emitted when pendingAdmin is accepted, which means admin is updated
|
||||||
|
event NewAdmin(address oldAdmin, address newAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract GovernorBravoDelegatorStorage {
|
||||||
|
/// @notice Administrator for this contract
|
||||||
|
address public admin;
|
||||||
|
|
||||||
|
/// @notice Pending administrator for this contract
|
||||||
|
address public pendingAdmin;
|
||||||
|
|
||||||
|
/// @notice Active brains of Governor
|
||||||
|
address public implementation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title Storage for Governor Bravo Delegate
|
||||||
|
* @notice For future upgrades, do not change GovernorBravoDelegateStorageV1. Create a new
|
||||||
|
* contract which implements GovernorBravoDelegateStorageV1 and following the naming convention
|
||||||
|
* GovernorBravoDelegateStorageVX.
|
||||||
|
*/
|
||||||
|
contract GovernorBravoDelegateStorageV1 is GovernorBravoDelegatorStorage {
|
||||||
|
/// @notice The delay before voting on a proposal may take place, once proposed, in blocks
|
||||||
|
uint public votingDelay;
|
||||||
|
|
||||||
|
/// @notice The duration of voting on a proposal, in blocks
|
||||||
|
uint public votingPeriod;
|
||||||
|
|
||||||
|
/// @notice The number of votes required in order for a voter to become a proposer
|
||||||
|
uint public proposalThreshold;
|
||||||
|
|
||||||
|
/// @notice The total number of proposals
|
||||||
|
uint public proposalCount;
|
||||||
|
|
||||||
|
/// @notice The address of the DSL Protocol Timelock
|
||||||
|
TimelockInterface public timelock;
|
||||||
|
|
||||||
|
/// @notice The address of the DSL governance token
|
||||||
|
TokenInterface public token;
|
||||||
|
|
||||||
|
/// @notice The official record of all proposals ever proposed
|
||||||
|
mapping (uint => Proposal) public proposals;
|
||||||
|
|
||||||
|
/// @notice The latest proposal for each proposer
|
||||||
|
mapping (address => uint) public latestProposalIds;
|
||||||
|
|
||||||
|
struct Proposal {
|
||||||
|
// Unique id for looking up a proposal
|
||||||
|
uint id;
|
||||||
|
|
||||||
|
// Creator of the proposal
|
||||||
|
address proposer;
|
||||||
|
|
||||||
|
// The timestamp that the proposal will be available for execution, set once the vote succeeds
|
||||||
|
uint eta;
|
||||||
|
|
||||||
|
// the ordered list of target addresses for calls to be made
|
||||||
|
address[] targets;
|
||||||
|
|
||||||
|
// The ordered list of values (i.e. msg.value) to be passed to the calls to be made
|
||||||
|
uint[] values;
|
||||||
|
|
||||||
|
// The ordered list of function signatures to be called
|
||||||
|
string[] signatures;
|
||||||
|
|
||||||
|
// The ordered list of calldata to be passed to each call
|
||||||
|
bytes[] calldatas;
|
||||||
|
|
||||||
|
// The block at which voting begins: holders must delegate their votes prior to this block
|
||||||
|
uint startBlock;
|
||||||
|
|
||||||
|
// The block at which voting ends: votes must be cast prior to this block
|
||||||
|
uint endBlock;
|
||||||
|
|
||||||
|
// Current number of votes in favor of this proposal
|
||||||
|
uint forVotes;
|
||||||
|
|
||||||
|
// Current number of votes in opposition to this proposal
|
||||||
|
uint againstVotes;
|
||||||
|
|
||||||
|
// Current number of votes for abstaining for this proposal
|
||||||
|
uint abstainVotes;
|
||||||
|
|
||||||
|
// Flag marking whether the proposal has been canceled
|
||||||
|
bool canceled;
|
||||||
|
|
||||||
|
// Flag marking whether the proposal has been executed
|
||||||
|
bool executed;
|
||||||
|
|
||||||
|
// Receipts of ballots for the entire set of voters
|
||||||
|
mapping (address => Receipt) receipts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Ballot receipt record for a voter
|
||||||
|
struct Receipt {
|
||||||
|
// Whether or not a vote has been cast
|
||||||
|
bool hasVoted;
|
||||||
|
|
||||||
|
// Whether or not the voter supports the proposal or abstains
|
||||||
|
uint8 support;
|
||||||
|
|
||||||
|
// The number of votes the voter had, which were cast
|
||||||
|
uint96 votes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Possible states that a proposal may be in
|
||||||
|
enum ProposalState {
|
||||||
|
Pending,
|
||||||
|
Active,
|
||||||
|
Canceled,
|
||||||
|
Defeated,
|
||||||
|
Succeeded,
|
||||||
|
Queued,
|
||||||
|
Expired,
|
||||||
|
Executed
|
||||||
|
}
|
||||||
|
}
|
||||||
186
contracts/SafeMath.sol
Normal file
186
contracts/SafeMath.sol
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
|
||||||
|
// Subject to the MIT license.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Wrappers over Solidity's arithmetic operations with added overflow
|
||||||
|
* checks.
|
||||||
|
*
|
||||||
|
* Arithmetic operations in Solidity wrap on overflow. This can easily result
|
||||||
|
* in bugs, because programmers usually assume that an overflow raises an
|
||||||
|
* error, which is the standard behavior in high level programming languages.
|
||||||
|
* `SafeMath` restores this intuition by reverting the transaction when an
|
||||||
|
* operation overflows.
|
||||||
|
*
|
||||||
|
* Using this library instead of the unchecked operations eliminates an entire
|
||||||
|
* class of bugs, so it's recommended to use it always.
|
||||||
|
*/
|
||||||
|
library SafeMath {
|
||||||
|
/**
|
||||||
|
* @dev Returns the addition of two unsigned integers, reverting on overflow.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `+` operator.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Addition cannot overflow.
|
||||||
|
*/
|
||||||
|
function add(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||||
|
uint256 c = a + b;
|
||||||
|
require(c >= a, "SafeMath: addition overflow");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `+` operator.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Addition cannot overflow.
|
||||||
|
*/
|
||||||
|
function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
|
||||||
|
uint256 c = a + b;
|
||||||
|
require(c >= a, errorMessage);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `-` operator.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Subtraction cannot underflow.
|
||||||
|
*/
|
||||||
|
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||||
|
return sub(a, b, "SafeMath: subtraction underflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `-` operator.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Subtraction cannot underflow.
|
||||||
|
*/
|
||||||
|
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
|
||||||
|
require(b <= a, errorMessage);
|
||||||
|
uint256 c = a - b;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the multiplication of two unsigned integers, reverting on overflow.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `*` operator.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Multiplication cannot overflow.
|
||||||
|
*/
|
||||||
|
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||||
|
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
|
||||||
|
// benefit is lost if 'b' is also tested.
|
||||||
|
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
|
||||||
|
if (a == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 c = a * b;
|
||||||
|
require(c / a == b, "SafeMath: multiplication overflow");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the multiplication of two unsigned integers, reverting on overflow.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `*` operator.
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - Multiplication cannot overflow.
|
||||||
|
*/
|
||||||
|
function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
|
||||||
|
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
|
||||||
|
// benefit is lost if 'b' is also tested.
|
||||||
|
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
|
||||||
|
if (a == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 c = a * b;
|
||||||
|
require(c / a == b, errorMessage);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the integer division of two unsigned integers.
|
||||||
|
* Reverts on division by zero. The result is rounded towards zero.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `/` operator. Note: this function uses a
|
||||||
|
* `revert` opcode (which leaves remaining gas untouched) while Solidity
|
||||||
|
* uses an invalid opcode to revert (consuming all remaining gas).
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - The divisor cannot be zero.
|
||||||
|
*/
|
||||||
|
function div(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||||
|
return div(a, b, "SafeMath: division by zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the integer division of two unsigned integers.
|
||||||
|
* Reverts with custom message on division by zero. The result is rounded towards zero.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `/` operator. Note: this function uses a
|
||||||
|
* `revert` opcode (which leaves remaining gas untouched) while Solidity
|
||||||
|
* uses an invalid opcode to revert (consuming all remaining gas).
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - The divisor cannot be zero.
|
||||||
|
*/
|
||||||
|
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
|
||||||
|
// Solidity only automatically asserts when dividing by 0
|
||||||
|
require(b > 0, errorMessage);
|
||||||
|
uint256 c = a / b;
|
||||||
|
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
|
||||||
|
* Reverts when dividing by zero.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `%` operator. This function uses a `revert`
|
||||||
|
* opcode (which leaves remaining gas untouched) while Solidity uses an
|
||||||
|
* invalid opcode to revert (consuming all remaining gas).
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - The divisor cannot be zero.
|
||||||
|
*/
|
||||||
|
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
|
||||||
|
return mod(a, b, "SafeMath: modulo by zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
|
||||||
|
* Reverts with custom message when dividing by zero.
|
||||||
|
*
|
||||||
|
* Counterpart to Solidity's `%` operator. This function uses a `revert`
|
||||||
|
* opcode (which leaves remaining gas untouched) while Solidity uses an
|
||||||
|
* invalid opcode to revert (consuming all remaining gas).
|
||||||
|
*
|
||||||
|
* Requirements:
|
||||||
|
* - The divisor cannot be zero.
|
||||||
|
*/
|
||||||
|
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
|
||||||
|
require(b != 0, errorMessage);
|
||||||
|
return a % b;
|
||||||
|
}
|
||||||
|
}
|
||||||
111
contracts/Timelock.sol
Normal file
111
contracts/Timelock.sol
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
import "./SafeMath.sol";
|
||||||
|
|
||||||
|
contract Timelock {
|
||||||
|
using SafeMath for uint;
|
||||||
|
|
||||||
|
event NewAdmin(address indexed newAdmin);
|
||||||
|
event NewPendingAdmin(address indexed newPendingAdmin);
|
||||||
|
event NewDelay(uint indexed newDelay);
|
||||||
|
event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);
|
||||||
|
event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);
|
||||||
|
event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);
|
||||||
|
|
||||||
|
uint public constant GRACE_PERIOD = 14 days;
|
||||||
|
uint public constant MINIMUM_DELAY = 2 days;
|
||||||
|
uint public constant MAXIMUM_DELAY = 30 days;
|
||||||
|
|
||||||
|
address public admin;
|
||||||
|
address public pendingAdmin;
|
||||||
|
uint public delay;
|
||||||
|
|
||||||
|
mapping (bytes32 => bool) public queuedTransactions;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(address admin_, uint delay_) {
|
||||||
|
require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay.");
|
||||||
|
require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
|
||||||
|
|
||||||
|
admin = admin_;
|
||||||
|
delay = delay_;
|
||||||
|
}
|
||||||
|
|
||||||
|
fallback() external payable { }
|
||||||
|
|
||||||
|
function setDelay(uint delay_) public {
|
||||||
|
require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock.");
|
||||||
|
require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay.");
|
||||||
|
require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay.");
|
||||||
|
delay = delay_;
|
||||||
|
|
||||||
|
emit NewDelay(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function acceptAdmin() public {
|
||||||
|
require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin.");
|
||||||
|
admin = msg.sender;
|
||||||
|
pendingAdmin = address(0);
|
||||||
|
|
||||||
|
emit NewAdmin(admin);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPendingAdmin(address pendingAdmin_) public {
|
||||||
|
require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock.");
|
||||||
|
pendingAdmin = pendingAdmin_;
|
||||||
|
|
||||||
|
emit NewPendingAdmin(pendingAdmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) {
|
||||||
|
require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin.");
|
||||||
|
require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay.");
|
||||||
|
|
||||||
|
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
|
||||||
|
queuedTransactions[txHash] = true;
|
||||||
|
|
||||||
|
emit QueueTransaction(txHash, target, value, signature, data, eta);
|
||||||
|
return txHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public {
|
||||||
|
require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin.");
|
||||||
|
|
||||||
|
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
|
||||||
|
queuedTransactions[txHash] = false;
|
||||||
|
|
||||||
|
emit CancelTransaction(txHash, target, value, signature, data, eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) {
|
||||||
|
require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin.");
|
||||||
|
|
||||||
|
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
|
||||||
|
require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued.");
|
||||||
|
require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock.");
|
||||||
|
require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale.");
|
||||||
|
|
||||||
|
queuedTransactions[txHash] = false;
|
||||||
|
|
||||||
|
bytes memory callData;
|
||||||
|
|
||||||
|
if (bytes(signature).length == 0) {
|
||||||
|
callData = data;
|
||||||
|
} else {
|
||||||
|
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// solium-disable-next-line security/no-call-value
|
||||||
|
(bool success, bytes memory returnData) = target.call{value: value}(callData);
|
||||||
|
require(success, "Timelock::executeTransaction: Transaction execution reverted.");
|
||||||
|
|
||||||
|
emit ExecuteTransaction(txHash, target, value, signature, data, eta);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBlockTimestamp() internal view returns (uint) {
|
||||||
|
// solium-disable-next-line security/no-block-members
|
||||||
|
return block.timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user