// SPDX-License-Identifier: GPL-3.0 // // :+#####%%%%%%%%%%%%%%+ // .-*@@@%+.:+%@@@@@%%#***%@@%= // :=*%@@@#=. :#@@% *@@@%= // .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%- // :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#. // -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+ // =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%- // -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%: // :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#. // %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*. // #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+ // *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@- // -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#: // .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#- // -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%- // -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@# // *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+- // +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=: // =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+: // .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+. // +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+. // -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=. // ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=. // @@@@@@+. +@@*. .+@@@@@%=. // -@@@@@= =@@%: -#@@@@%+. // +@@@@@. =@@@= .+@@@@@*: // #@@@@#:%@@#. :*@@@@#- // @@@@@%@@@= :#@@@@+. // :@@@@@@@#.:#@@@%- // +@@@@@@-.*@@@*: // #@@@@#.=@@@+. // @@@@+-%@%= // :@@@#%@%= // +@@@@%- // :#%%= // /** * NOTICE * * The T-REX software is licensed under a proprietary license or the GPL v.3. * If you choose to receive it under the GPL v.3 license, the following applies: * T-REX is a suite of smart contracts implementing the ERC-3643 standard and * developed by Tokeny to manage and transfer financial assets on EVM blockchains * * Copyright (C) 2023, Tokeny sàrl. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ pragma solidity 0.8.17; import "../../../token/IToken.sol"; import "../../../registry/interface/IIdentityRegistry.sol"; import "../../../registry/interface/ITrustedIssuersRegistry.sol"; import "../../../registry/interface/IClaimTopicsRegistry.sol"; import "../../../compliance/legacy/ICompliance.sol"; import "./OwnerRoles.sol"; import "../../AgentRole.sol"; import "@onchain-id/solidity/contracts/interface/IIdentity.sol"; import "@onchain-id/solidity/contracts/interface/IClaimIssuer.sol"; contract OwnerManager is OwnerRoles { /// @dev the token that is managed by this OwnerManager Contract IToken public token; /// @dev Event emitted for each executed interaction with the compliance contract. /// /// For gas efficiency, only the interaction calldata selector (first 4 /// bytes) is included in the event. For interactions without calldata or /// whose calldata is shorter than 4 bytes, the selector will be `0`. event ComplianceInteraction(address indexed target, bytes4 selector); /** * @dev the constructor initiates the OwnerManager contract * and sets msg.sender as owner of the contract * @param _token the token managed by this OwnerManager contract */ constructor(address _token) { token = IToken(_token); } /** * @dev calls the `setIdentityRegistry` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-setIdentityRegistry}. * Requires that `_onchainID` is set as RegistryAddressSetter on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetIdentityRegistry(address _identityRegistry, IIdentity _onchainID) external { require( isRegistryAddressSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Registry Address Setter" ); token.setIdentityRegistry(_identityRegistry); } /** * @dev calls the `setCompliance` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-setCompliance}. * Requires that `_onchainID` is set as ComplianceSetter on the OwnerManager contract * Requires that msg.sender is a MANAGEMENT KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetCompliance(address _compliance, IIdentity _onchainID) external { require( isComplianceSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Compliance Setter" ); token.setCompliance(_compliance); } /** * @dev calls any onlyOwner function available on the compliance contract * OwnerManager has to be set as owner on the compliance smart contract to process this function * Requires that `_onchainID` is set as ComplianceManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callComplianceFunction(bytes calldata callData, IIdentity _onchainID) external { require( isComplianceManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Compliance Manager"); address target = address(token.compliance()); // NOTE: Use assembly to call the interaction instead of a low level // call for two reasons: // - We don't want to copy the return data, since we discard it for // interactions. // - Solidity will under certain conditions generate code to copy input // calldata twice to memory (the second being a "memcopy loop"). // solhint-disable-next-line no-inline-assembly assembly { let freeMemoryPointer := mload(0x40) calldatacopy(freeMemoryPointer, callData.offset, callData.length) if iszero( call( gas(), target, 0, freeMemoryPointer, callData.length, 0, 0 )) { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } emit ComplianceInteraction(target, _selector(callData)); } /** * @dev calls the `setName` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-setName}. * Requires that `_onchainID` is set as TokenInfoManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetTokenName(string calldata _name, IIdentity _onchainID) external { require( isTokenInfoManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Token Information Manager" ); token.setName(_name); } /** * @dev calls the `setSymbol` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-setSymbol}. * Requires that `_onchainID` is set as TokenInfoManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetTokenSymbol(string calldata _symbol, IIdentity _onchainID) external { require( isTokenInfoManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Token Information Manager" ); token.setSymbol(_symbol); } /** * @dev calls the `setOnchainID` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-setOnchainID}. * Requires that `_tokenOnchainID` is set as TokenInfoManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetTokenOnchainID(address _tokenOnchainID, IIdentity _onchainID) external { require( isTokenInfoManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Token Information Manager" ); token.setOnchainID(_tokenOnchainID); } /** * @dev calls the `setClaimTopicsRegistry` function on the Identity Registry contract * OwnerManager has to be set as owner on the Identity Registry smart contract to process this function * See {IIdentityRegistry-setClaimTopicsRegistry}. * Requires that `_onchainID` is set as RegistryAddressSetter on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetClaimTopicsRegistry(address _claimTopicsRegistry, IIdentity _onchainID) external { require( isRegistryAddressSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Registry Address Setter" ); token.identityRegistry().setClaimTopicsRegistry(_claimTopicsRegistry); } /** * @dev calls the `setTrustedIssuersRegistry` function on the Identity Registry contract * OwnerManager has to be set as owner on the Identity Registry smart contract to process this function * See {IIdentityRegistry-setTrustedIssuersRegistry}. * Requires that `_onchainID` is set as RegistryAddressSetter on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callSetTrustedIssuersRegistry(address _trustedIssuersRegistry, IIdentity _onchainID) external { require( isRegistryAddressSetter(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT Registry Address Setter" ); token.identityRegistry().setTrustedIssuersRegistry(_trustedIssuersRegistry); } /** * @dev calls the `addTrustedIssuer` function on the Trusted Issuers Registry contract * OwnerManager has to be set as owner on the Trusted Issuers Registry smart contract to process this function * See {ITrustedIssuersRegistry-addTrustedIssuer}. * Requires that `_onchainID` is set as IssuersRegistryManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callAddTrustedIssuer( IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics, IIdentity _onchainID ) external { require( isIssuersRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT IssuersRegistryManager" ); token.identityRegistry().issuersRegistry().addTrustedIssuer(_trustedIssuer, _claimTopics); } /** * @dev calls the `removeTrustedIssuer` function on the Trusted Issuers Registry contract * OwnerManager has to be set as owner on the Trusted Issuers Registry smart contract to process this function * See {ITrustedIssuersRegistry-removeTrustedIssuer}. * Requires that `_onchainID` is set as IssuersRegistryManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callRemoveTrustedIssuer(IClaimIssuer _trustedIssuer, IIdentity _onchainID) external { require( isIssuersRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT IssuersRegistryManager" ); token.identityRegistry().issuersRegistry().removeTrustedIssuer(_trustedIssuer); } /** * @dev calls the `updateIssuerClaimTopics` function on the Trusted Issuers Registry contract * OwnerManager has to be set as owner on the Trusted Issuers Registry smart contract to process this function * See {ITrustedIssuersRegistry-updateIssuerClaimTopics}. * Requires that `_onchainID` is set as IssuersRegistryManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callUpdateIssuerClaimTopics( IClaimIssuer _trustedIssuer, uint256[] calldata _claimTopics, IIdentity _onchainID ) external { require( isIssuersRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT IssuersRegistryManager" ); token.identityRegistry().issuersRegistry().updateIssuerClaimTopics(_trustedIssuer, _claimTopics); } /** * @dev calls the `addClaimTopic` function on the Claim Topics Registry contract * OwnerManager has to be set as owner on the Claim Topics Registry smart contract to process this function * See {IClaimTopicsRegistry-addClaimTopic}. * Requires that `_onchainID` is set as ClaimRegistryManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callAddClaimTopic(uint256 _claimTopic, IIdentity _onchainID) external { require( isClaimRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT ClaimRegistryManager" ); token.identityRegistry().topicsRegistry().addClaimTopic(_claimTopic); } /** * @dev calls the `removeClaimTopic` function on the Claim Topics Registry contract * OwnerManager has to be set as owner on the Claim Topics Registry smart contract to process this function * See {IClaimTopicsRegistry-removeClaimTopic}. * Requires that `_onchainID` is set as ClaimRegistryManager on the OwnerManager contract * Requires that msg.sender is an ACTION KEY on `_onchainID` * @param _onchainID the _onchainID contract of the caller, e.g. "i call this function and i am Bob" */ function callRemoveClaimTopic(uint256 _claimTopic, IIdentity _onchainID) external { require( isClaimRegistryManager(address(_onchainID)) && _onchainID.keyHasPurpose(keccak256(abi.encode(msg.sender)), 2), "Role: Sender is NOT ClaimRegistryManager" ); token.identityRegistry().topicsRegistry().removeClaimTopic(_claimTopic); } /** * @dev calls the `transferOwnershipOnTokenContract` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-transferOwnershipOnTokenContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callTransferOwnershipOnTokenContract(address _newOwner) external onlyAdmin { Ownable(address(token)).transferOwnership(_newOwner); } /** * @dev calls the `transferOwnershipOnIdentityRegistryContract` function on the Identity Registry contract * OwnerManager has to be set as owner on the Identity Registry smart contract to process this function * See {IIdentityRegistry-transferOwnershipOnIdentityRegistryContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callTransferOwnershipOnIdentityRegistryContract(address _newOwner) external onlyAdmin { Ownable(address(token.identityRegistry())).transferOwnership(_newOwner); } /** * @dev calls the `transferOwnershipOnComplianceContract` function on the Compliance contract * OwnerManager has to be set as owner on the Compliance smart contract to process this function * See {ICompliance-transferOwnershipOnComplianceContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callTransferOwnershipOnComplianceContract(address _newOwner) external onlyAdmin { Ownable(address(token.compliance())).transferOwnership(_newOwner); } /** * @dev calls the `transferOwnershipOnClaimTopicsRegistryContract` function on the Claim Topics Registry contract * OwnerManager has to be set as owner on the Claim Topics registry smart contract to process this function * See {IClaimTopicsRegistry-transferOwnershipOnClaimTopicsRegistryContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callTransferOwnershipOnClaimTopicsRegistryContract(address _newOwner) external onlyAdmin { Ownable(address(token.identityRegistry().topicsRegistry())).transferOwnership(_newOwner); } /** * @dev calls the `transferOwnershipOnIssuersRegistryContract` function on the Trusted Issuers Registry contract * OwnerManager has to be set as owner on the Trusted Issuers registry smart contract to process this function * See {ITrustedIssuersRegistry-transferOwnershipOnIssuersRegistryContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callTransferOwnershipOnIssuersRegistryContract(address _newOwner) external onlyAdmin { Ownable(address(token.identityRegistry().issuersRegistry())).transferOwnership(_newOwner); } /** * @dev calls the `addAgentOnTokenContract` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-addAgentOnTokenContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callAddAgentOnTokenContract(address _agent) external onlyAdmin { AgentRole(address(token)).addAgent(_agent); } /** * @dev calls the `removeAgentOnTokenContract` function on the token contract * OwnerManager has to be set as owner on the token smart contract to process this function * See {IToken-removeAgentOnTokenContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callRemoveAgentOnTokenContract(address _agent) external onlyAdmin { AgentRole(address(token)).removeAgent(_agent); } /** * @dev calls the `addAgentOnIdentityRegistryContract` function on the Identity Registry contract * OwnerManager has to be set as owner on the Identity Registry smart contract to process this function * See {IIdentityRegistry-addAgentOnIdentityRegistryContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callAddAgentOnIdentityRegistryContract(address _agent) external onlyAdmin { AgentRole(address(token.identityRegistry())).addAgent(_agent); } /** * @dev calls the `removeAgentOnIdentityRegistryContract` function on the Identity Registry contract * OwnerManager has to be set as owner on the Identity Registry smart contract to process this function * See {IIdentityRegistry-removeAgentOnIdentityRegistryContract}. * Requires that msg.sender is an Admin of the OwnerManager contract */ function callRemoveAgentOnIdentityRegistryContract(address _agent) external onlyAdmin { AgentRole(address(token.identityRegistry())).removeAgent(_agent); } /// @dev Extracts the Solidity ABI selector for the specified interaction. /// @param callData Interaction data. /// @return result The 4 byte function selector of the call encoded in /// this interaction. function _selector(bytes calldata callData) internal pure returns (bytes4 result) { if (callData.length >= 4) { // NOTE: Read the first word of the interaction's calldata. The // value does not need to be shifted since `bytesN` values are left // aligned, and the value does not need to be masked since masking // occurs when the value is accessed and not stored: // // // solhint-disable-next-line no-inline-assembly assembly { result := calldataload(callData.offset) } } } }