forked from trusttoken/contracts-pre22
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🦅 Feat: add Registry with initialize function (trusttoken#1247)
* fix: add Registry.sol * fix: add initialize to Registry * fix: set owner and initialized state in impl contract
- Loading branch information
Showing
1 changed file
with
207 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |