// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.6;
import "./../interfaces/utils/IGovernable.sol";
/**
* @title Governable
* @author solace.fi
* @notice Enforces access control for important functions to [**governor**](/docs/protocol/governance).
*
* Many contracts contain functionality that should only be accessible to a privileged user. The most common access control pattern is [OpenZeppelin's `Ownable`](https://docs.openzeppelin.com/contracts/4.x/access-control#ownership-and-ownable). We instead use `Governable` with a few key differences:
* - Transferring the governance role is a two step process. The current governance must [`setPendingGovernance(pendingGovernance_)`](#setpendinggovernance) then the new governance must [`acceptGovernance()`](#acceptgovernance). This is to safeguard against accidentally setting ownership to the wrong address and locking yourself out of your contract.
* - `governance` is a constructor argument instead of `msg.sender`. This is especially useful when deploying contracts via a [`SingletonFactory`](./../interfaces/utils/ISingletonFactory).
* - We use `lockGovernance()` instead of `renounceOwnership()`. `renounceOwnership()` is a prerequisite for the reinitialization bug because it sets `owner = address(0x0)`. We also use the `governanceIsLocked()` flag.
*/
contract Governable is IGovernable {
/***************************************
GLOBAL VARIABLES
***************************************/
// Governor.
address private _governance;
// governance to take over.
address private _pendingGovernance;
bool private _locked;
/**
* @notice Constructs the governable contract.
* @param governance_ The address of the [governor](/docs/protocol/governance).
*/
constructor(address governance_) {
require(governance_ != address(0x0), "zero address governance");
_governance = governance_;
_pendingGovernance = address(0x0);
_locked = false;
}
/***************************************
MODIFIERS
***************************************/
// can only be called by governor
// can only be called while unlocked
modifier onlyGovernance() {
require(!_locked, "governance locked");
require(msg.sender == _governance, "!governance");
_;
}
// can only be called by pending governor
// can only be called while unlocked
modifier onlyPendingGovernance() {
require(!_locked, "governance locked");
require(msg.sender == _pendingGovernance, "!pending governance");
_;
}
/***************************************
VIEW FUNCTIONS
***************************************/
/// @notice Address of the current governor.
function governance() public view override returns (address) {
return _governance;
}
/// @notice Address of the governor to take over.
function pendingGovernance() external view override returns (address) {
return _pendingGovernance;
}
/// @notice Returns true if governance is locked.
function governanceIsLocked() external view override returns (bool) {
return _locked;
}
/***************************************
MUTATOR FUNCTIONS
***************************************/
/**
* @notice Initiates transfer of the governance role to a new governor.
* Transfer is not complete until the new governor accepts the role.
* Can only be called by the current [**governor**](/docs/protocol/governance).
* @param pendingGovernance_ The new governor.
*/
function setPendingGovernance(address pendingGovernance_) external override onlyGovernance {
_pendingGovernance = pendingGovernance_;
emit GovernancePending(pendingGovernance_);
}
/**
* @notice Accepts the governance role.
* Can only be called by the pending governor.
*/
function acceptGovernance() external override onlyPendingGovernance {
// sanity check against transferring governance to the zero address
// if someone figures out how to sign transactions from the zero address
// consider the entirety of ethereum to be rekt
require(_pendingGovernance != address(0x0), "zero governance");
address oldGovernance = _governance;
_governance = _pendingGovernance;
_pendingGovernance = address(0x0);
emit GovernanceTransferred(oldGovernance, _governance);
}
/**
* @notice Permanently locks this contract's governance role and any of its functions that require the role.
* This action cannot be reversed.
* Before you call it, ask yourself:
* - Is the contract self-sustaining?
* - Is there a chance you will need governance privileges in the future?
* Can only be called by the current [**governor**](/docs/protocol/governance).
*/
function lockGovernance() external override onlyGovernance {
_locked = true;
// intentionally not using address(0x0), see re-initialization exploit
_governance = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
_pendingGovernance = address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF);
emit GovernanceTransferred(msg.sender, address(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF));
emit GovernanceLocked();
}
}
|