pragma solidity >0.6.12; // SPDX-License-Identifier: GPL-3.0-or-later // File-Version: 2 contract CustodialAccountIndex { uint256 constant blockedField = 1 << 128; address[] entryList; mapping(address => uint256) public entryIndex; mapping(address => bool) public isWriter; address public owner; address newOwner; event AddressAdded(address indexed _executor, address _account); // AccountsIndex event AddressActive(address indexed _executor, address indexed _account, bool _active); event AddressRemoved(address indexed _executor, address _account); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 event WriterAdded(address indexed _executor, address _account); // AccountsIndex event WriterDeleted(address indexed _executor, 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(msg.sender, _writer); return true; } function deleteWriter(address _writer) public returns (bool) { require(owner == msg.sender); delete isWriter[_writer]; emit WriterDeleted(msg.sender, _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(msg.sender, _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(msg.sender, _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(msg.sender, _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(msg.sender, _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 == 0xcbdb05c7) { // AccountsIndex return true; } if (_sum == 0x01ffc9a7) { // EIP165 return true; } if (_sum == 0x9493f8b2) { // EIP173 return true; } if (_sum == 0x80c84bd6) { // Writer return true; } return false; } }