// SPDX-License-Identifier: LGPL-3.0-only pragma solidity >=0.7.0 <0.9.0; import "../common/SelfAuthorized.sol"; /** * @title OwnerManager - Manages Safe owners and a threshold to authorize transactions. * @dev Uses a linked list to store the owners because the code generate by the solidity compiler * is more efficient than using a dynamic array. * @author Stefan George - @Georgi87 * @author Richard Meissner - @rmeissner */ abstract contract OwnerManager is SelfAuthorized { event AddedOwner(address indexed owner); event RemovedOwner(address indexed owner); event ChangedThreshold(uint256 threshold); address internal constant SENTINEL_OWNERS = address(0x1); mapping(address => address) internal owners; uint256 internal ownerCount; uint256 internal threshold; /** * @notice Sets the initial storage of the contract. * @param _owners List of Safe owners. * @param _threshold Number of required confirmations for a Safe transaction. */ function setupOwners(address[] memory _owners, uint256 _threshold) internal { // Threshold can only be 0 at initialization. // Check ensures that setup function can only be called once. require(threshold == 0, "GS200"); // Validate that threshold is smaller than number of added owners. require(_threshold <= _owners.length, "GS201"); // There has to be at least one Safe owner. require(_threshold >= 1, "GS202"); // Initializing Safe owners. address currentOwner = SENTINEL_OWNERS; for (uint256 i = 0; i < _owners.length; i++) { // Owner address cannot be null. address owner = _owners[i]; require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this) && currentOwner != owner, "GS203"); // No duplicate owners allowed. require(owners[owner] == address(0), "GS204"); owners[currentOwner] = owner; currentOwner = owner; } owners[currentOwner] = SENTINEL_OWNERS; ownerCount = _owners.length; threshold = _threshold; } /** * @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`. * @dev This can only be done via a Safe transaction. * @param owner New owner address. * @param _threshold New threshold. */ function addOwnerWithThreshold(address owner, uint256 _threshold) public authorized { // Owner address cannot be null, the sentinel or the Safe itself. require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this), "GS203"); // No duplicate owners allowed. require(owners[owner] == address(0), "GS204"); owners[owner] = owners[SENTINEL_OWNERS]; owners[SENTINEL_OWNERS] = owner; ownerCount++; emit AddedOwner(owner); // Change threshold if threshold was changed. if (threshold != _threshold) changeThreshold(_threshold); } /** * @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`. * @dev This can only be done via a Safe transaction. * @param prevOwner Owner that pointed to the owner to be removed in the linked list * @param owner Owner address to be removed. * @param _threshold New threshold. */ function removeOwner(address prevOwner, address owner, uint256 _threshold) public authorized { // Only allow to remove an owner, if threshold can still be reached. require(ownerCount - 1 >= _threshold, "GS201"); // Validate owner address and check that it corresponds to owner index. require(owner != address(0) && owner != SENTINEL_OWNERS, "GS203"); require(owners[prevOwner] == owner, "GS205"); owners[prevOwner] = owners[owner]; owners[owner] = address(0); ownerCount--; emit RemovedOwner(owner); // Change threshold if threshold was changed. if (threshold != _threshold) changeThreshold(_threshold); } /** * @notice Replaces the owner `oldOwner` in the Safe with `newOwner`. * @dev This can only be done via a Safe transaction. * @param prevOwner Owner that pointed to the owner to be replaced in the linked list * @param oldOwner Owner address to be replaced. * @param newOwner New owner address. */ function swapOwner(address prevOwner, address oldOwner, address newOwner) public authorized { // Owner address cannot be null, the sentinel or the Safe itself. require(newOwner != address(0) && newOwner != SENTINEL_OWNERS && newOwner != address(this), "GS203"); // No duplicate owners allowed. require(owners[newOwner] == address(0), "GS204"); // Validate oldOwner address and check that it corresponds to owner index. require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "GS203"); require(owners[prevOwner] == oldOwner, "GS205"); owners[newOwner] = owners[oldOwner]; owners[prevOwner] = newOwner; owners[oldOwner] = address(0); emit RemovedOwner(oldOwner); emit AddedOwner(newOwner); } /** * @notice Changes the threshold of the Safe to `_threshold`. * @dev This can only be done via a Safe transaction. * @param _threshold New threshold. */ function changeThreshold(uint256 _threshold) public authorized { // Validate that threshold is smaller than number of owners. require(_threshold <= ownerCount, "GS201"); // There has to be at least one Safe owner. require(_threshold >= 1, "GS202"); threshold = _threshold; emit ChangedThreshold(threshold); } /** * @notice Returns the number of required confirmations for a Safe transaction aka the threshold. * @return Threshold number. */ function getThreshold() public view returns (uint256) { return threshold; } /** * @notice Returns if `owner` is an owner of the Safe. * @return Boolean if owner is an owner of the Safe. */ function isOwner(address owner) public view returns (bool) { return owner != SENTINEL_OWNERS && owners[owner] != address(0); } /** * @notice Returns a list of Safe owners. * @return Array of Safe owners. */ function getOwners() public view returns (address[] memory) { address[] memory array = new address[](ownerCount); // populate return array uint256 index = 0; address currentOwner = owners[SENTINEL_OWNERS]; while (currentOwner != SENTINEL_OWNERS) { array[index] = currentOwner; currentOwner = owners[currentOwner]; index++; } return array; } }