// SPDX-License-Identifier: MIT pragma solidity ^0.8.6; import {SimpleReadAccessController} from "../../shared/access/SimpleReadAccessController.sol"; import {AccessControllerInterface} from "../../shared/interfaces/AccessControllerInterface.sol"; import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; /* dev dependencies - to be re/moved after audit */ import {FlagsInterface} from "./interfaces/FlagsInterface.sol"; /** * @title The Flags contract * @notice Allows flags to signal to any reader on the access control list. * The owner can set flags, or designate other addresses to set flags. * Raise flag actions are controlled by its own access controller. * Lower flag actions are controlled by its own access controller. * An expected pattern is to allow addresses to raise flags on themselves, so if you are subscribing to * FlagOn events you should filter for addresses you care about. */ // solhint-disable gas-custom-errors contract Flags is ITypeAndVersion, FlagsInterface, SimpleReadAccessController { AccessControllerInterface public raisingAccessController; AccessControllerInterface public loweringAccessController; mapping(address => bool) private s_flags; event FlagRaised(address indexed subject); event FlagLowered(address indexed subject); event RaisingAccessControllerUpdated(address indexed previous, address indexed current); event LoweringAccessControllerUpdated(address indexed previous, address indexed current); /** * @param racAddress address for the raising access controller. * @param lacAddress address for the lowering access controller. */ constructor(address racAddress, address lacAddress) { setRaisingAccessController(racAddress); setLoweringAccessController(lacAddress); } /** * @notice versions: * * - Flags 1.1.0: upgraded to solc 0.8, added lowering access controller * - Flags 1.0.0: initial release * * @inheritdoc ITypeAndVersion */ function typeAndVersion() external pure virtual override returns (string memory) { return "Flags 1.1.0"; } /** * @notice read the warning flag status of a contract address. * @param subject The contract address being checked for a flag. * @return A true value indicates that a flag was raised and a * false value indicates that no flag was raised. */ function getFlag(address subject) external view override checkAccess returns (bool) { return s_flags[subject]; } /** * @notice read the warning flag status of a contract address. * @param subjects An array of addresses being checked for a flag. * @return An array of bools where a true value for any flag indicates that * a flag was raised and a false value indicates that no flag was raised. */ function getFlags(address[] calldata subjects) external view override checkAccess returns (bool[] memory) { bool[] memory responses = new bool[](subjects.length); for (uint256 i = 0; i < subjects.length; i++) { responses[i] = s_flags[subjects[i]]; } return responses; } /** * @notice enable the warning flag for an address. * Access is controlled by raisingAccessController, except for owner * who always has access. * @param subject The contract address whose flag is being raised */ function raiseFlag(address subject) external override { require(_allowedToRaiseFlags(), "Not allowed to raise flags"); _tryToRaiseFlag(subject); } /** * @notice enable the warning flags for multiple addresses. * Access is controlled by raisingAccessController, except for owner * who always has access. * @param subjects List of the contract addresses whose flag is being raised */ function raiseFlags(address[] calldata subjects) external override { require(_allowedToRaiseFlags(), "Not allowed to raise flags"); for (uint256 i = 0; i < subjects.length; i++) { _tryToRaiseFlag(subjects[i]); } } /** * @notice allows owner to disable the warning flags for an addresses. * Access is controlled by loweringAccessController, except for owner * who always has access. * @param subject The contract address whose flag is being lowered */ function lowerFlag(address subject) external override { require(_allowedToLowerFlags(), "Not allowed to lower flags"); _tryToLowerFlag(subject); } /** * @notice allows owner to disable the warning flags for multiple addresses. * Access is controlled by loweringAccessController, except for owner * who always has access. * @param subjects List of the contract addresses whose flag is being lowered */ function lowerFlags(address[] calldata subjects) external override { require(_allowedToLowerFlags(), "Not allowed to lower flags"); for (uint256 i = 0; i < subjects.length; i++) { address subject = subjects[i]; _tryToLowerFlag(subject); } } /** * @notice allows owner to change the access controller for raising flags. * @param racAddress new address for the raising access controller. */ function setRaisingAccessController(address racAddress) public override onlyOwner { address previous = address(raisingAccessController); if (previous != racAddress) { raisingAccessController = AccessControllerInterface(racAddress); emit RaisingAccessControllerUpdated(previous, racAddress); } } function setLoweringAccessController(address lacAddress) public override onlyOwner { address previous = address(loweringAccessController); if (previous != lacAddress) { loweringAccessController = AccessControllerInterface(lacAddress); emit LoweringAccessControllerUpdated(previous, lacAddress); } } // PRIVATE function _allowedToRaiseFlags() private view returns (bool) { return msg.sender == owner() || raisingAccessController.hasAccess(msg.sender, msg.data); } function _allowedToLowerFlags() private view returns (bool) { return msg.sender == owner() || loweringAccessController.hasAccess(msg.sender, msg.data); } function _tryToRaiseFlag(address subject) private { if (!s_flags[subject]) { s_flags[subject] = true; emit FlagRaised(subject); } } function _tryToLowerFlag(address subject) private { if (s_flags[subject]) { s_flags[subject] = false; emit FlagLowered(subject); } } }