// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../libs/ECDSALib.sol"; /** * @title Guarded * * @dev Contract module which provides a guardian-type control mechanism. * It allows key accounts to have guardians and restricts specific methods to be accessible by guardians only. * * Each guardian account can remove other guardians * * Use `_initializeGuarded` to initialize the contract * * @author Stanisław Głogowski */ contract Guarded { using ECDSALib for bytes32; mapping(address => bool) private guardians; // events /** * @dev Emitted when a new guardian is added * @param sender sender address * @param guardian guardian address */ event GuardianAdded( address sender, address guardian ); /** * @dev Emitted when the existing guardian is removed * @param sender sender address * @param guardian guardian address */ event GuardianRemoved( address sender, address guardian ); // modifiers /** * @dev Throws if tx.origin is not a guardian account */ modifier onlyGuardian() { require( // solhint-disable-next-line avoid-tx-origin guardians[tx.origin], "Guarded: tx.origin is not the guardian" ); _; } /** * @dev Internal constructor */ constructor() internal {} // external functions /** * @notice Adds a new guardian * @param guardian guardian address */ function addGuardian( address guardian ) external onlyGuardian { _addGuardian(guardian); } /** * @notice Removes the existing guardian * @param guardian guardian address */ function removeGuardian( address guardian ) external onlyGuardian { require( // solhint-disable-next-line avoid-tx-origin tx.origin != guardian, "Guarded: cannot remove self" ); require( guardians[guardian], "Guarded: guardian doesn't exist" ); guardians[guardian] = false; emit GuardianRemoved( // solhint-disable-next-line avoid-tx-origin tx.origin, guardian ); } // external functions (views) /** * @notice Check if guardian exists * @param guardian guardian address * @return true when guardian exists */ function isGuardian( address guardian ) external view returns (bool) { return guardians[guardian]; } /** * @notice Verifies guardian signature * @param messageHash message hash * @param signature signature * @return true on correct guardian signature */ function verifyGuardianSignature( bytes32 messageHash, bytes calldata signature ) external view returns (bool) { return _verifyGuardianSignature( messageHash, signature ); } // internal functions /** * @notice Initializes `Guarded` contract * @dev If `guardians_` array is empty `tx.origin` is added as guardian account * @param guardians_ array of guardians addresses */ function _initializeGuarded( address[] memory guardians_ ) internal { if (guardians_.length == 0) { // solhint-disable-next-line avoid-tx-origin _addGuardian(tx.origin); } else { uint guardiansLen = guardians_.length; for (uint i = 0; i < guardiansLen; i++) { _addGuardian(guardians_[i]); } } } // internal functions (views) function _verifyGuardianSignature( bytes32 messageHash, bytes memory signature ) internal view returns (bool) { address guardian = messageHash.recoverAddress(signature); return guardians[guardian]; } // private functions function _addGuardian( address guardian ) private { require( guardian != address(0), "Guarded: cannot add 0x0 guardian" ); require( !guardians[guardian], "Guarded: guardian already exists" ); guardians[guardian] = true; emit GuardianAdded( // solhint-disable-next-line avoid-tx-origin tx.origin, guardian ); } }