pragma solidity >=0.8.0; // SPDX-License-Identifier: AGPL-3.0-or-later // File-Version: 3 contract AccountsIndex { uint256 constant blockedField = 1 << 128; address[] entryList; mapping(address => uint256) entryIndex; // Implements Writer mapping(address => bool) public isWriter; // Implements ERC173 address public owner; // Implements AccountsIndex event AddressAdded(address _account); // AccountsIndex // Implements AccountsIndex event AddressActive(address indexed _account, bool _active); // Implements AccountsIndex event AddressRemoved(address _account); // Implements ERC173 event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 // Implements Writer event WriterAdded(address _account); // AccountsIndex // Implements Writer event WriterDeleted(address _account); constructor() { owner = msg.sender; entryList.push(address(0)); } function entryCount() external view returns (uint256) { return entryList.length - 1; } function addWriter(address _writer) public returns (bool) { require(owner == msg.sender); isWriter[_writer] = true; emit WriterAdded(_writer); return true; } function deleteWriter(address _writer) public returns (bool) { require(owner == msg.sender); delete isWriter[_writer]; emit WriterDeleted(_writer); return true; } // Implements AccountsIndex function add(address _account) external returns (bool) { uint256 i; uint256 _entry; require(isWriter[msg.sender]); require(entryIndex[_account] == 0); require(entryList.length < (1 << 64)); i = entryList.length; entryList.push(_account); _entry = uint64(i); _entry |= block.timestamp << 64; entryIndex[_account] = _entry; emit AddressAdded(_account); return true; } // Implements AccountsIndex function remove(address _account) external returns (bool) { uint256 i; uint256 l; require(isWriter[msg.sender]); require(this.have(_account)); l = entryList.length - 1; i = entryIndex[_account]; if (i < l) { entryList[i] = entryList[l]; } entryList.pop(); entryIndex[_account] = 0; emit AddressRemoved(_account); return true; } // Implements AccountsIndex // Activate previously deactivated account. Will not affect the entry count. function activate(address _account) external returns (bool) { require(isWriter[msg.sender]); require(entryIndex[_account] > 0 && entryIndex[_account] & blockedField == blockedField); entryIndex[_account] >>= 129; emit AddressActive(_account, true); return true; } // Implements AccountsIndex // Deactivate account, without removing the entry. The entry will still be part of the entry count. function deactivate(address _account) external returns (bool) { require(isWriter[msg.sender]); require(entryIndex[_account] > 0 && entryIndex[_account] & blockedField == 0); entryIndex[_account] <<= 129; entryIndex[_account] |= blockedField; emit AddressActive(_account, false); return true; } // Implements AccountsIndex function entry(uint256 _i) external view returns (address) { return entryList[_i + 1]; } // Implements AccountsIndex function time(address _account) external view returns (uint256) { require(entryIndex[_account] > 0); return entryIndex[_account] >> 64; } // Implements AccountsIndex function have(address _account) external view returns (bool) { return entryIndex[_account] > 0; } // Implements AccountsIndex function isActive(address _account) external view returns (bool) { return this.have(_account) && entryIndex[_account] & blockedField != blockedField; } // Implements EIP173 function transferOwnership(address _newOwner) public returns (bool) { address oldOwner; require(msg.sender == owner); oldOwner = owner; owner = _newOwner; emit OwnershipTransferred(oldOwner, owner); return true; } // Implements EIP165 function supportsInterface(bytes4 _sum) public pure returns (bool) { if (_sum == 0x23c5568b) { // AccountsIndex return true; } if (_sum == 0x01ffc9a7) { // EIP165 return true; } if (_sum == 0x9493f8b2) { // EIP173 return true; } if (_sum == 0xabe1f1f5) { // Writer return true; } return false; } }