diff --git a/contracts/CMTAT_PROXY_UUPS.sol b/contracts/CMTAT_PROXY_UUPS.sol new file mode 100644 index 00000000..0c48a5be --- /dev/null +++ b/contracts/CMTAT_PROXY_UUPS.sol @@ -0,0 +1,60 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; +import "../openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol"; +import "./modules/CMTAT_BASE.sol"; + +contract CMTAT_PROXY_UUPS is CMTAT_BASE, UUPSUpgradeable { + /** + * @notice Contract version for the deployment with a proxy + * @param forwarderIrrevocable address of the forwarder, required for the gasless support + */ + /// @custom:oz-upgrades-unsafe-allow constructor + constructor( + address forwarderIrrevocable + ) MetaTxModule(forwarderIrrevocable) { + // Disable the possibility to initialize the implementation + _disableInitializers(); + } + + /** + * @notice + * initialize the proxy contract + * The calls to this function will revert if the contract was deployed without a proxy + * @param admin address of the admin of contract (Access Control) + * @param nameIrrevocable name of the token + * @param symbolIrrevocable name of the symbol + * @param decimalsIrrevocable number of decimals of the token, must be 0 to be compliant with Swiss law as per CMTAT specifications (non-zero decimal number may be needed for other use cases) + * @param tokenId_ name of the tokenId + * @param terms_ terms associated with the token + * @param ruleEngine_ address of the ruleEngine to apply rules to transfers + * @param information_ additional information to describe the token + * @param flag_ add information under the form of bit(0, 1) + */ + function initialize( address admin, + IAuthorizationEngine authorizationEngineIrrevocable, + string memory nameIrrevocable, + string memory symbolIrrevocable, + uint8 decimalsIrrevocable, + string memory tokenId_, + string memory terms_, + IRuleEngine ruleEngine_, + string memory information_, + uint256 flag_) public override initializer { + CMTAT_BASE.initialize( admin, + authorizationEngineIrrevocable, + nameIrrevocable, + symbolIrrevocable, + decimalsIrrevocable, + tokenId_, + terms_, + ruleEngine_, + information_, + flag_); + __UUPSUpgradeable_init_unchained(); + } + + function _authorizeUpgrade(address) internal override onlyRole("DEFAULT_ADMIN_ROLE") {} + + uint256[50] private __gap; +} diff --git a/contracts/interfaces/engine/IDebtEngine.sol b/contracts/interfaces/engine/IDebtEngine.sol new file mode 100644 index 00000000..75052aa4 --- /dev/null +++ b/contracts/interfaces/engine/IDebtEngine.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; +import "../IDebtGlobal.sol"; + +interface IDebtEngine is IDebtGlobal { + /** + * @dev Returns true if the operation is authorized, and false otherwise. + */ + function debt() external returns(IDebtGlobal.DebtBase memory); + /** + * @dev Returns true if the operation is authorized, and false otherwise. + */ + function creditEvents() external returns(IDebtGlobal.CreditEvents memory); + +} diff --git a/contracts/modules/CMTAT_BASE.sol b/contracts/modules/CMTAT_BASE.sol index 36ce86c8..ace05698 100644 --- a/contracts/modules/CMTAT_BASE.sol +++ b/contracts/modules/CMTAT_BASE.sol @@ -20,8 +20,8 @@ import "./wrapper/extensions/ERC20SnapshotModule.sol"; import "./wrapper/controllers/ValidationModule.sol"; import "./wrapper/extensions/MetaTxModule.sol"; -import "./wrapper/extensions/DebtModule/DebtBaseModule.sol"; -import "./wrapper/extensions/DebtModule/CreditEventsModule.sol"; +import "./wrapper/extensions/DebtModule.sol"; +import "./wrapper/extensions/DocumentModule.sol"; import "./security/AuthorizationModule.sol"; import "../libraries/Errors.sol"; @@ -29,15 +29,19 @@ import "../libraries/Errors.sol"; abstract contract CMTAT_BASE is Initializable, ContextUpgradeable, + // Core BaseModule, PauseModule, ERC20MintModule, ERC20BurnModule, EnforcementModule, ValidationModule, - MetaTxModule, ERC20BaseModule, - ERC20SnapshotModule + // Extension + MetaTxModule, + ERC20SnapshotModule, + DebtModule, + DocumentModule { /** * @notice @@ -64,7 +68,7 @@ abstract contract CMTAT_BASE is IRuleEngine ruleEngine_, string memory information_, uint256 flag_ - ) public initializer { + ) public virtual initializer { __CMTAT_init( admin, authorizationEngineIrrevocable, diff --git a/contracts/modules/security/AuthorizationModule.sol b/contracts/modules/security/AuthorizationModule.sol index 7870b318..e626b3a1 100644 --- a/contracts/modules/security/AuthorizationModule.sol +++ b/contracts/modules/security/AuthorizationModule.sol @@ -27,9 +27,8 @@ abstract contract AuthorizationModule is AccessControlUpgradeable { _grantRole(DEFAULT_ADMIN_ROLE, admin); if (address(authorizationEngine) != address (0)) { authorizationEngine = authorizationEngine_; + emit AuthorizationEngine(authorizationEngine_); } - - } /* diff --git a/contracts/modules/wrapper/controllers/ValidationModule.sol b/contracts/modules/wrapper/controllers/ValidationModule.sol index 63fe5183..6e5ffd5f 100644 --- a/contracts/modules/wrapper/controllers/ValidationModule.sol +++ b/contracts/modules/wrapper/controllers/ValidationModule.sol @@ -41,7 +41,7 @@ abstract contract ValidationModule is emit RuleEngine(ruleEngine_); } - /** + /** * @dev ERC1404 returns the human readable explaination corresponding to the error code returned by detectTransferRestriction * @param restrictionCode The error code returned by detectTransferRestriction * @return message The human readable explaination corresponding to the error code returned by detectTransferRestriction diff --git a/contracts/modules/wrapper/core/BaseModule.sol b/contracts/modules/wrapper/core/BaseModule.sol index 0ba3b98f..bef934c3 100644 --- a/contracts/modules/wrapper/core/BaseModule.sol +++ b/contracts/modules/wrapper/core/BaseModule.sol @@ -5,8 +5,7 @@ pragma solidity ^0.8.20; // required OZ imports here import "../../security/AuthorizationModule.sol"; import "../../../libraries/Errors.sol"; -import "../../../interfaces/draft-IERC1643.sol"; -abstract contract BaseModule is IERC1643, AuthorizationModule { +abstract contract BaseModule is AuthorizationModule { /** * @notice * Get the current version of the smart contract @@ -27,9 +26,6 @@ abstract contract BaseModule is IERC1643, AuthorizationModule { string public information; // additional attribute to store information as an uint256 uint256 public flag; - IERC1643 DocumentEngine; - - /* Initializers */ /** @@ -81,19 +77,5 @@ abstract contract BaseModule is IERC1643, AuthorizationModule { emit Information(information_, information_); } - function getDocument(bytes32 _name) public view returns (string memory, bytes32, uint256){ - if(address(DocumentEngine) != address(0)){ - return DocumentEngine.getDocument( _name); - } else{ - return ("",0x0, 0); - } - } - - function getAllDocuments() public view returns (bytes32[] memory documents){ - if(address(DocumentEngine) != address(0)){ - documents = DocumentEngine.getAllDocuments(); - } - } - uint256[50] private __gap; } diff --git a/contracts/modules/wrapper/core/ERC20BaseModule.sol b/contracts/modules/wrapper/core/ERC20BaseModule.sol index ad0ee813..54bea708 100644 --- a/contracts/modules/wrapper/core/ERC20BaseModule.sol +++ b/contracts/modules/wrapper/core/ERC20BaseModule.sol @@ -7,6 +7,7 @@ import "../../../../openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC import "../../../libraries/Errors.sol"; abstract contract ERC20BaseModule is ERC20Upgradeable { + /* Events */ /** * @notice Emitted when the specified `spender` spends the specified `value` tokens owned by the specified `owner` reducing the corresponding allowance. diff --git a/contracts/modules/wrapper/extensions/DebtModule.sol b/contracts/modules/wrapper/extensions/DebtModule.sol new file mode 100644 index 00000000..336130b1 --- /dev/null +++ b/contracts/modules/wrapper/extensions/DebtModule.sol @@ -0,0 +1,57 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import "../../security/AuthorizationModule.sol"; +import "../../../libraries/Errors.sol"; +import "../../../interfaces/engine/IDebtEngine.sol"; + +abstract contract DebtModule is AuthorizationModule, IDebtEngine { + bytes32 public constant DEBT_ROLE = keccak256("DEBT_ROLE"); + IDebtEngine public debtEngine; + /** + * @dev Emitted when a rule engine is set. + */ + event DebtEngine(IDebtEngine indexed newDebtEngine); + /** + * @dev + * + * - The grant to the admin role is done by AccessControlDefaultAdminRules + * - The control of the zero address is done by AccessControlDefaultAdminRules + * + */ + function __DebtModule_init_unchained(IDebtEngine debtEngine_) + internal onlyInitializing { + if (address(debtEngine_) != address (0)) { + debtEngine = debtEngine_; + emit DebtEngine(debtEngine_); + } + + + } + + /* + * @notice set an authorizationEngine if not already set + * + */ + function setDebtEngine( + IDebtEngine debtEngine_ + ) external onlyRole(DEBT_ROLE) { + debtEngine = debtEngine_; + emit DebtEngine(debtEngine_); + } + + function debt() external returns(DebtBase memory debtBaseResult){ + if(address(debtEngine) != address(0)){ + debtBaseResult = debtEngine.debt(); + } + } + + function creditEvents() external returns(CreditEvents memory creditEventsResult){ + if(address(debtEngine) != address(0)){ + creditEventsResult = debtEngine.creditEvents(); + } + } + + uint256[50] private __gap; +} diff --git a/contracts/modules/wrapper/extensions/DocumentModule.sol b/contracts/modules/wrapper/extensions/DocumentModule.sol new file mode 100644 index 00000000..9c775b2c --- /dev/null +++ b/contracts/modules/wrapper/extensions/DocumentModule.sol @@ -0,0 +1,58 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import "../../security/AuthorizationModule.sol"; +import "../../../libraries/Errors.sol"; +import "../../../interfaces/draft-IERC1643.sol"; +abstract contract DocumentModule is AuthorizationModule, IERC1643 { + bytes32 public constant DOCUMENT_ROLE = keccak256("DOCUMENT_ROLE"); + IERC1643 public documentEngine; + /** + * @dev Emitted when a rule engine is set. + */ + event DocumentEngine(IERC1643 indexed newDocumentEngine); + /** + * @dev + * + * - The grant to the admin role is done by AccessControlDefaultAdminRules + * - The control of the zero address is done by AccessControlDefaultAdminRules + * + */ + function __DocumentModule_init_unchained(IERC1643 documentEngine_) + internal onlyInitializing { + if (address(documentEngine_) != address (0)) { + documentEngine = documentEngine_; + emit DocumentEngine(documentEngine_); + } + } + + /* + * @notice set an authorizationEngine if not already set + * + */ + function setDocumentEngine( + IERC1643 documentEngine_ + ) external onlyRole(DOCUMENT_ROLE) { + documentEngine = documentEngine_; + emit DocumentEngine(documentEngine_); + } + + + function getDocument(bytes32 _name) public view returns (string memory, bytes32, uint256){ + if(address(documentEngine) != address(0)){ + return documentEngine.getDocument( _name); + } else{ + return ("",0x0, 0); + } + } + + function getAllDocuments() public view returns (bytes32[] memory documents){ + if(address(documentEngine) != address(0)){ + documents = documentEngine.getAllDocuments(); + } + } + + + uint256[50] private __gap; +} diff --git a/test/proxy/general/UpgradeProxy.test.js b/test/proxy/general/UpgradeProxy.test.js index 699a82f7..dc6e9338 100644 --- a/test/proxy/general/UpgradeProxy.test.js +++ b/test/proxy/general/UpgradeProxy.test.js @@ -17,7 +17,6 @@ describe( Functions used: balanceOf, totalSupply, mint */ it('testKeepStorageForTokens', async function () { - /// // ADAPT TRUFFLE TEST TO HARDHAT /* Factory & Artefact */ const ETHERS_CMTAT_PROXY_FACTORY = await ethers.getContractFactory( 'CMTAT_PROXY'