pragma solidity >0.6.12; // SPDX-License-Identifier: GPL-3.0-or-later // File-Version: 2 contract CustodialAccountIndex { uint256 constant blockedField = 1 << 64; uint64 public entryCount; address[] entryList; mapping(address => uint256) public entryIndex; mapping(address => bool) writers; address public owner; address newOwner; event AddressAdded(address indexed _executor, uint256 indexed _accountIndex, address _account); // AccountsIndex event AddressActive(address indexed _executor, address _account); event AddressInactive(address indexed _executor, address _account); event AddressRemoved(address indexed _executor, address _account); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 constructor() public { owner = msg.sender; entryList.push(address(0)); } function addWriter(address _writer) public returns (bool) { require(owner == msg.sender); writers[_writer] = true; return true; } function deleteWriter(address _writer) public returns (bool) { require(owner == msg.sender); delete writers[_writer]; return true; } // Implements AccountsIndex function add(address _account) external returns (bool) { uint256 i; require(writers[msg.sender]); require(entryIndex[_account] == 0); require(entryList.length < (1 << 128)); i = entryList.length; entryList.push(_account); entryIndex[_account] = i; entryCount += 1; emit AddressAdded(msg.sender, i, _account); 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(writers[msg.sender]); require(entryIndex[_account] > 0 && entryIndex[_account] & blockedField == 0); entryIndex[_account] <<= 65; entryIndex[_account] |= blockedField; emit AddressInactive(msg.sender, _account); } // Implements AccountsIndex // Activate previously deactivated account. Will not affect the entry count. function activate(address _account) external returns (bool) { require(writers[msg.sender]); require(entryIndex[_account] > 0 && entryIndex[_account] & blockedField == blockedField); entryIndex[_account] >>= 65; emit AddressActive(msg.sender, _account); } // Implements AccountsIndex // (An account can remove itself). function remove(address _account) external returns (bool) { require(writers[msg.sender] || msg.sender == _account); require(entryIndex[_account] > 0); entryIndex[_account] = 0; entryCount = entryCount - 1; emit AddressRemoved(msg.sender, _account); return true; } // 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; } // Implements EIP173 function transferOwnership(address _newOwner) public returns (bool) { address oldOwner; require(msg.sender == owner); oldOwner = owner; owner = _newOwner; emit OwnershipTransferred(oldOwner, owner); } // Implements AccountsIndex function entry(uint256 _idx) public returns(address) { address r; r = entryList[_idx + 1]; if (entryIndex[r] > 0) { return r; } return address(0); } // 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; } }