pragma solidity ^0.5.16; // Inheritance import "./Owned.sol"; import "./interfaces/ISystemStatus.sol"; // https://docs.oikos.cash/contracts/SystemStatus contract SystemStatus is Owned, ISystemStatus { struct Status { bool canSuspend; bool canResume; } mapping(bytes32 => mapping(address => Status)) public accessControl; struct Suspension { bool suspended; // reason is an integer code, // 0 => no reason, 1 => upgrading, 2+ => defined by system usage uint248 reason; } uint248 public constant SUSPENSION_REASON_UPGRADE = 1; bytes32 public constant SECTION_SYSTEM = "System"; bytes32 public constant SECTION_ISSUANCE = "Issuance"; bytes32 public constant SECTION_EXCHANGE = "Exchange"; bytes32 public constant SECTION_SYNTH = "Synth"; Suspension public systemSuspension; Suspension public issuanceSuspension; Suspension public exchangeSuspension; mapping(bytes32 => Suspension) public synthSuspension; constructor(address _owner) public Owned(_owner) { _internalUpdateAccessControl(SECTION_SYSTEM, _owner, true, true); _internalUpdateAccessControl(SECTION_ISSUANCE, _owner, true, true); _internalUpdateAccessControl(SECTION_EXCHANGE, _owner, true, true); _internalUpdateAccessControl(SECTION_SYNTH, _owner, true, true); } /* ========== VIEWS ========== */ function requireSystemActive() external view { _internalRequireSystemActive(); } function requireIssuanceActive() external view { // Issuance requires the system be active _internalRequireSystemActive(); require(!issuanceSuspension.suspended, "Issuance is suspended. Operation prohibited"); } function requireExchangeActive() external view { // Issuance requires the system be active _internalRequireSystemActive(); require(!exchangeSuspension.suspended, "Exchange is suspended. Operation prohibited"); } function requireSynthActive(bytes32 currencyKey) external view { // Synth exchange and transfer requires the system be active _internalRequireSystemActive(); require(!synthSuspension[currencyKey].suspended, "Synth is suspended. Operation prohibited"); } function requireSynthsActive(bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) external view { // Synth exchange and transfer requires the system be active _internalRequireSystemActive(); require( !synthSuspension[sourceCurrencyKey].suspended && !synthSuspension[destinationCurrencyKey].suspended, "One or more synths are suspended. Operation prohibited" ); } function isSystemUpgrading() external view returns (bool) { return systemSuspension.suspended && systemSuspension.reason == SUSPENSION_REASON_UPGRADE; } function getSynthSuspensions(bytes32[] calldata synths) external view returns (bool[] memory suspensions, uint256[] memory reasons) { suspensions = new bool[](synths.length); reasons = new uint256[](synths.length); for (uint i = 0; i < synths.length; i++) { suspensions[i] = synthSuspension[synths[i]].suspended; reasons[i] = synthSuspension[synths[i]].reason; } } /* ========== MUTATIVE FUNCTIONS ========== */ function updateAccessControl( bytes32 section, address account, bool canSuspend, bool canResume ) external onlyOwner { _internalUpdateAccessControl(section, account, canSuspend, canResume); } function suspendSystem(uint256 reason) external { _requireAccessToSuspend(SECTION_SYSTEM); systemSuspension.suspended = true; systemSuspension.reason = uint248(reason); emit SystemSuspended(systemSuspension.reason); } function resumeSystem() external { _requireAccessToResume(SECTION_SYSTEM); systemSuspension.suspended = false; emit SystemResumed(uint256(systemSuspension.reason)); systemSuspension.reason = 0; } function suspendIssuance(uint256 reason) external { _requireAccessToSuspend(SECTION_ISSUANCE); issuanceSuspension.suspended = true; issuanceSuspension.reason = uint248(reason); emit IssuanceSuspended(reason); } function resumeIssuance() external { _requireAccessToResume(SECTION_ISSUANCE); issuanceSuspension.suspended = false; emit IssuanceResumed(uint256(issuanceSuspension.reason)); issuanceSuspension.reason = 0; } function suspendExchange(uint256 reason) external { _requireAccessToSuspend(SECTION_EXCHANGE); exchangeSuspension.suspended = true; exchangeSuspension.reason = uint248(reason); emit ExchangeSuspended(reason); } function resumeExchange() external { _requireAccessToResume(SECTION_EXCHANGE); exchangeSuspension.suspended = false; emit ExchangeResumed(uint256(exchangeSuspension.reason)); exchangeSuspension.reason = 0; } function suspendSynth(bytes32 currencyKey, uint256 reason) external { _requireAccessToSuspend(SECTION_SYNTH); synthSuspension[currencyKey].suspended = true; synthSuspension[currencyKey].reason = uint248(reason); emit SynthSuspended(currencyKey, reason); } function resumeSynth(bytes32 currencyKey) external { _requireAccessToResume(SECTION_SYNTH); emit SynthResumed(currencyKey, uint256(synthSuspension[currencyKey].reason)); delete synthSuspension[currencyKey]; } /* ========== INTERNAL FUNCTIONS ========== */ function _requireAccessToSuspend(bytes32 section) internal view { require(accessControl[section][msg.sender].canSuspend, "Restricted to access control list"); } function _requireAccessToResume(bytes32 section) internal view { require(accessControl[section][msg.sender].canResume, "Restricted to access control list"); } function _internalRequireSystemActive() internal view { require( !systemSuspension.suspended, systemSuspension.reason == SUSPENSION_REASON_UPGRADE ? "Oikos is suspended, upgrade in progress... please stand by" : "Oikos is suspended. Operation prohibited" ); } function _internalUpdateAccessControl( bytes32 section, address account, bool canSuspend, bool canResume ) internal { require( section == SECTION_SYSTEM || section == SECTION_ISSUANCE || section == SECTION_EXCHANGE || section == SECTION_SYNTH, "Invalid section supplied" ); accessControl[section][account].canSuspend = canSuspend; accessControl[section][account].canResume = canResume; emit AccessControlUpdated(section, account, canSuspend, canResume); } /* ========== EVENTS ========== */ event SystemSuspended(uint256 reason); event SystemResumed(uint256 reason); event IssuanceSuspended(uint256 reason); event IssuanceResumed(uint256 reason); event ExchangeSuspended(uint256 reason); event ExchangeResumed(uint256 reason); event SynthSuspended(bytes32 currencyKey, uint256 reason); event SynthResumed(bytes32 currencyKey, uint256 reason); event AccessControlUpdated(bytes32 indexed section, address indexed account, bool canSuspend, bool canResume); }