Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- GovernedOwnerProxy
- Optimization enabled
- true
- Compiler version
- v0.8.9+commit.e5eed63a
- Optimization runs
- 800
- Verified at
- 2022-07-05T02:39:29.353426Z
Constructor Arguments
0000000000000000000000001b9dfc56e38b0f92448659c114e2347bd803911c
Arg [0] (address) : 0x1b9dfc56e38b0f92448659c114e2347bd803911c
./contracts/governed-owner/GovernedOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./proxies/CommonOwnerProxy.sol"; import "./proxies/BridgeOwnerProxy.sol"; import "./proxies/MessageOwnerProxy.sol"; import "./proxies/SgnOwnerProxy.sol"; import "./proxies/UpgradeableOwnerProxy.sol"; contract GovernedOwnerProxy is CommonOwnerProxy, BridgeOwnerProxy, MessageOwnerProxy, SgnOwnerProxy, UpgradeableOwnerProxy { constructor(address _initializer) OwnerProxyBase(_initializer) {} }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/interfaces/IUpgradeableOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IUpgradeableOwner { function changeProxyAdmin(address _proxy, address _newAdmin) external; function upgrade(address _proxy, address _implementation) external; function upgradeAndCall( address _proxy, address _implementation, bytes calldata _data ) external; function upgradeTo(address _implementation) external; function upgradeToAndCall(address _implementation, bytes calldata _data) external; }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
./contracts/governed-owner/SimpleGovernance.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // mainly used for governed-owner to do infrequent sgn/cbridge owner operations, // relatively prefer easy-to-use over gas-efficiency contract SimpleGovernance { uint256 public constant THRESHOLD_DECIMAL = 100; uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks using SafeERC20 for IERC20; enum ParamName { ActivePeriod, QuorumThreshold, // default threshold for votes to pass FastPassThreshold // lower threshold for less critical operations } enum ProposalType { ExternalDefault, ExternalFastPass, InternalParamChange, InternalVoterUpdate, InternalProxyUpdate, InternalTransferToken } mapping(ParamName => uint256) public params; struct Proposal { bytes32 dataHash; // hash(proposalType, targetAddress, calldata) uint256 deadline; mapping(address => bool) votes; } mapping(uint256 => Proposal) public proposals; uint256 public nextProposalId; address[] public voters; mapping(address => uint256) public voterPowers; // voter addr -> voting power // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements: // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer. // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers. // See ./proxies/CommonOwnerProxy.sol for example. mapping(address => bool) public proposerProxies; event Initiated( address[] voters, uint256[] powers, address[] proxies, uint256 activePeriod, uint256 quorumThreshold, uint256 fastPassThreshold ); event ProposalCreated( uint256 proposalId, ProposalType proposalType, address target, bytes data, uint256 deadline, address proposer ); event ProposalVoted(uint256 proposalId, address voter, bool vote); event ProposalExecuted(uint256 proposalId); event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value); event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers); event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops); event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount); constructor( address[] memory _voters, uint256[] memory _powers, address[] memory _proxies, uint256 _activePeriod, uint256 _quorumThreshold, uint256 _fastPassThreshold ) { require(_voters.length > 0 && _voters.length == _powers.length, "invalid init voters"); require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, "invalid active period"); require( _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold, "invalid init thresholds" ); for (uint256 i = 0; i < _voters.length; i++) { _setVoter(_voters[i], _powers[i]); } for (uint256 i = 0; i < _proxies.length; i++) { proposerProxies[_proxies[i]] = true; } params[ParamName.ActivePeriod] = _activePeriod; params[ParamName.QuorumThreshold] = _quorumThreshold; params[ParamName.FastPassThreshold] = _fastPassThreshold; emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold); } /********************************* * External and Public Functions * *********************************/ function createProposal(address _target, bytes memory _data) external returns (uint256) { return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault); } // create proposal through proxy function createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) external returns (uint256) { require(proposerProxies[msg.sender], "sender is not a valid proxy"); require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, "invalid type"); return _createProposal(_proposer, _target, _data, _type); } function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) { bytes memory data = abi.encode(_name, _value); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange); emit ParamChangeProposalCreated(proposalId, _name, _value); return proposalId; } function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) { require(_voters.length == _powers.length, "voters and powers length not match"); bytes memory data = abi.encode(_voters, _powers); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate); emit VoterUpdateProposalCreated(proposalId, _voters, _powers); return proposalId; } function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) { require(_addrs.length == _ops.length, "_addrs and _ops length not match"); bytes memory data = abi.encode(_addrs, _ops); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate); emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops); return proposalId; } function createTransferTokenProposal( address _receiver, address _token, uint256 _amount ) external returns (uint256) { bytes memory data = abi.encode(_receiver, _token, _amount); uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken); emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount); return proposalId; } function voteProposal(uint256 _proposalId, bool _vote) external { require(voterPowers[msg.sender] > 0, "invalid voter"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); p.votes[msg.sender] = _vote; emit ProposalVoted(_proposalId, msg.sender, _vote); } function executeProposal( uint256 _proposalId, ProposalType _type, address _target, bytes calldata _data ) external { require(voterPowers[msg.sender] > 0, "only voter can execute a proposal"); Proposal storage p = proposals[_proposalId]; require(block.timestamp < p.deadline, "deadline passed"); require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, "data hash not match"); p.deadline = 0; p.votes[msg.sender] = true; (, , bool pass) = countVotes(_proposalId, _type); require(pass, "not enough votes"); if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) { (bool success, bytes memory res) = _target.call(_data); require(success, _getRevertMsg(res)); } else if (_type == ProposalType.InternalParamChange) { (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256)); params[name] = value; if (name == ParamName.ActivePeriod) { require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, "invalid active period"); } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) { require( params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] && value < THRESHOLD_DECIMAL && value > 0, "invalid threshold" ); } } else if (_type == ProposalType.InternalVoterUpdate) { (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[])); for (uint256 i = 0; i < addrs.length; i++) { if (powers[i] > 0) { _setVoter(addrs[i], powers[i]); } else { _removeVoter(addrs[i]); } } } else if (_type == ProposalType.InternalProxyUpdate) { (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[])); for (uint256 i = 0; i < addrs.length; i++) { if (ops[i]) { proposerProxies[addrs[i]] = true; } else { delete proposerProxies[addrs[i]]; } } } else if (_type == ProposalType.InternalTransferToken) { (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256)); _transfer(receiver, token, amount); } emit ProposalExecuted(_proposalId); } receive() external payable {} /************************** * Public View Functions * **************************/ function getVoters() public view returns (address[] memory, uint256[] memory) { address[] memory addrs = new address[](voters.length); uint256[] memory powers = new uint256[](voters.length); for (uint32 i = 0; i < voters.length; i++) { addrs[i] = voters[i]; powers[i] = voterPowers[voters[i]]; } return (addrs, powers); } function getVote(uint256 _proposalId, address _voter) public view returns (bool) { return proposals[_proposalId].votes[_voter]; } function countVotes(uint256 _proposalId, ProposalType _type) public view returns ( uint256, uint256, bool ) { uint256 yesVotes; uint256 totalPower; for (uint32 i = 0; i < voters.length; i++) { if (getVote(_proposalId, voters[i])) { yesVotes += voterPowers[voters[i]]; } totalPower += voterPowers[voters[i]]; } uint256 threshold; if (_type == ProposalType.ExternalFastPass) { threshold = params[ParamName.FastPassThreshold]; } else { threshold = params[ParamName.QuorumThreshold]; } bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL); return (totalPower, yesVotes, pass); } /********************************** * Internal and Private Functions * **********************************/ // create a proposal and vote yes function _createProposal( address _proposer, address _target, bytes memory _data, ProposalType _type ) private returns (uint256) { require(voterPowers[_proposer] > 0, "only voter can create a proposal"); uint256 proposalId = nextProposalId; nextProposalId += 1; Proposal storage p = proposals[proposalId]; p.dataHash = keccak256(abi.encodePacked(_type, _target, _data)); p.deadline = block.timestamp + params[ParamName.ActivePeriod]; p.votes[_proposer] = true; emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer); return proposalId; } function _setVoter(address _voter, uint256 _power) private { require(_power > 0, "zero power"); if (voterPowers[_voter] == 0) { // add new voter voters.push(_voter); } voterPowers[_voter] = _power; } function _removeVoter(address _voter) private { require(voterPowers[_voter] > 0, "not a voter"); uint256 lastIndex = voters.length - 1; for (uint256 i = 0; i < voters.length; i++) { if (voters[i] == _voter) { if (i < lastIndex) { voters[i] = voters[lastIndex]; } voters.pop(); voterPowers[_voter] = 0; return; } } revert("voter not found"); // this should never happen } function _transfer( address _receiver, address _token, uint256 _amount ) private { if (_token == address(0)) { (bool sent, ) = _receiver.call{value: _amount, gas: 50000}(""); require(sent, "failed to send native token"); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } // https://ethereum.stackexchange.com/a/83577 // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol function _getRevertMsg(bytes memory _returnData) private pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return "Transaction reverted silently"; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } }
./contracts/governed-owner/proxies/OwnerProxyBase.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; import "../SimpleGovernance.sol"; abstract contract OwnerProxyBase { SimpleGovernance public gov; address private initializer; constructor(address _initializer) { initializer = _initializer; } function initGov(SimpleGovernance _gov) public { require(msg.sender == initializer, "only initializer can init"); require(address(gov) == address(0), "gov addr already set"); gov = _gov; } }
./contracts/governed-owner/interfaces/IBridgeOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IBridgeOwner { // for bridges function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external; function notifyResetSigners() external; function increaseNoticePeriod(uint256 _period) external; function setWrap(address _token) external; function setSupply(address _token, uint256 _supply) external; function increaseSupply(address _token, uint256 _delta) external; function decreaseSupply(address _token, uint256 _delta) external; function addGovernor(address _account) external; function removeGovernor(address _account) external; // for bridge tokens function updateBridge(address _bridge) external; function updateBridgeSupplyCap(address _bridge, uint256 _cap) external; function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external; }
./contracts/governed-owner/interfaces/ICommonOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ICommonOwner { function transferOwnership(address _newOwner) external; function addPauser(address _account) external; function removePauser(address _account) external; }
./contracts/governed-owner/interfaces/IMessageOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface IMessageOwner { function setFeePerByte(uint256 _fee) external; function setFeeBase(uint256 _fee) external; function setLiquidityBridge(address _addr) external; function setPegBridge(address _addr) external; function setPegVault(address _addr) external; function setPegBridgeV2(address _addr) external; function setPegVaultV2(address _addr) external; function setPreExecuteMessageGasUsage(uint256 _usage) external; }
./contracts/governed-owner/interfaces/ISgnOwner.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; interface ISgnOwner { function setWhitelistEnabled(bool _whitelistEnabled) external; function addWhitelisted(address _account) external; function removeWhitelisted(address _account) external; function setGovContract(address _addr) external; function setRewardContract(address _addr) external; function setMaxSlashFactor(uint256 _maxSlashFactor) external; }
./contracts/governed-owner/proxies/BridgeOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IBridgeOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract BridgeOwnerProxy is OwnerProxyBase { // for bridges event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers); event NotifyResetSignersProposalCreated(uint256 proposalId, address target); event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period); event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token); event UpdateSupplyProposalCreated( uint256 proposalId, address target, dt.Action action, address token, uint256 supply ); event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account); // for bridge tokens event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr); event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap); event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap); function proposeResetSigners( address _target, address[] calldata _signers, uint256[] calldata _powers ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers); } function proposeNotifyResetSigners(address _target) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit NotifyResetSignersProposalCreated(proposalId, _target); } function proposeIncreaseNoticePeriod(address _target, uint256 _period) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period); } function proposeSetNativeWrap(address _target, address _token) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetNativeWrapProposalCreated(proposalId, _target, _token); } function proposeUpdateSupply( address _target, dt.Action _action, address _token, uint256 _supply ) external { bytes4 selector; if (_action == dt.Action.Set) { selector = IBridgeOwner.setSupply.selector; } else if (_action == dt.Action.Add) { selector = IBridgeOwner.increaseSupply.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.decreaseSupply.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _token, _supply); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply); } function proposeUpdateGovernor( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = IBridgeOwner.addGovernor.selector; } else if (_action == dt.Action.Remove) { selector = IBridgeOwner.removeGovernor.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account); } function proposeUpdateBridgeSupplyCap( address _target, address _bridge, uint256 _cap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap); } function proposeSetBridgeTokenSwapCap( address _target, address _bridgeToken, uint256 _swapCap ) external { bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap); } }
./contracts/governed-owner/proxies/CommonOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ICommonOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract CommonOwnerProxy is OwnerProxyBase { event TransferOwnershipProposalCreated(uint256 proposalId, address target, uint256 newOwner); event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account); function proposeTransferOwnership(address _target, uint256 _newOwner) external { bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner); } function proposeUpdatePauser( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ICommonOwner.addPauser.selector; } else if (_action == dt.Action.Remove) { selector = ICommonOwner.removePauser.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdatePauserProposalCreated(proposalId, _target, _action, _account); } }
./contracts/governed-owner/proxies/MessageOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IMessageOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract MessageOwnerProxy is OwnerProxyBase { event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee); event SetBridgeAddressProposalCreated( uint256 proposalId, address target, dt.BridgeType bridgeType, address bridgeAddr ); event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage); function proposeSetMsgFee( address _target, dt.MsgFeeType _feeType, uint256 _fee ) external { bytes4 selector; if (_feeType == dt.MsgFeeType.PerByte) { selector = IMessageOwner.setFeePerByte.selector; } else if (_feeType == dt.MsgFeeType.Base) { selector = IMessageOwner.setFeeBase.selector; } else { revert("invalid fee type"); } bytes memory data = abi.encodeWithSelector(selector, _fee); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee); } function proposeSetBridgeAddress( address _target, dt.BridgeType _bridgeType, address _bridgeAddr ) external { bytes4 selector; if (_bridgeType == dt.BridgeType.Liquidity) { selector = IMessageOwner.setLiquidityBridge.selector; } else if (_bridgeType == dt.BridgeType.PegBridge) { selector = IMessageOwner.setPegBridge.selector; } else if (_bridgeType == dt.BridgeType.PegVault) { selector = IMessageOwner.setPegVault.selector; } else if (_bridgeType == dt.BridgeType.PegBridgeV2) { selector = IMessageOwner.setPegBridgeV2.selector; } else if (_bridgeType == dt.BridgeType.PegVaultV2) { selector = IMessageOwner.setPegVaultV2.selector; } else { revert("invalid bridge type"); } bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr); } function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external { bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage); } }
./contracts/governed-owner/proxies/OwnerDataTypes.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.8.0; library OwnerDataTypes { enum Action { Set, Add, Remove } enum MsgFeeType { PerByte, Base } enum BridgeType { Liquidity, PegBridge, PegVault, PegBridgeV2, PegVaultV2 } }
./contracts/governed-owner/proxies/SgnOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/ISgnOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract SgnOwnerProxy is OwnerProxyBase { event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled); event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account); event SetGovContractProposalCreated(uint256 proposalId, address target, address addr); event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr); event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor); event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount); function proposeSetWhitelistEnable(address _target, bool _enable) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable); } function proposeUpdateWhitelisted( address _target, dt.Action _action, address _account ) external { bytes4 selector; if (_action == dt.Action.Add) { selector = ISgnOwner.addWhitelisted.selector; } else if (_action == dt.Action.Remove) { selector = ISgnOwner.removeWhitelisted.selector; } else { revert("invalid action"); } bytes memory data = abi.encodeWithSelector(selector, _account); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass); emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account); } function proposeSetGovContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetGovContractProposalCreated(proposalId, _target, _addr); } function proposeSetRewardContract(address _target, address _addr) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetRewardContractProposalCreated(proposalId, _target, _addr); } function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external { bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor); } function proposeDrainToken( address _target, address _token, uint256 _amount ) external { bytes memory data; if (_token == address(0)) { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(uint256"))), _amount); } else { data = abi.encodeWithSelector(bytes4(keccak256(bytes("drainToken(address,uint256"))), _token, _amount); } uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit DrainTokenProposalCreated(proposalId, _target, _token, _amount); } }
./contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol
// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.9; import "./OwnerProxyBase.sol"; import "../interfaces/IUpgradeableOwner.sol"; import {SimpleGovernance as sg} from "../SimpleGovernance.sol"; import {OwnerDataTypes as dt} from "./OwnerDataTypes.sol"; abstract contract UpgradeableOwnerProxy is OwnerProxyBase { event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin); event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation); event UpgradeAndCallProposalCreated( uint256 proposalId, address target, address proxy, address implementation, bytes data ); event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation); event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data); function proposeChangeProxyAdmin( address _target, address _proxy, address _newAdmin ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin); } function proposeUpgrade( address _target, address _proxy, address _implementation ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation); } function proposeUpgradeAndCall( address _target, address _proxy, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector( IUpgradeableOwner.upgradeAndCall.selector, _proxy, _implementation, _data ); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data); } function proposeUpgradeTo(address _target, address _implementation) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToProposalCreated(proposalId, _target, _implementation); } function proposeUpgradeToAndCall( address _target, address _implementation, bytes calldata _data ) external { bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data); uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault); emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data); } }
@openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { 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)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
@openzeppelin/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ 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"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ 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"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ 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); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_initializer","internalType":"address"}]},{"type":"event","name":"ChangeProxyAdminProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"proxy","internalType":"address","indexed":false},{"type":"address","name":"newAdmin","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"DrainTokenProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"IncreaseNoticePeriodProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint256","name":"period","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"NotifyResetSignersProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"ResetSignersProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address[]","name":"signers","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"powers","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"SetBridgeAddressProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint8","name":"bridgeType","internalType":"enum OwnerDataTypes.BridgeType","indexed":false},{"type":"address","name":"bridgeAddr","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetBridgeTokenSwapCapProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"bridgeToken","internalType":"address","indexed":false},{"type":"uint256","name":"cap","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetGovContractProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"addr","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetMaxSlashFactorProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint256","name":"maxSlashFactor","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetMsgFeeProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint8","name":"feeType","internalType":"enum OwnerDataTypes.MsgFeeType","indexed":false},{"type":"uint256","name":"fee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetNativeWrapProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"token","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetPreExecuteMessageGasUsageProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint256","name":"usage","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetRewardContractProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"addr","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"SetWhitelistEnableProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"bool","name":"enabled","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"TransferOwnershipProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint256","name":"newOwner","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateBridgeProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"bridgeAddr","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateBridgeSupplyCapProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"bridge","internalType":"address","indexed":false},{"type":"uint256","name":"cap","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateGovernorProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint8","name":"action","internalType":"enum OwnerDataTypes.Action","indexed":false},{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"UpdatePauserProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint8","name":"action","internalType":"enum OwnerDataTypes.Action","indexed":false},{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateSupplyProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint8","name":"action","internalType":"enum OwnerDataTypes.Action","indexed":false},{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"supply","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateWhitelistedProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"uint8","name":"action","internalType":"enum OwnerDataTypes.Action","indexed":false},{"type":"address","name":"account","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"UpgradeAndCallProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"proxy","internalType":"address","indexed":false},{"type":"address","name":"implementation","internalType":"address","indexed":false},{"type":"bytes","name":"data","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"UpgradeProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"proxy","internalType":"address","indexed":false},{"type":"address","name":"implementation","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"UpgradeToAndCallProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"implementation","internalType":"address","indexed":false},{"type":"bytes","name":"data","internalType":"bytes","indexed":false}],"anonymous":false},{"type":"event","name":"UpgradeToProposalCreated","inputs":[{"type":"uint256","name":"proposalId","internalType":"uint256","indexed":false},{"type":"address","name":"target","internalType":"address","indexed":false},{"type":"address","name":"implementation","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract SimpleGovernance"}],"name":"gov","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initGov","inputs":[{"type":"address","name":"_gov","internalType":"contract SimpleGovernance"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeChangeProxyAdmin","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_proxy","internalType":"address"},{"type":"address","name":"_newAdmin","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeDrainToken","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeIncreaseNoticePeriod","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint256","name":"_period","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeNotifyResetSigners","inputs":[{"type":"address","name":"_target","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeResetSigners","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address[]","name":"_signers","internalType":"address[]"},{"type":"uint256[]","name":"_powers","internalType":"uint256[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetBridgeAddress","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint8","name":"_bridgeType","internalType":"enum OwnerDataTypes.BridgeType"},{"type":"address","name":"_bridgeAddr","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetBridgeTokenSwapCap","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_bridgeToken","internalType":"address"},{"type":"uint256","name":"_swapCap","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetGovContract","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_addr","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetMaxSlashFactor","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint256","name":"_maxSlashFactor","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetMsgFee","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint8","name":"_feeType","internalType":"enum OwnerDataTypes.MsgFeeType"},{"type":"uint256","name":"_fee","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetNativeWrap","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetPreExecuteMessageGasUsage","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint256","name":"_usage","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetRewardContract","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_addr","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeSetWhitelistEnable","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"bool","name":"_enable","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeTransferOwnership","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint256","name":"_newOwner","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpdateBridgeSupplyCap","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_bridge","internalType":"address"},{"type":"uint256","name":"_cap","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpdateGovernor","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint8","name":"_action","internalType":"enum OwnerDataTypes.Action"},{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpdatePauser","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint8","name":"_action","internalType":"enum OwnerDataTypes.Action"},{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpdateSupply","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint8","name":"_action","internalType":"enum OwnerDataTypes.Action"},{"type":"address","name":"_token","internalType":"address"},{"type":"uint256","name":"_supply","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpdateWhitelisted","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"uint8","name":"_action","internalType":"enum OwnerDataTypes.Action"},{"type":"address","name":"_account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpgrade","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_proxy","internalType":"address"},{"type":"address","name":"_implementation","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpgradeAndCall","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_proxy","internalType":"address"},{"type":"address","name":"_implementation","internalType":"address"},{"type":"bytes","name":"_data","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpgradeTo","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_implementation","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"proposeUpgradeToAndCall","inputs":[{"type":"address","name":"_target","internalType":"address"},{"type":"address","name":"_implementation","internalType":"address"},{"type":"bytes","name":"_data","internalType":"bytes"}]}]
Contract Creation Code
0x60806040523480156200001157600080fd5b5060405162002b8338038062002b8383398101604081905262000034916200005a565b600180546001600160a01b0319166001600160a01b03929092169190911790556200008c565b6000602082840312156200006d57600080fd5b81516001600160a01b03811681146200008557600080fd5b9392505050565b612ae7806200009c6000396000f3fe608060405234801561001057600080fd5b50600436106101ad5760003560e01c806377e96d21116100ee578063a71ac5f011610097578063adf43efb11610071578063adf43efb14610372578063bcf612fc14610385578063d19bf98b14610398578063dc797ce3146103ab57600080fd5b8063a71ac5f014610339578063a9ed9d161461034c578063aa9f79431461035f57600080fd5b806382887e02116100c857806382887e02146103005780639f492e7c14610313578063a2ac8d861461032657600080fd5b806377e96d21146102c757806378a4f076146102da5780637f1f1990146102ed57600080fd5b8063224468a81161015b57806339acf6951161013557806339acf6951461027b57806339f2b6511461028e578063656fe066146102a15780636a5c9358146102b457600080fd5b8063224468a8146102425780632ef754af1461025557806338cd4d091461026857600080fd5b8063173a06f91161018c578063173a06f9146102095780631a6ca6da1461021c5780631de8a0161461022f57600080fd5b8062395f11146101b25780630de54802146101c757806312d43a51146101da575b600080fd5b6101c56101c03660046122c4565b6103be565b005b6101c56101d53660046122f0565b6104d4565b6000546101ed906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b6101c5610217366004612375565b6105e7565b6101c561022a3660046122f0565b610710565b6101c561023d3660046123f8565b610823565b6101c56102503660046122f0565b610952565b6101c5610263366004612457565b610a65565b6101c56102763660046122c4565b610c32565b6101c56102893660046124e8565b610d3e565b6101c561029c3660046122c4565b610e62565b6101c56102af36600461254d565b610f6e565b6101c56102c23660046122c4565b611111565b6101c56102d5366004612592565b61121d565b6101c56102e83660046125c2565b61133b565b6101c56102fb3660046125f0565b611497565b6101c561030e366004612624565b6116b5565b6101c56103213660046125c2565b6117c9565b6101c5610334366004612657565b611925565b6101c561034736600461267b565b611a22565b6101c561035a366004612657565b611b3a565b6101c561036d3660046123f8565b611c27565b6101c56103803660046125c2565b611d4b565b6101c56103933660046122f0565b611ea7565b6101c56103a6366004612592565b611fba565b6101c56103b9366004612592565b61218e565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b0316631a20325760e01b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd4802749061042f903390889087908790600401612710565b602060405180830381600087803b15801561044957600080fd5b505af115801561045d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104819190612790565b604080518281526001600160a01b03871660208201529081018590529091507f170cfbd4ca14e4c07750912ad957e666499c81fdfef63021cde154c6ffaac2d7906060015b60405180910390a150505050565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316631a1c1b9560e21b179052600080549351633f52009d60e21b81529293909291169063fd48027490610548903390889087908790600401612710565b602060405180830381600087803b15801561056257600080fd5b505af1158015610576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059a9190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f0440d8b8371306b54b33893631962ccab8e82436d91eb4a2c481454cc2ed4781906060016104c6565b600063a7bdf45a60e01b858585856040516024016106089493929190612841565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd480274906106729033908b9087908790600401612710565b602060405180830381600087803b15801561068c57600080fd5b505af11580156106a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c49190612790565b90507f4c42642dbb775b1ef06ad1492581510de04ccfb5ad078338d6ae369b56f356a08188888888886040516106ff96959493929190612873565b60405180910390a150505050505050565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316631b2ce7f360e11b179052600080549351633f52009d60e21b81529293909291169063fd48027490610784903390889087908790600401612710565b602060405180830381600087803b15801561079e57600080fd5b505af11580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d69190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f322efec8297181586148e7711ed39e230b706795edf152fd4e3a3a8fc009d33b906060016104c6565b604080516001600160a01b03848116602483015283811660448084019190915283518084039091018152606490920183526020820180516001600160e01b0316633f7f93af60e11b179052600080549351633f52009d60e21b81529293909291169063fd4802749061089f903390899087908790600401612710565b602060405180830381600087803b1580156108b957600080fd5b505af11580156108cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f19190612790565b604080518281526001600160a01b0380891660208301528088169282019290925290851660608201529091507f892ed68a932643ffae382937ae40947f858726f5014c80e6612255d981508090906080015b60405180910390a15050505050565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316634ffc800d60e11b179052600080549351633f52009d60e21b81529293909291169063fd480274906109c6903390889087908790600401612710565b602060405180830381600087803b1580156109e057600080fd5b505af11580156109f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a189190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f0691c48365957fae1547e6922078da4127a4ec5beedd4aca7b80a5c793722556906060016104c6565b600080846002811115610a7a57610a7a6126e6565b1415610a8e5750632d3633b760e21b610b2b565b6001846002811115610aa257610aa26126e6565b1415610ab65750633cfe6c7760e11b610b2b565b6002846002811115610aca57610aca6126e6565b1415610ade575063057425d760e21b610b2b565b60405162461bcd60e51b815260206004820152600e60248201527f696e76616c696420616374696f6e00000000000000000000000000000000000060448201526064015b60405180910390fd5b604080516001600160a01b038581166024830152604480830186905283518084039091018152606490920183526020820180516001600160e01b03166001600160e01b03198616179052600080549351633f52009d60e21b81529293909291169063fd48027490610ba79033908b908790600190600401612710565b602060405180830381600087803b158015610bc157600080fd5b505af1158015610bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf99190612790565b90507f68a1245119792f0c0b4396c9d85ed7e432c53279527235d14c64bc4e801d2bb081888888886040516106ff9594939291906128cc565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b0316634586f33160e01b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd48027490610ca3903390889087908790600401612710565b602060405180830381600087803b158015610cbd57600080fd5b505af1158015610cd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf59190612790565b604080518281526001600160a01b03871660208201529081018590529091507fa140c5e7d56a62eaaaa3aeb3b9d49c5fc85004d8d25920be0bce5e08a0b7ec9b906060016104c6565b6000634f1ef28660e01b848484604051602401610d5d9392919061292e565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd48027490610dc79033908a9087908790600401612710565b602060405180830381600087803b158015610de157600080fd5b505af1158015610df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e199190612790565b90507f7936bef0e4e9424795215dbcddd145b5f6ccbe6d35ed9ceacc3c9cec1b1a1ed18187878787604051610e52959493929190612951565b60405180910390a1505050505050565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b0316637906491560e11b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd48027490610ed3903390889087908790600401612710565b602060405180830381600087803b158015610eed57600080fd5b505af1158015610f01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f259190612790565b604080518281526001600160a01b03871660208201529081018590529091507f27cc981a956034897b297d307eaa4e907654717913a238daf3e32a5bd0e18aea906060016104c6565b600080836001811115610f8357610f836126e6565b1415610f97575063e2c1ed2560e01b611007565b6001836001811115610fab57610fab6126e6565b1415610fbf575063036145eb60e11b611007565b60405162461bcd60e51b815260206004820152601060248201527f696e76616c6964206665652074797065000000000000000000000000000000006044820152606401610b22565b6000818360405160240161101d91815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd480274906110889033908a908790600190600401612710565b602060405180830381600087803b1580156110a257600080fd5b505af11580156110b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110da9190612790565b90507f924ed1dc9162d297ef826f4eb457b22796e62033fea2fb6f201c13661d04c64181878787604051610e529493929190612984565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b031663f2fde38b60e01b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd48027490611182903390889087908790600401612710565b602060405180830381600087803b15801561119c57600080fd5b505af11580156111b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d49190612790565b604080518281526001600160a01b03871660208201529081018590529091507f2e7f126e99465c6c540098e0eca13d2fc8d71908debbf31d908bee17e2866d0c906060016104c6565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663cd04611960e01b179052600080549351633f52009d60e21b81529293909291169063fd48027490611295903390899087908790600401612710565b602060405180830381600087803b1580156112af57600080fd5b505af11580156112c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e79190612790565b604080518281526001600160a01b038089166020830152871691810191909152606081018590529091507fbbf294b7b2aadbd73c0198ccea847bbc640871eb1ad2a0b01ac080d077f088e790608001610943565b60006001836002811115611351576113516126e6565b141561136557506320b707b160e21b611389565b6002836002811115611379576113796126e6565b1415610ade5750636b2c0f5560e01b5b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd4802749061140e9033908a908790600190600401612710565b602060405180830381600087803b15801561142857600080fd5b505af115801561143c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114609190612790565b90507f9fe02e73a05d9dc9d3f3cb9423f6531a055a6083c43978dc2b64d6e505e4366581878787604051610e5294939291906129bc565b6000808360048111156114ac576114ac6126e6565b14156114c0575063588be02b60e01b6115a8565b60018360048111156114d4576114d46126e6565b14156114e857506301e5ff3360e11b6115a8565b60028360048111156114fc576114fc6126e6565b14156115105750639b05a77560e01b6115a8565b6003836004811115611524576115246126e6565b14156115385750634177ea8160e11b6115a8565b600483600481111561154c5761154c6126e6565b1415611560575063f83b0fb960e01b6115a8565b60405162461bcd60e51b815260206004820152601360248201527f696e76616c6964206272696467652074797065000000000000000000000000006044820152606401610b22565b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd4802749061162c9033908a9087908790600401612710565b602060405180830381600087803b15801561164657600080fd5b505af115801561165a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167e9190612790565b90507f1605f3e8c96ab34dc0790b6937e2f0c59bb340db6e689a77bc500789d32314f881878787604051610e5294939291906129f3565b6040805182151560248083019190915282518083039091018152604490910182526020810180516001600160e01b0316630296cf3f60e11b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd4802749061172a903390889087908790600401612710565b602060405180830381600087803b15801561174457600080fd5b505af1158015611758573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177c9190612790565b604080518281526001600160a01b0387166020820152851515918101919091529091507fb415e98e9740cf326dbeb0b43f0fa7d742ab13c6c2fc4e0042b6289cca074526906060016104c6565b600060018360028111156117df576117df6126e6565b14156117f357506310154bad60e01b611817565b6002836002811115611807576118076126e6565b1415610ade575063291d954960e01b5b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd4802749061189c9033908a908790600190600401612710565b602060405180830381600087803b1580156118b657600080fd5b505af11580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ee9190612790565b90507f4336c9be2e5abfad187a39a7f7346158c644f54824688bb5a5b335f9306fb9bb81878787604051610e5294939291906129bc565b6040805160048082526024820183526020820180516001600160e01b03166325c38b9f60e01b179052600080549351633f52009d60e21b8152929390926001600160a01b039091169163fd480274916119879133918891889160019101612710565b602060405180830381600087803b1580156119a157600080fd5b505af11580156119b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d99190612790565b604080518281526001600160a01b03861660208201529192507f149a32d950b4bc0cb34725d5a842addc6f3e428807357a578301e078cc644357910160405180910390a1505050565b6000639623609d60e01b85858585604051602401611a439493929190612a33565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd48027490611aad9033908b9087908790600401612710565b602060405180830381600087803b158015611ac757600080fd5b505af1158015611adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aff9190612790565b90507ff6b46465ce70c6b53ccde6e33cdd45c1906ded1e8553ff64ebdadf97d3ac29518188888888886040516106ff96959493929190612a6a565b6001546001600160a01b03163314611b945760405162461bcd60e51b815260206004820152601960248201527f6f6e6c7920696e697469616c697a65722063616e20696e6974000000000000006044820152606401610b22565b6000546001600160a01b031615611bed5760405162461bcd60e51b815260206004820152601460248201527f676f76206164647220616c7265616479207365740000000000000000000000006044820152606401610b22565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b604080516001600160a01b03848116602483015283811660448084019190915283518084039091018152606490920183526020820180516001600160e01b031663266a23b160e21b179052600080549351633f52009d60e21b81529293909291169063fd48027490611ca3903390899087908790600401612710565b602060405180830381600087803b158015611cbd57600080fd5b505af1158015611cd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cf59190612790565b604080518281526001600160a01b0380891660208301528088169282019290925290851660608201529091507fc1dead51ac2e9fa286784eeefebacaf915332934c6d6fd290ec2c8de51c7863290608001610943565b60006001836002811115611d6157611d616126e6565b1415611d7557506303c4a25d60e41b611d99565b6002836002811115611d8957611d896126e6565b1415610ade5750631dd9b59160e31b5b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd48027490611e1e9033908a908790600190600401612710565b602060405180830381600087803b158015611e3857600080fd5b505af1158015611e4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e709190612790565b90507f725ab9c9498b34fa1336448b0dc8dfb419461e03707ace510d44e8bb63d8485181878787604051610e5294939291906129bc565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b03166328a8478560e11b179052600080549351633f52009d60e21b81529293909291169063fd48027490611f1b903390889087908790600401612710565b602060405180830381600087803b158015611f3557600080fd5b505af1158015611f49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6d9190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f8aaee1ae0ee9b8019d672115e172cd8fd03686f0f117ce65aa0ed224ca6d12b3906060016104c6565b60606001600160a01b0383166120385750604080518082018252601281527f647261696e546f6b656e2875696e74323536000000000000000000000000000060209182015281516024808201859052835180830390910181526044909101909252810180516001600160e01b031663a76cd1bb60e01b1790526120b1565b50604080518082018252601a81527f647261696e546f6b656e28616464726573732c75696e7432353600000000000060209182015281516001600160a01b03851660248201526044808201859052835180830390910181526064909101909252810180516001600160e01b0316634dd256db60e11b1790525b60008054604051633f52009d60e21b81526001600160a01b039091169063fd480274906120e8903390899087908790600401612710565b602060405180830381600087803b15801561210257600080fd5b505af1158015612116573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213a9190612790565b604080518281526001600160a01b038089166020830152871691810191909152606081018590529091507f07315b20d3a4862680a34cedf6a8b62bd45bdf98fa602f664417d8a84d47c1bc90608001610943565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03166326717b8d60e11b179052600080549351633f52009d60e21b81529293909291169063fd48027490612206903390899087908790600401612710565b602060405180830381600087803b15801561222057600080fd5b505af1158015612234573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122589190612790565b604080518281526001600160a01b038089166020830152871691810191909152606081018590529091507fdf93094bf7ed4f11758b798f8463ab250ae3db9edc392c26b92443f3cf73100490608001610943565b6001600160a01b03811681146122c157600080fd5b50565b600080604083850312156122d757600080fd5b82356122e2816122ac565b946020939093013593505050565b6000806040838503121561230357600080fd5b823561230e816122ac565b9150602083013561231e816122ac565b809150509250929050565b60008083601f84011261233b57600080fd5b50813567ffffffffffffffff81111561235357600080fd5b6020830191508360208260051b850101111561236e57600080fd5b9250929050565b60008060008060006060868803121561238d57600080fd5b8535612398816122ac565b9450602086013567ffffffffffffffff808211156123b557600080fd5b6123c189838a01612329565b909650945060408801359150808211156123da57600080fd5b506123e788828901612329565b969995985093965092949392505050565b60008060006060848603121561240d57600080fd5b8335612418816122ac565b92506020840135612428816122ac565b91506040840135612438816122ac565b809150509250925092565b80356003811061245257600080fd5b919050565b6000806000806080858703121561246d57600080fd5b8435612478816122ac565b935061248660208601612443565b92506040850135612496816122ac565b9396929550929360600135925050565b60008083601f8401126124b857600080fd5b50813567ffffffffffffffff8111156124d057600080fd5b60208301915083602082850101111561236e57600080fd5b600080600080606085870312156124fe57600080fd5b8435612509816122ac565b93506020850135612519816122ac565b9250604085013567ffffffffffffffff81111561253557600080fd5b612541878288016124a6565b95989497509550505050565b60008060006060848603121561256257600080fd5b833561256d816122ac565b925060208401356002811061258157600080fd5b929592945050506040919091013590565b6000806000606084860312156125a757600080fd5b83356125b2816122ac565b92506020840135612581816122ac565b6000806000606084860312156125d757600080fd5b83356125e2816122ac565b925061242860208501612443565b60008060006060848603121561260557600080fd5b8335612610816122ac565b925060208401356005811061242857600080fd5b6000806040838503121561263757600080fd5b8235612642816122ac565b91506020830135801515811461231e57600080fd5b60006020828403121561266957600080fd5b8135612674816122ac565b9392505050565b60008060008060006080868803121561269357600080fd5b853561269e816122ac565b945060208601356126ae816122ac565b935060408601356126be816122ac565b9250606086013567ffffffffffffffff8111156126da57600080fd5b6123e7888289016124a6565b634e487b7160e01b600052602160045260246000fd5b6006811061270c5761270c6126e6565b9052565b60006001600160a01b038087168352602081871681850152608060408501528551915081608085015260005b828110156127585786810182015185820160a00152810161273c565b8281111561276a57600060a084870101525b5050601f01601f1916820160a001905061278760608301846126fc565b95945050505050565b6000602082840312156127a257600080fd5b5051919050565b8183526000602080850194508260005b858110156127e75781356127cc816122ac565b6001600160a01b0316875295820195908201906001016127b9565b509495945050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561282457600080fd5b8260051b8083602087013760009401602001938452509192915050565b6040815260006128556040830186886127a9565b82810360208401526128688185876127f2565b979650505050505050565b8681526001600160a01b038616602082015260806040820152600061289c6080830186886127a9565b82810360608401526128af8185876127f2565b9998505050505050505050565b6003811061270c5761270c6126e6565b8581526001600160a01b03858116602083015260a08201906128f160408401876128bc565b939093166060820152608001529392505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0384168152604060208201526000612787604083018486612905565b85815260006001600160a01b03808716602084015280861660408401525060806060830152612868608083018486612905565b8481526001600160a01b038416602082015260808101600284106129aa576129aa6126e6565b60408201939093526060015292915050565b8481526001600160a01b03848116602083015260808201906129e160408401866128bc565b80841660608401525095945050505050565b8481526001600160a01b038481166020830152608082019060058510612a1b57612a1b6126e6565b84604084015280841660608401525095945050505050565b60006001600160a01b03808716835280861660208401525060606040830152612a60606083018486612905565b9695505050505050565b86815260006001600160a01b038088166020840152808716604084015280861660608401525060a06080830152612aa560a083018486612905565b9897505050505050505056fea2646970667358221220cd73b65b509940334bb91e620ef353f932d88894ac4721bdf2c9427782c3ed2664736f6c634300080900330000000000000000000000001b9dfc56e38b0f92448659c114e2347bd803911c
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101ad5760003560e01c806377e96d21116100ee578063a71ac5f011610097578063adf43efb11610071578063adf43efb14610372578063bcf612fc14610385578063d19bf98b14610398578063dc797ce3146103ab57600080fd5b8063a71ac5f014610339578063a9ed9d161461034c578063aa9f79431461035f57600080fd5b806382887e02116100c857806382887e02146103005780639f492e7c14610313578063a2ac8d861461032657600080fd5b806377e96d21146102c757806378a4f076146102da5780637f1f1990146102ed57600080fd5b8063224468a81161015b57806339acf6951161013557806339acf6951461027b57806339f2b6511461028e578063656fe066146102a15780636a5c9358146102b457600080fd5b8063224468a8146102425780632ef754af1461025557806338cd4d091461026857600080fd5b8063173a06f91161018c578063173a06f9146102095780631a6ca6da1461021c5780631de8a0161461022f57600080fd5b8062395f11146101b25780630de54802146101c757806312d43a51146101da575b600080fd5b6101c56101c03660046122c4565b6103be565b005b6101c56101d53660046122f0565b6104d4565b6000546101ed906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b6101c5610217366004612375565b6105e7565b6101c561022a3660046122f0565b610710565b6101c561023d3660046123f8565b610823565b6101c56102503660046122f0565b610952565b6101c5610263366004612457565b610a65565b6101c56102763660046122c4565b610c32565b6101c56102893660046124e8565b610d3e565b6101c561029c3660046122c4565b610e62565b6101c56102af36600461254d565b610f6e565b6101c56102c23660046122c4565b611111565b6101c56102d5366004612592565b61121d565b6101c56102e83660046125c2565b61133b565b6101c56102fb3660046125f0565b611497565b6101c561030e366004612624565b6116b5565b6101c56103213660046125c2565b6117c9565b6101c5610334366004612657565b611925565b6101c561034736600461267b565b611a22565b6101c561035a366004612657565b611b3a565b6101c561036d3660046123f8565b611c27565b6101c56103803660046125c2565b611d4b565b6101c56103933660046122f0565b611ea7565b6101c56103a6366004612592565b611fba565b6101c56103b9366004612592565b61218e565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b0316631a20325760e01b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd4802749061042f903390889087908790600401612710565b602060405180830381600087803b15801561044957600080fd5b505af115801561045d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104819190612790565b604080518281526001600160a01b03871660208201529081018590529091507f170cfbd4ca14e4c07750912ad957e666499c81fdfef63021cde154c6ffaac2d7906060015b60405180910390a150505050565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316631a1c1b9560e21b179052600080549351633f52009d60e21b81529293909291169063fd48027490610548903390889087908790600401612710565b602060405180830381600087803b15801561056257600080fd5b505af1158015610576573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061059a9190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f0440d8b8371306b54b33893631962ccab8e82436d91eb4a2c481454cc2ed4781906060016104c6565b600063a7bdf45a60e01b858585856040516024016106089493929190612841565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd480274906106729033908b9087908790600401612710565b602060405180830381600087803b15801561068c57600080fd5b505af11580156106a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106c49190612790565b90507f4c42642dbb775b1ef06ad1492581510de04ccfb5ad078338d6ae369b56f356a08188888888886040516106ff96959493929190612873565b60405180910390a150505050505050565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316631b2ce7f360e11b179052600080549351633f52009d60e21b81529293909291169063fd48027490610784903390889087908790600401612710565b602060405180830381600087803b15801561079e57600080fd5b505af11580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d69190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f322efec8297181586148e7711ed39e230b706795edf152fd4e3a3a8fc009d33b906060016104c6565b604080516001600160a01b03848116602483015283811660448084019190915283518084039091018152606490920183526020820180516001600160e01b0316633f7f93af60e11b179052600080549351633f52009d60e21b81529293909291169063fd4802749061089f903390899087908790600401612710565b602060405180830381600087803b1580156108b957600080fd5b505af11580156108cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108f19190612790565b604080518281526001600160a01b0380891660208301528088169282019290925290851660608201529091507f892ed68a932643ffae382937ae40947f858726f5014c80e6612255d981508090906080015b60405180910390a15050505050565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b0316634ffc800d60e11b179052600080549351633f52009d60e21b81529293909291169063fd480274906109c6903390889087908790600401612710565b602060405180830381600087803b1580156109e057600080fd5b505af11580156109f4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a189190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f0691c48365957fae1547e6922078da4127a4ec5beedd4aca7b80a5c793722556906060016104c6565b600080846002811115610a7a57610a7a6126e6565b1415610a8e5750632d3633b760e21b610b2b565b6001846002811115610aa257610aa26126e6565b1415610ab65750633cfe6c7760e11b610b2b565b6002846002811115610aca57610aca6126e6565b1415610ade575063057425d760e21b610b2b565b60405162461bcd60e51b815260206004820152600e60248201527f696e76616c696420616374696f6e00000000000000000000000000000000000060448201526064015b60405180910390fd5b604080516001600160a01b038581166024830152604480830186905283518084039091018152606490920183526020820180516001600160e01b03166001600160e01b03198616179052600080549351633f52009d60e21b81529293909291169063fd48027490610ba79033908b908790600190600401612710565b602060405180830381600087803b158015610bc157600080fd5b505af1158015610bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf99190612790565b90507f68a1245119792f0c0b4396c9d85ed7e432c53279527235d14c64bc4e801d2bb081888888886040516106ff9594939291906128cc565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b0316634586f33160e01b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd48027490610ca3903390889087908790600401612710565b602060405180830381600087803b158015610cbd57600080fd5b505af1158015610cd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cf59190612790565b604080518281526001600160a01b03871660208201529081018590529091507fa140c5e7d56a62eaaaa3aeb3b9d49c5fc85004d8d25920be0bce5e08a0b7ec9b906060016104c6565b6000634f1ef28660e01b848484604051602401610d5d9392919061292e565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd48027490610dc79033908a9087908790600401612710565b602060405180830381600087803b158015610de157600080fd5b505af1158015610df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e199190612790565b90507f7936bef0e4e9424795215dbcddd145b5f6ccbe6d35ed9ceacc3c9cec1b1a1ed18187878787604051610e52959493929190612951565b60405180910390a1505050505050565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b0316637906491560e11b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd48027490610ed3903390889087908790600401612710565b602060405180830381600087803b158015610eed57600080fd5b505af1158015610f01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f259190612790565b604080518281526001600160a01b03871660208201529081018590529091507f27cc981a956034897b297d307eaa4e907654717913a238daf3e32a5bd0e18aea906060016104c6565b600080836001811115610f8357610f836126e6565b1415610f97575063e2c1ed2560e01b611007565b6001836001811115610fab57610fab6126e6565b1415610fbf575063036145eb60e11b611007565b60405162461bcd60e51b815260206004820152601060248201527f696e76616c6964206665652074797065000000000000000000000000000000006044820152606401610b22565b6000818360405160240161101d91815260200190565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd480274906110889033908a908790600190600401612710565b602060405180830381600087803b1580156110a257600080fd5b505af11580156110b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110da9190612790565b90507f924ed1dc9162d297ef826f4eb457b22796e62033fea2fb6f201c13661d04c64181878787604051610e529493929190612984565b60408051602480820184905282518083039091018152604490910182526020810180516001600160e01b031663f2fde38b60e01b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd48027490611182903390889087908790600401612710565b602060405180830381600087803b15801561119c57600080fd5b505af11580156111b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d49190612790565b604080518281526001600160a01b03871660208201529081018590529091507f2e7f126e99465c6c540098e0eca13d2fc8d71908debbf31d908bee17e2866d0c906060016104c6565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663cd04611960e01b179052600080549351633f52009d60e21b81529293909291169063fd48027490611295903390899087908790600401612710565b602060405180830381600087803b1580156112af57600080fd5b505af11580156112c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e79190612790565b604080518281526001600160a01b038089166020830152871691810191909152606081018590529091507fbbf294b7b2aadbd73c0198ccea847bbc640871eb1ad2a0b01ac080d077f088e790608001610943565b60006001836002811115611351576113516126e6565b141561136557506320b707b160e21b611389565b6002836002811115611379576113796126e6565b1415610ade5750636b2c0f5560e01b5b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd4802749061140e9033908a908790600190600401612710565b602060405180830381600087803b15801561142857600080fd5b505af115801561143c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114609190612790565b90507f9fe02e73a05d9dc9d3f3cb9423f6531a055a6083c43978dc2b64d6e505e4366581878787604051610e5294939291906129bc565b6000808360048111156114ac576114ac6126e6565b14156114c0575063588be02b60e01b6115a8565b60018360048111156114d4576114d46126e6565b14156114e857506301e5ff3360e11b6115a8565b60028360048111156114fc576114fc6126e6565b14156115105750639b05a77560e01b6115a8565b6003836004811115611524576115246126e6565b14156115385750634177ea8160e11b6115a8565b600483600481111561154c5761154c6126e6565b1415611560575063f83b0fb960e01b6115a8565b60405162461bcd60e51b815260206004820152601360248201527f696e76616c6964206272696467652074797065000000000000000000000000006044820152606401610b22565b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd4802749061162c9033908a9087908790600401612710565b602060405180830381600087803b15801561164657600080fd5b505af115801561165a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167e9190612790565b90507f1605f3e8c96ab34dc0790b6937e2f0c59bb340db6e689a77bc500789d32314f881878787604051610e5294939291906129f3565b6040805182151560248083019190915282518083039091018152604490910182526020810180516001600160e01b0316630296cf3f60e11b179052600080549251633f52009d60e21b8152919290916001600160a01b039091169063fd4802749061172a903390889087908790600401612710565b602060405180830381600087803b15801561174457600080fd5b505af1158015611758573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061177c9190612790565b604080518281526001600160a01b0387166020820152851515918101919091529091507fb415e98e9740cf326dbeb0b43f0fa7d742ab13c6c2fc4e0042b6289cca074526906060016104c6565b600060018360028111156117df576117df6126e6565b14156117f357506310154bad60e01b611817565b6002836002811115611807576118076126e6565b1415610ade575063291d954960e01b5b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd4802749061189c9033908a908790600190600401612710565b602060405180830381600087803b1580156118b657600080fd5b505af11580156118ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ee9190612790565b90507f4336c9be2e5abfad187a39a7f7346158c644f54824688bb5a5b335f9306fb9bb81878787604051610e5294939291906129bc565b6040805160048082526024820183526020820180516001600160e01b03166325c38b9f60e01b179052600080549351633f52009d60e21b8152929390926001600160a01b039091169163fd480274916119879133918891889160019101612710565b602060405180830381600087803b1580156119a157600080fd5b505af11580156119b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119d99190612790565b604080518281526001600160a01b03861660208201529192507f149a32d950b4bc0cb34725d5a842addc6f3e428807357a578301e078cc644357910160405180910390a1505050565b6000639623609d60e01b85858585604051602401611a439493929190612a33565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd48027490611aad9033908b9087908790600401612710565b602060405180830381600087803b158015611ac757600080fd5b505af1158015611adb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611aff9190612790565b90507ff6b46465ce70c6b53ccde6e33cdd45c1906ded1e8553ff64ebdadf97d3ac29518188888888886040516106ff96959493929190612a6a565b6001546001600160a01b03163314611b945760405162461bcd60e51b815260206004820152601960248201527f6f6e6c7920696e697469616c697a65722063616e20696e6974000000000000006044820152606401610b22565b6000546001600160a01b031615611bed5760405162461bcd60e51b815260206004820152601460248201527f676f76206164647220616c7265616479207365740000000000000000000000006044820152606401610b22565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b604080516001600160a01b03848116602483015283811660448084019190915283518084039091018152606490920183526020820180516001600160e01b031663266a23b160e21b179052600080549351633f52009d60e21b81529293909291169063fd48027490611ca3903390899087908790600401612710565b602060405180830381600087803b158015611cbd57600080fd5b505af1158015611cd1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cf59190612790565b604080518281526001600160a01b0380891660208301528088169282019290925290851660608201529091507fc1dead51ac2e9fa286784eeefebacaf915332934c6d6fd290ec2c8de51c7863290608001610943565b60006001836002811115611d6157611d616126e6565b1415611d7557506303c4a25d60e41b611d99565b6002836002811115611d8957611d896126e6565b1415610ade5750631dd9b59160e31b5b6040516001600160a01b0383166024820152600090829060440160408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252600080549251633f52009d60e21b8152919350916001600160a01b03169063fd48027490611e1e9033908a908790600190600401612710565b602060405180830381600087803b158015611e3857600080fd5b505af1158015611e4c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e709190612790565b90507f725ab9c9498b34fa1336448b0dc8dfb419461e03707ace510d44e8bb63d8485181878787604051610e5294939291906129bc565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b03166328a8478560e11b179052600080549351633f52009d60e21b81529293909291169063fd48027490611f1b903390889087908790600401612710565b602060405180830381600087803b158015611f3557600080fd5b505af1158015611f49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f6d9190612790565b604080518281526001600160a01b0380881660208301528616918101919091529091507f8aaee1ae0ee9b8019d672115e172cd8fd03686f0f117ce65aa0ed224ca6d12b3906060016104c6565b60606001600160a01b0383166120385750604080518082018252601281527f647261696e546f6b656e2875696e74323536000000000000000000000000000060209182015281516024808201859052835180830390910181526044909101909252810180516001600160e01b031663a76cd1bb60e01b1790526120b1565b50604080518082018252601a81527f647261696e546f6b656e28616464726573732c75696e7432353600000000000060209182015281516001600160a01b03851660248201526044808201859052835180830390910181526064909101909252810180516001600160e01b0316634dd256db60e11b1790525b60008054604051633f52009d60e21b81526001600160a01b039091169063fd480274906120e8903390899087908790600401612710565b602060405180830381600087803b15801561210257600080fd5b505af1158015612116573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213a9190612790565b604080518281526001600160a01b038089166020830152871691810191909152606081018590529091507f07315b20d3a4862680a34cedf6a8b62bd45bdf98fa602f664417d8a84d47c1bc90608001610943565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b03166326717b8d60e11b179052600080549351633f52009d60e21b81529293909291169063fd48027490612206903390899087908790600401612710565b602060405180830381600087803b15801561222057600080fd5b505af1158015612234573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122589190612790565b604080518281526001600160a01b038089166020830152871691810191909152606081018590529091507fdf93094bf7ed4f11758b798f8463ab250ae3db9edc392c26b92443f3cf73100490608001610943565b6001600160a01b03811681146122c157600080fd5b50565b600080604083850312156122d757600080fd5b82356122e2816122ac565b946020939093013593505050565b6000806040838503121561230357600080fd5b823561230e816122ac565b9150602083013561231e816122ac565b809150509250929050565b60008083601f84011261233b57600080fd5b50813567ffffffffffffffff81111561235357600080fd5b6020830191508360208260051b850101111561236e57600080fd5b9250929050565b60008060008060006060868803121561238d57600080fd5b8535612398816122ac565b9450602086013567ffffffffffffffff808211156123b557600080fd5b6123c189838a01612329565b909650945060408801359150808211156123da57600080fd5b506123e788828901612329565b969995985093965092949392505050565b60008060006060848603121561240d57600080fd5b8335612418816122ac565b92506020840135612428816122ac565b91506040840135612438816122ac565b809150509250925092565b80356003811061245257600080fd5b919050565b6000806000806080858703121561246d57600080fd5b8435612478816122ac565b935061248660208601612443565b92506040850135612496816122ac565b9396929550929360600135925050565b60008083601f8401126124b857600080fd5b50813567ffffffffffffffff8111156124d057600080fd5b60208301915083602082850101111561236e57600080fd5b600080600080606085870312156124fe57600080fd5b8435612509816122ac565b93506020850135612519816122ac565b9250604085013567ffffffffffffffff81111561253557600080fd5b612541878288016124a6565b95989497509550505050565b60008060006060848603121561256257600080fd5b833561256d816122ac565b925060208401356002811061258157600080fd5b929592945050506040919091013590565b6000806000606084860312156125a757600080fd5b83356125b2816122ac565b92506020840135612581816122ac565b6000806000606084860312156125d757600080fd5b83356125e2816122ac565b925061242860208501612443565b60008060006060848603121561260557600080fd5b8335612610816122ac565b925060208401356005811061242857600080fd5b6000806040838503121561263757600080fd5b8235612642816122ac565b91506020830135801515811461231e57600080fd5b60006020828403121561266957600080fd5b8135612674816122ac565b9392505050565b60008060008060006080868803121561269357600080fd5b853561269e816122ac565b945060208601356126ae816122ac565b935060408601356126be816122ac565b9250606086013567ffffffffffffffff8111156126da57600080fd5b6123e7888289016124a6565b634e487b7160e01b600052602160045260246000fd5b6006811061270c5761270c6126e6565b9052565b60006001600160a01b038087168352602081871681850152608060408501528551915081608085015260005b828110156127585786810182015185820160a00152810161273c565b8281111561276a57600060a084870101525b5050601f01601f1916820160a001905061278760608301846126fc565b95945050505050565b6000602082840312156127a257600080fd5b5051919050565b8183526000602080850194508260005b858110156127e75781356127cc816122ac565b6001600160a01b0316875295820195908201906001016127b9565b509495945050505050565b81835260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83111561282457600080fd5b8260051b8083602087013760009401602001938452509192915050565b6040815260006128556040830186886127a9565b82810360208401526128688185876127f2565b979650505050505050565b8681526001600160a01b038616602082015260806040820152600061289c6080830186886127a9565b82810360608401526128af8185876127f2565b9998505050505050505050565b6003811061270c5761270c6126e6565b8581526001600160a01b03858116602083015260a08201906128f160408401876128bc565b939093166060820152608001529392505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b6001600160a01b0384168152604060208201526000612787604083018486612905565b85815260006001600160a01b03808716602084015280861660408401525060806060830152612868608083018486612905565b8481526001600160a01b038416602082015260808101600284106129aa576129aa6126e6565b60408201939093526060015292915050565b8481526001600160a01b03848116602083015260808201906129e160408401866128bc565b80841660608401525095945050505050565b8481526001600160a01b038481166020830152608082019060058510612a1b57612a1b6126e6565b84604084015280841660608401525095945050505050565b60006001600160a01b03808716835280861660208401525060606040830152612a60606083018486612905565b9695505050505050565b86815260006001600160a01b038088166020840152808716604084015280861660608401525060a06080830152612aa560a083018486612905565b9897505050505050505056fea2646970667358221220cd73b65b509940334bb91e620ef353f932d88894ac4721bdf2c9427782c3ed2664736f6c63430008090033