Skip to content

Instantly share code, notes, and snippets.

@d1ll0n
Created February 5, 2021 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d1ll0n/750aeec7ff0ce1c9b71f210ab12d17f3 to your computer and use it in GitHub Desktop.
Save d1ll0n/750aeec7ff0ce1c9b71f210ab12d17f3 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/access/Ownable.sol";
contract CommitteeProxy is Ownable {
event CommitteeChanged(address indexed previousCommittee, address indexed newCommittee);
event ExecuteTransaction(
address indexed target,
uint256 value,
string signature,
bytes data
);
address public committee;
constructor(address committee_) public Ownable() {
committee = committee_;
emit CommitteeChanged(address(0), committee_);
}
function setCommittee(address committee_) external onlyOwner {
emit CommitteeChanged(committee, committee_);
committee = committee_;
}
modifier onlyCommittee {
require(msg.sender == committee, "CommitteeOwnable: caller not committee");
_;
}
function executeTransaction(
address target,
uint256 value,
string calldata signature,
bytes calldata data
)
external
payable
onlyCommittee
returns (bytes memory)
{
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
(bool success, bytes memory returnData) = target.call{value: value}(callData);
require(
success,
"CommitteeProxy::executeTransaction: Transaction execution reverted."
);
emit ExecuteTransaction(target, value, signature, data);
return returnData;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../interfaces/ITimelock.sol";
contract Timelock is ITimelock {
using SafeMath for uint256;
event NewAdmin(address indexed newAdmin);
event NewPendingAdmin(address indexed newPendingAdmin);
event NewDelay(uint256 indexed newDelay);
event CancelTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event ExecuteTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
event QueueTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
string signature,
bytes data,
uint256 eta
);
uint256 public constant override GRACE_PERIOD = 14 days;
uint256 public constant override MINIMUM_DELAY = 7 days;
uint256 public constant override MAXIMUM_DELAY = 30 days;
address public immutable override superUser;
address public override admin;
address public override pendingAdmin;
uint256 public override delay;
modifier isAdmin {
require(
msg.sender == admin || msg.sender == superUser,
"Timelock::isAdmin: Call must come from admin."
);
_;
}
mapping(bytes32 => bool) public override queuedTransactions;
constructor(address admin_, address superUser_) public {
admin = admin_;
delay = 7 days;
superUser = superUser_;
}
fallback() external payable {}
function setDelay(uint256 delay_) public override {
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 override {
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 override {
require(
msg.sender == address(this),
"Timelock::setPendingAdmin: Call must come from Timelock."
);
pendingAdmin = pendingAdmin_;
emit NewPendingAdmin(pendingAdmin);
}
function queueTransaction(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public override isAdmin returns (bytes32) {
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,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public override isAdmin {
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,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public payable override isAdmin returns (bytes memory) {
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."
);
return _executeTransaction(
txHash,
target,
value,
signature,
data,
eta
);
}
function sudo(
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) public payable override returns (bytes memory) {
require(msg.sender == superUser, "Timelock::sudo: Caller is not superUser.");
bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta));
return _executeTransaction(
txHash,
target,
value,
signature,
data,
eta
);
}
function _executeTransaction(
bytes32 txHash,
address target,
uint256 value,
string memory signature,
bytes memory data,
uint256 eta
) internal returns (bytes memory) {
queuedTransactions[txHash] = false;
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
(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 (uint256) {
// solium-disable-next-line security/no-block-members
return block.timestamp;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment