Skip to content

Commit

Permalink
🦅 Feat: add Registry with initialize function (trusttoken#1247)
Browse files Browse the repository at this point in the history
* fix: add Registry.sol

* fix: add initialize to Registry

* fix: set owner and initialized state in impl contract
  • Loading branch information
qiwihui authored Mar 24, 2023
1 parent b1384be commit e947606
Showing 1 changed file with 207 additions and 0 deletions.
207 changes: 207 additions & 0 deletions packages/contracts-bsc/contracts/Registry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.6.10;

import {IBEP20} from "./interface/IBEP20.sol";

interface RegistryClone {
function syncAttributeValue(
address _who,
bytes32 _attribute,
uint256 _value
) external;
}

contract Registry {
struct AttributeData {
uint256 value;
bytes32 notes;
address adminAddr;
uint256 timestamp;
}

// never remove any storage variables
address public owner;
address public pendingOwner;
bool initialized;

// Stores arbitrary attributes for users. An example use case is an IERC20
// token that requires its users to go through a KYC/AML check - in this case
// a validator can set an account's "hasPassedKYC/AML" attribute to 1 to indicate
// that account can use the token. This mapping stores that value (1, in the
// example) as well as which validator last set the value and at what time,
// so that e.g. the check can be renewed at appropriate intervals.
mapping(address => mapping(bytes32 => AttributeData)) attributes;
// The logic governing who is allowed to set what attributes is abstracted as
// this accessManager, so that it may be replaced by the owner as needed
bytes32 constant WRITE_PERMISSION = keccak256("canWriteTo-");
mapping(bytes32 => RegistryClone[]) subscribers;

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event SetAttribute(address indexed who, bytes32 attribute, uint256 value, bytes32 notes, address indexed adminAddr);
event SetManager(address indexed oldManager, address indexed newManager);
event StartSubscription(bytes32 indexed attribute, RegistryClone indexed subscriber);
event StopSubscription(bytes32 indexed attribute, RegistryClone indexed subscriber);

/**
* @dev sets the original `owner` of the contract to the sender
* at construction. Must then be reinitialized
*/
constructor() public {
owner = address(0);
initialized = true;
}

function initialize() public {
require(!initialized, "already initialized");
initialized = true;
owner = msg.sender;
emit OwnershipTransferred(address(0), owner);
}

// Allows a write if either a) the writer is that Registry's owner, or
// b) the writer is writing to attribute foo and that writer already has
// the canWriteTo-foo attribute set (in that same Registry)
function confirmWrite(bytes32 _attribute, address _admin) internal view returns (bool) {
return (_admin == owner || hasAttribute(_admin, keccak256(abi.encodePacked(WRITE_PERMISSION ^ _attribute))));
}

// Writes are allowed only if the accessManager approves
function setAttribute(
address _who,
bytes32 _attribute,
uint256 _value,
bytes32 _notes
) public {
require(confirmWrite(_attribute, msg.sender));
attributes[_who][_attribute] = AttributeData(_value, _notes, msg.sender, block.timestamp);
emit SetAttribute(_who, _attribute, _value, _notes, msg.sender);

RegistryClone[] storage targets = subscribers[_attribute];
uint256 index = targets.length;
while (index-- > 0) {
targets[index].syncAttributeValue(_who, _attribute, _value);
}
}

function subscribe(bytes32 _attribute, RegistryClone _syncer) external onlyOwner {
subscribers[_attribute].push(_syncer);
emit StartSubscription(_attribute, _syncer);
}

function unsubscribe(bytes32 _attribute, uint256 _index) external onlyOwner {
uint256 length = subscribers[_attribute].length;
require(_index < length);
emit StopSubscription(_attribute, subscribers[_attribute][_index]);
subscribers[_attribute][_index] = subscribers[_attribute][length - 1];
subscribers[_attribute].pop();
}

function subscriberCount(bytes32 _attribute) public view returns (uint256) {
return subscribers[_attribute].length;
}

function setAttributeValue(
address _who,
bytes32 _attribute,
uint256 _value
) public {
require(confirmWrite(_attribute, msg.sender));
attributes[_who][_attribute] = AttributeData(_value, "", msg.sender, block.timestamp);
emit SetAttribute(_who, _attribute, _value, "", msg.sender);
RegistryClone[] storage targets = subscribers[_attribute];
uint256 index = targets.length;
while (index-- > 0) {
targets[index].syncAttributeValue(_who, _attribute, _value);
}
}

// Returns true if the uint256 value stored for this attribute is non-zero
function hasAttribute(address _who, bytes32 _attribute) public view returns (bool) {
return attributes[_who][_attribute].value != 0;
}

// Returns the exact value of the attribute, as well as its metadata
function getAttribute(address _who, bytes32 _attribute)
public
view
returns (
uint256,
bytes32,
address,
uint256
)
{
AttributeData memory data = attributes[_who][_attribute];
return (data.value, data.notes, data.adminAddr, data.timestamp);
}

function getAttributeValue(address _who, bytes32 _attribute) public view returns (uint256) {
return attributes[_who][_attribute].value;
}

function getAttributeAdminAddr(address _who, bytes32 _attribute) public view returns (address) {
return attributes[_who][_attribute].adminAddr;
}

function getAttributeTimestamp(address _who, bytes32 _attribute) public view returns (uint256) {
return attributes[_who][_attribute].timestamp;
}

function syncAttribute(
bytes32 _attribute,
uint256 _startIndex,
address[] calldata _addresses
) external {
RegistryClone[] storage targets = subscribers[_attribute];
uint256 index = targets.length;
while (index-- > _startIndex) {
RegistryClone target = targets[index];
for (uint256 i = _addresses.length; i-- > 0; ) {
address who = _addresses[i];
target.syncAttributeValue(who, _attribute, attributes[who][_attribute].value);
}
}
}

function reclaimEther(address payable _to) external onlyOwner {
_to.transfer(address(this).balance);
}

function reclaimToken(IBEP20 token, address _to) external onlyOwner {
uint256 balance = token.balanceOf(address(this));
token.transfer(_to, balance);
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner, "only Owner");
_;
}

/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner);
_;
}

/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
pendingOwner = newOwner;
}

/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public onlyPendingOwner {
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}

0 comments on commit e947606

Please sign in to comment.