文件 1 的 11:AccessControl.sol
pragma solidity >=0.6.0 <0.8.0;
import "../utils/EnumerableSet.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
abstract contract AccessControl is Context {
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 11:Address.sol
pragma solidity >=0.6.2 <0.8.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 3 的 11:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 4 的 11:EnumerableSet.sol
pragma solidity >=0.6.0 <0.8.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping (bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
bytes32 lastvalue = set._values[lastIndex];
set._values[toDeleteIndex] = lastvalue;
set._indexes[lastvalue] = toDeleteIndex + 1;
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
文件 5 的 11:IERC20.sol
pragma solidity >=0.6.0 <0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 6 的 11:SafeERC20.sol
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 7 的 11:SafeMath.sol
pragma solidity >=0.6.0 <0.8.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
文件 8 的 11:SafeOwnable.sol
pragma solidity 0.7.6;
import '@openzeppelin/contracts/utils/Context.sol';
abstract contract SafeOwnable is Context {
address private _owner;
address private _pendingOwner;
event ChangePendingOwner(address indexed previousPendingOwner, address indexed newPendingOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor (address _currentOwner) {
if (_currentOwner == address(0)) {
_currentOwner = _msgSender();
}
_owner = _currentOwner;
emit OwnershipTransferred(address(0), _currentOwner);
}
function owner() public view virtual returns (address) {
return _owner;
}
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
modifier onlyPendingOwner() {
require(pendingOwner() == _msgSender(), "Ownable: caller is not the pendingOwner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
if (_pendingOwner != address(0)) {
emit ChangePendingOwner(_pendingOwner, address(0));
_pendingOwner = address(0);
}
}
function setPendingOwner(address pendingOwner_) public virtual onlyOwner {
require(pendingOwner_ != address(0), "Ownable: pendingOwner is the zero address");
emit ChangePendingOwner(_pendingOwner, pendingOwner_);
_pendingOwner = pendingOwner_;
}
function acceptOwner() public virtual onlyPendingOwner {
emit OwnershipTransferred(_owner, _pendingOwner);
_owner = _pendingOwner;
emit ChangePendingOwner(_pendingOwner, address(0));
_pendingOwner = address(0);
}
}
文件 9 的 11:TimeLocker.sol
pragma solidity 0.7.6;
pragma abicoder v2;
import '@openzeppelin/contracts/access/TimelockController.sol';
import './SafeOwnable.sol';
contract TimeLocker is TimelockController {
constructor(uint minDelay, address[] memory proposers, address[] memory executors) TimelockController(minDelay, proposers, executors) {
}
function acceptOwner(SafeOwnable con) external onlyRole(TIMELOCK_ADMIN_ROLE) {
con.acceptOwner();
}
}
文件 10 的 11:TimelockController.sol
pragma solidity >=0.6.9 <0.8.0;
pragma experimental ABIEncoderV2;
import "./../math/SafeMath.sol";
import "./AccessControl.sol";
contract TimelockController is AccessControl {
bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
uint256 internal constant _DONE_TIMESTAMP = uint256(1);
mapping(bytes32 => uint256) private _timestamps;
uint256 private _minDelay;
event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay);
event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
event Cancelled(bytes32 indexed id);
event MinDelayChange(uint256 oldDuration, uint256 newDuration);
constructor(uint256 minDelay, address[] memory proposers, address[] memory executors) public {
_setRoleAdmin(TIMELOCK_ADMIN_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE);
_setRoleAdmin(EXECUTOR_ROLE, TIMELOCK_ADMIN_ROLE);
_setupRole(TIMELOCK_ADMIN_ROLE, _msgSender());
_setupRole(TIMELOCK_ADMIN_ROLE, address(this));
for (uint256 i = 0; i < proposers.length; ++i) {
_setupRole(PROPOSER_ROLE, proposers[i]);
}
for (uint256 i = 0; i < executors.length; ++i) {
_setupRole(EXECUTOR_ROLE, executors[i]);
}
_minDelay = minDelay;
emit MinDelayChange(0, minDelay);
}
modifier onlyRole(bytes32 role) {
require(hasRole(role, _msgSender()) || hasRole(role, address(0)), "TimelockController: sender requires permission");
_;
}
receive() external payable {}
function isOperation(bytes32 id) public view virtual returns (bool pending) {
return getTimestamp(id) > 0;
}
function isOperationPending(bytes32 id) public view virtual returns (bool pending) {
return getTimestamp(id) > _DONE_TIMESTAMP;
}
function isOperationReady(bytes32 id) public view virtual returns (bool ready) {
uint256 timestamp = getTimestamp(id);
return timestamp > _DONE_TIMESTAMP && timestamp <= block.timestamp;
}
function isOperationDone(bytes32 id) public view virtual returns (bool done) {
return getTimestamp(id) == _DONE_TIMESTAMP;
}
function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp) {
return _timestamps[id];
}
function getMinDelay() public view virtual returns (uint256 duration) {
return _minDelay;
}
function hashOperation(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) public pure virtual returns (bytes32 hash) {
return keccak256(abi.encode(target, value, data, predecessor, salt));
}
function hashOperationBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) public pure virtual returns (bytes32 hash) {
return keccak256(abi.encode(targets, values, datas, predecessor, salt));
}
function schedule(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt, uint256 delay) public virtual onlyRole(PROPOSER_ROLE) {
bytes32 id = hashOperation(target, value, data, predecessor, salt);
_schedule(id, delay);
emit CallScheduled(id, 0, target, value, data, predecessor, delay);
}
function scheduleBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt, uint256 delay) public virtual onlyRole(PROPOSER_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == datas.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt);
_schedule(id, delay);
for (uint256 i = 0; i < targets.length; ++i) {
emit CallScheduled(id, i, targets[i], values[i], datas[i], predecessor, delay);
}
}
function _schedule(bytes32 id, uint256 delay) private {
require(!isOperation(id), "TimelockController: operation already scheduled");
require(delay >= getMinDelay(), "TimelockController: insufficient delay");
_timestamps[id] = SafeMath.add(block.timestamp, delay);
}
function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE) {
require(isOperationPending(id), "TimelockController: operation cannot be cancelled");
delete _timestamps[id];
emit Cancelled(id);
}
function execute(address target, uint256 value, bytes calldata data, bytes32 predecessor, bytes32 salt) public payable virtual onlyRole(EXECUTOR_ROLE) {
bytes32 id = hashOperation(target, value, data, predecessor, salt);
_beforeCall(id, predecessor);
_call(id, 0, target, value, data);
_afterCall(id);
}
function executeBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata datas, bytes32 predecessor, bytes32 salt) public payable virtual onlyRole(EXECUTOR_ROLE) {
require(targets.length == values.length, "TimelockController: length mismatch");
require(targets.length == datas.length, "TimelockController: length mismatch");
bytes32 id = hashOperationBatch(targets, values, datas, predecessor, salt);
_beforeCall(id, predecessor);
for (uint256 i = 0; i < targets.length; ++i) {
_call(id, i, targets[i], values[i], datas[i]);
}
_afterCall(id);
}
function _beforeCall(bytes32 id, bytes32 predecessor) private view {
require(isOperationReady(id), "TimelockController: operation is not ready");
require(predecessor == bytes32(0) || isOperationDone(predecessor), "TimelockController: missing dependency");
}
function _afterCall(bytes32 id) private {
require(isOperationReady(id), "TimelockController: operation is not ready");
_timestamps[id] = _DONE_TIMESTAMP;
}
function _call(bytes32 id, uint256 index, address target, uint256 value, bytes calldata data) private {
(bool success,) = target.call{value: value}(data);
require(success, "TimelockController: underlying transaction reverted");
emit CallExecuted(id, index, target, value, data);
}
function updateDelay(uint256 newDelay) external virtual {
require(msg.sender == address(this), "TimelockController: caller must be timelock");
emit MinDelayChange(_minDelay, newDelay);
_minDelay = newDelay;
}
}
文件 11 的 11:TokenControllerMultiSignature.sol
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../core/TimeLocker.sol";
contract TokenControllerMultiSignature is AccessControl {
event NewTimeLocker(address oldTimeLocker, address newTimeLocker);
event ApplyAddMinter(address requester, address minter, uint maxAmount, uint requestAt, uint expireAt, bytes32 salt);
event AcceptAddMinter(address requester, address minter, uint maxAmount, uint requestAt, uint expireAt, bytes32 salt, address acceptor, uint currentNum);
event ApplyDelMinter(address requester, address minter, uint requestAt, uint expireAt, bytes32 salt);
event AcceptDelMinter(address requester, address minter, uint requestAt, uint expireAt, bytes32 salt, address acceptor, uint currentNum);
event ApplySetPendingOwner(address requester, address owner, uint requestAt, uint expireAt, bytes32 salt);
event AcceptSetPendingOwner(address requester, address owner, uint requestAt, uint expireAt, bytes32 salt, address acceptor, uint currentNum);
bytes32 public constant SIGNER_ROLE = keccak256("MULTI_SIGNATURE_SIGNER");
IERC20 public token;
TimeLocker public timeLocker;
mapping(bytes32 => bool) public transactions;
mapping(bytes32 => uint) public signatureNum;
mapping(bytes32 => mapping(address => bool)) signatureRecord;
uint256 public immutable SIGNATURE_MIN_NUM;
constructor(IERC20 _token, address[] memory _signaturer, uint _minNum) {
require(address(_token) != address(0), "illegal token");
token = _token;
for (uint256 i = 0; i < _signaturer.length; ++i) {
require(!hasRole(SIGNER_ROLE, _signaturer[i]), "address already signature");
_setupRole(SIGNER_ROLE, _signaturer[i]);
}
require(_minNum > 0 && _minNum <= _signaturer.length, "illegal minNum");
SIGNATURE_MIN_NUM = _minNum;
}
function setTimeLocker(TimeLocker _timeLocker) external {
require(address(timeLocker) == address(0), "already be setted");
timeLocker = _timeLocker;
emit NewTimeLocker(address(0), address(timeLocker));
}
function setSignature(address _newSignature) external {
require(hasRole(SIGNER_ROLE, _msgSender()), "caller is not a signature");
renounceRole(SIGNER_ROLE, _msgSender());
_setupRole(SIGNER_ROLE, _newSignature);
}
function applyAddMinter(address minter, uint maxAmount, uint expireAt, bytes32 salt) external {
bytes32 hash = keccak256(abi.encode(_msgSender(), minter, maxAmount, block.timestamp, expireAt, salt));
require(!transactions[hash], "apply already request");
transactions[hash] = true;
signatureNum[hash] = 0;
emit ApplyAddMinter(msg.sender, minter, maxAmount, block.timestamp, expireAt, salt);
}
function acceptAddMinter(address _requester, address _minter, uint _maxAmount, uint _requestAt, uint _expireAt, bytes32 _salt) external {
require(block.timestamp >= _requestAt && block.timestamp <= _expireAt, "illegal time");
require(hasRole(SIGNER_ROLE, _msgSender()), "caller is not a signature");
bytes32 hash = keccak256(abi.encode(_requester, _minter, _maxAmount, _requestAt, _expireAt, _salt));
require(transactions[hash], "apply not exists");
require(!signatureRecord[hash][_msgSender()], "signature already accepted");
signatureNum[hash] = signatureNum[hash] + 1;
signatureRecord[hash][_msgSender()] = true;
emit AcceptAddMinter(_requester, _minter, _maxAmount, _requestAt, _expireAt, _salt, _msgSender(), signatureNum[hash]);
if (signatureNum[hash] >= SIGNATURE_MIN_NUM) {
timeLocker.schedule(address(token), 0, abi.encodeWithSignature("addMinter(address,uint256)", _minter, _maxAmount), bytes32(0), _salt, timeLocker.getMinDelay());
}
}
function executeAddMinter(address _minter, uint _maxAmount, bytes32 _salt) external {
timeLocker.execute(address(token), 0, abi.encodeWithSignature("addMinter(address,uint256)", _minter, _maxAmount), bytes32(0), _salt);
}
function applyDelMinter(address minter, uint expireAt, bytes32 salt) external {
bytes32 hash = keccak256(abi.encode(_msgSender(), minter, block.timestamp, expireAt, salt));
require(!transactions[hash], "apply already request");
transactions[hash] = true;
signatureNum[hash] = 0;
emit ApplyDelMinter(msg.sender, minter, block.timestamp, expireAt, salt);
}
function acceptDelMinter(address _requester, address _minter, uint _requestAt, uint _expireAt, bytes32 _salt) external {
require(block.timestamp >= _requestAt && block.timestamp <= _expireAt, "illegal time");
require(hasRole(SIGNER_ROLE, _msgSender()), "caller is not a signature");
bytes32 hash = keccak256(abi.encode(_requester, _minter, _requestAt, _expireAt, _salt));
require(transactions[hash], "apply not exists");
require(!signatureRecord[hash][_msgSender()], "signature already accepted");
signatureNum[hash] = signatureNum[hash] + 1;
signatureRecord[hash][_msgSender()] = true;
emit AcceptDelMinter(_requester, _minter, _requestAt, _expireAt, _salt, _msgSender(), signatureNum[hash]);
if (signatureNum[hash] >= SIGNATURE_MIN_NUM) {
timeLocker.schedule(address(token), 0, abi.encodeWithSignature("delMinter(address)", _minter), bytes32(0), _salt, timeLocker.getMinDelay());
}
}
function executeDelMinter(address _minter, bytes32 _salt) external {
timeLocker.execute(address(token), 0, abi.encodeWithSignature("delMinter(address)", _minter), bytes32(0), _salt);
}
function applySetPendingOwner(address owner, uint expireAt, bytes32 salt) external {
bytes32 hash = keccak256(abi.encode(_msgSender(), owner, block.timestamp, expireAt, salt));
require(!transactions[hash], "apply already request");
transactions[hash] = true;
signatureNum[hash] = 0;
emit ApplySetPendingOwner(msg.sender, owner, block.timestamp, expireAt, salt);
}
function acceptSetPendingOwner(address _requester, address _owner, uint _requestAt, uint _expireAt, bytes32 _salt) external {
require(block.timestamp >= _requestAt && block.timestamp <= _expireAt, "illegal time");
require(hasRole(SIGNER_ROLE, _msgSender()), "caller is not a signature");
bytes32 hash = keccak256(abi.encode(_requester, _owner, _requestAt, _expireAt, _salt));
require(transactions[hash], "apply not exists");
require(!signatureRecord[hash][_msgSender()], "signature already accepted");
signatureNum[hash] = signatureNum[hash] + 1;
signatureRecord[hash][_msgSender()] = true;
emit AcceptSetPendingOwner(_requester, _owner, _requestAt, _expireAt, _salt, _msgSender(), signatureNum[hash]);
if (signatureNum[hash] >= SIGNATURE_MIN_NUM) {
timeLocker.schedule(address(token), 0, abi.encodeWithSignature("setPendingOwner(address)", _owner), bytes32(0), _salt, timeLocker.getMinDelay());
}
}
function executeSetPendingOwner(address _owner, bytes32 _salt) external {
timeLocker.execute(address(token), 0, abi.encodeWithSignature("setPendingOwner(address)", _owner), bytes32(0), _salt);
}
}
{
"compilationTarget": {
"contracts/token/TokenControllerMultiSignature.sol": "TokenControllerMultiSignature"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address[]","name":"_signaturer","type":"address[]"},{"internalType":"uint256","name":"_minNum","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"maxAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requestAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireAt","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"address","name":"acceptor","type":"address"},{"indexed":false,"internalType":"uint256","name":"currentNum","type":"uint256"}],"name":"AcceptAddMinter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireAt","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"address","name":"acceptor","type":"address"},{"indexed":false,"internalType":"uint256","name":"currentNum","type":"uint256"}],"name":"AcceptDelMinter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireAt","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"},{"indexed":false,"internalType":"address","name":"acceptor","type":"address"},{"indexed":false,"internalType":"uint256","name":"currentNum","type":"uint256"}],"name":"AcceptSetPendingOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"maxAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"requestAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireAt","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"ApplyAddMinter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireAt","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"ApplyDelMinter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"requester","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestAt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expireAt","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"ApplySetPendingOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldTimeLocker","type":"address"},{"indexed":false,"internalType":"address","name":"newTimeLocker","type":"address"}],"name":"NewTimeLocker","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SIGNATURE_MIN_NUM","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SIGNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_requester","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"uint256","name":"_requestAt","type":"uint256"},{"internalType":"uint256","name":"_expireAt","type":"uint256"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"acceptAddMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_requester","type":"address"},{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_requestAt","type":"uint256"},{"internalType":"uint256","name":"_expireAt","type":"uint256"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"acceptDelMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_requester","type":"address"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_requestAt","type":"uint256"},{"internalType":"uint256","name":"_expireAt","type":"uint256"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"acceptSetPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"maxAmount","type":"uint256"},{"internalType":"uint256","name":"expireAt","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"applyAddMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"},{"internalType":"uint256","name":"expireAt","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"applyDelMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"expireAt","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"applySetPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"},{"internalType":"uint256","name":"_maxAmount","type":"uint256"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"executeAddMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"executeDelMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"bytes32","name":"_salt","type":"bytes32"}],"name":"executeSetPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newSignature","type":"address"}],"name":"setSignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract TimeLocker","name":"_timeLocker","type":"address"}],"name":"setTimeLocker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"signatureNum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timeLocker","outputs":[{"internalType":"contract TimeLocker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"transactions","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]