diff --git a/contracts/deployment/CMTAT_BEACON_FACTORY.sol b/contracts/deployment/CMTAT_BEACON_FACTORY.sol new file mode 100644 index 00000000..db45c509 --- /dev/null +++ b/contracts/deployment/CMTAT_BEACON_FACTORY.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; + +import '@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol'; +import "../CMTAT_PROXY.sol"; +import "../modules/CMTAT_BASE.sol"; +import '@openzeppelin/contracts/access/AccessControl.sol'; +contract CMTAT_BEACON_FACTORY is AccessControl { + // Private + mapping(uint256 => address) private cmtats; + uint256 private cmtatCounterId; + // public + /// @dev Role to deploy CMTAT + bytes32 public constant CMTAT_DEPLOYER_ROLE = keccak256("CMTAT_DEPLOYER_ROLE"); + UpgradeableBeacon public immutable beacon; + address[] public cmtatsList; + event CMTAT(address indexed CMTAT, uint256 id); + + /** + * @param implementation_ contract implementation + * @param factoryAdmin admin + * @param beaconOwner owner + */ + constructor(address implementation_, address factoryAdmin, address beaconOwner) { + beacon = new UpgradeableBeacon(implementation_, beaconOwner); + _grantRole(DEFAULT_ADMIN_ROLE, factoryAdmin); + _grantRole(CMTAT_DEPLOYER_ROLE, factoryAdmin); + } + + /** + * @notice deploy CMTAT with a beacon proxy + * + */ + function deployCMTAT( + // CMTAT 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 onlyRole(CMTAT_DEPLOYER_ROLE) returns(BeaconProxy cmtat) { + cmtat = new BeaconProxy( + address(beacon), + abi.encodeWithSelector( + CMTAT_PROXY(address(0)).initialize.selector, + admin, + authorizationEngineIrrevocable, + nameIrrevocable, + symbolIrrevocable, + decimalsIrrevocable, + tokenId_, + terms_, + ruleEngine_, + information_, + flag_ + ) + ); + cmtats[cmtatCounterId] = address(cmtat); + emit CMTAT(address(cmtat), cmtatCounterId); + cmtatCounterId++; + cmtatsList.push(address(cmtat)); + return cmtat; + } + + /** + * @notice get CMTAT proxy address + * + */ + function getAddress(uint256 cmtatID_) external view returns (address) { + return cmtats[cmtatID_]; + } + + /** + * @notice get the implementation address from the beacon + * @return implementation address + */ + function implementation() public view returns (address) { + return beacon.implementation(); + } +} \ No newline at end of file diff --git a/contracts/deployment/CMTAT_TP_FACTORY.sol b/contracts/deployment/CMTAT_TP_FACTORY.sol new file mode 100644 index 00000000..a1473a00 --- /dev/null +++ b/contracts/deployment/CMTAT_TP_FACTORY.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "../CMTAT_PROXY.sol"; +import '@openzeppelin/contracts/access/AccessControl.sol'; +contract CMTAT_TP_FACTORY is AccessControl { + // Private + mapping(uint256 => address) private cmtats; + uint256 private cmtatID; + event CMTAT(address indexed CMTAT, uint256 id); + // Public + /// @dev Role to deploy CMTAT + bytes32 public constant CMTAT_DEPLOYER_ROLE = keccak256("CMTAT_DEPLOYER_ROLE"); + address public immutable logic; + address[] public cmtatsList; + + + /** + * @param logic_ contract implementation + * @param factoryAdmin admin + */ + constructor(address logic_, address factoryAdmin) { + logic = logic_; + _grantRole(DEFAULT_ADMIN_ROLE, factoryAdmin); + _grantRole(CMTAT_DEPLOYER_ROLE, factoryAdmin); + } + + function deployCMTAT( + address proxyAdminOwner, + // CMTAT 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 onlyRole(CMTAT_DEPLOYER_ROLE) returns(TransparentUpgradeableProxy cmtat) { + cmtat = new TransparentUpgradeableProxy( + logic, + proxyAdminOwner, + abi.encodeWithSelector( + CMTAT_PROXY(address(0)).initialize.selector, + admin, + authorizationEngineIrrevocable, + nameIrrevocable, + symbolIrrevocable, + decimalsIrrevocable, + tokenId_, + terms_, + ruleEngine_, + information_, + flag_ + ) + ); + cmtats[cmtatID] = address(cmtat); + emit CMTAT(address(cmtat), cmtatID); + cmtatID++; + cmtatsList.push(address(cmtat)); + return cmtat; + } + + /** + * @notice get CMTAT proxy address + * + */ + function getAddress(uint256 cmtatID_) external view returns (address) { + return cmtats[cmtatID_]; + } +} \ No newline at end of file diff --git a/contracts/modules/CMTAT_BASE.sol b/contracts/modules/CMTAT_BASE.sol index 64dd0eb9..11936244 100644 --- a/contracts/modules/CMTAT_BASE.sol +++ b/contracts/modules/CMTAT_BASE.sol @@ -176,15 +176,15 @@ abstract contract CMTAT_BASE is } /** - @notice burn and mint atomically - @param from current token holder to burn tokens - @param to receiver to send the new minted tokens - @param amountToBurn number of tokens to burn - @param amountToMint number of tokens to mint - @dev - - The access control is managed by the functions burn (ERC20BurnModule) and mint (ERC20MintModule) - - Input validation is also managed by the functions burn and mint - - You can mint more tokens than burnt + * @notice burn and mint atomically + * @param from current token holder to burn tokens + * @param to receiver to send the new minted tokens + * @param amountToBurn number of tokens to burn + * @param amountToMint number of tokens to mint + * @dev + * - The access control is managed by the functions burn (ERC20BurnModule) and mint (ERC20MintModule) + * - Input validation is also managed by the functions burn and mint + * - You can mint more tokens than burnt */ function burnAndMint(address from, address to, uint256 amountToBurn, uint256 amountToMint, string calldata reason) public { burn(from, amountToBurn, reason); diff --git a/contracts/modules/internal/ValidationModuleInternal.sol b/contracts/modules/internal/ValidationModuleInternal.sol index a8efbbc2..d66fa008 100644 --- a/contracts/modules/internal/ValidationModuleInternal.sol +++ b/contracts/modules/internal/ValidationModuleInternal.sol @@ -32,7 +32,7 @@ abstract contract ValidationModuleInternal is } /** - @dev before making a call to this function, you have to check if a ruleEngine is set. + * @dev before making a call to this function, you have to check if a ruleEngine is set. */ function _validateTransfer( address from, @@ -43,7 +43,7 @@ abstract contract ValidationModuleInternal is } /** - @dev before making a call to this function, you have to check if a ruleEngine is set. + * @dev before making a call to this function, you have to check if a ruleEngine is set. */ function _messageForTransferRestriction( uint8 restrictionCode @@ -52,7 +52,7 @@ abstract contract ValidationModuleInternal is } /** - @dev before making a call to this function, you have to check if a ruleEngine is set. + * @dev before making a call to this function, you have to check if a ruleEngine is set. */ function _detectTransferRestriction( address from, diff --git a/contracts/modules/internal/base/SnapshotModuleBase.sol b/contracts/modules/internal/base/SnapshotModuleBase.sol index 935eba94..920ddb0c 100644 --- a/contracts/modules/internal/base/SnapshotModuleBase.sol +++ b/contracts/modules/internal/base/SnapshotModuleBase.sol @@ -25,15 +25,15 @@ abstract contract SnapshotModuleBase is Initializable { event SnapshotSchedule(uint256 indexed oldTime, uint256 indexed newTime); /** - @notice Emitted when the scheduled snapshot with the specified time was cancelled. + * @notice Emitted when the scheduled snapshot with the specified time was cancelled. */ event SnapshotUnschedule(uint256 indexed time); /** - @dev See {OpenZeppelin - ERC20Snapshot} - Snapshotted values have arrays of ids (time) and the value corresponding to that id. - ids is expected to be sorted in ascending order, and to contain no repeated elements - because we use findUpperBound in the function _valueAt + * @dev See {OpenZeppelin - ERC20Snapshot} + * Snapshotted values have arrays of ids (time) and the value corresponding to that id. + * ids is expected to be sorted in ascending order, and to contain no repeated elements + * because we use findUpperBound in the function _valueAt */ struct Snapshots { uint256[] ids; @@ -41,13 +41,13 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev See {OpenZeppelin - ERC20Snapshot} + * @dev See {OpenZeppelin - ERC20Snapshot} */ mapping(address => Snapshots) internal _accountBalanceSnapshots; Snapshots internal _totalSupplySnapshots; /** - @dev time instead of a counter for OpenZeppelin + * @dev time instead of a counter for OpenZeppelin */ // Initialized to zero uint256 private _currentSnapshotTime; @@ -55,9 +55,9 @@ abstract contract SnapshotModuleBase is Initializable { uint256 private _currentSnapshotIndex; /** - @dev - list of scheduled snapshot (time) - This list is sorted in ascending order + * @dev + * list of scheduled snapshot (time) + * This list is sorted in ascending order */ uint256[] private _scheduledSnapshots; @@ -67,8 +67,8 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev schedule a snapshot at the specified time - You can only add a snapshot after the last previous + * @dev schedule a snapshot at the specified time + * You can only add a snapshot after the last previous */ function _scheduleSnapshot(uint256 time) internal { // Check the time firstly to avoid an useless read of storage @@ -99,7 +99,7 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev schedule a snapshot at the specified time + * @dev schedule a snapshot at the specified time */ function _scheduleSnapshotNotOptimized(uint256 time) internal { if (time <= block.timestamp) { @@ -132,7 +132,7 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev reschedule a scheduled snapshot at the specified newTime + * @dev reschedule a scheduled snapshot at the specified newTime */ function _rescheduleSnapshot(uint256 oldTime, uint256 newTime) internal { // Check the time firstly to avoid an useless read of storage @@ -178,7 +178,7 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev unschedule the last scheduled snapshot + * @dev unschedule the last scheduled snapshot */ function _unscheduleLastSnapshot(uint256 time) internal { // Check the time firstly to avoid an useless read of storage @@ -197,10 +197,10 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev unschedule (remove) a scheduled snapshot in three steps: - - search the snapshot in the list - - If found, move all next snapshots one position to the left - - Reduce the array size by deleting the last snapshot + * @dev unschedule (remove) a scheduled snapshot in three steps: + * - search the snapshot in the list + * - If found, move all next snapshots one position to the left + * - Reduce the array size by deleting the last snapshot */ function _unscheduleSnapshotNotOptimized(uint256 time) internal { if (time <= block.timestamp) { @@ -220,8 +220,8 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev - Get the next scheduled snapshots + * @dev + * Get the next scheduled snapshots */ function getNextSnapshots() public view returns (uint256[] memory) { uint256[] memory nextScheduledSnapshot = new uint256[](0); @@ -257,8 +257,8 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev - Get all snapshots + * @dev + * Get all snapshots */ function getAllSnapshots() public view returns (uint256[] memory) { return _scheduledSnapshots; @@ -266,11 +266,11 @@ abstract contract SnapshotModuleBase is Initializable { /** - @dev See {OpenZeppelin - ERC20Snapshot} - @param time where we want a snapshot - @param snapshots the struct where are stored the snapshots - @return snapshotExist true if a snapshot is found, false otherwise - value 0 if no snapshot, balance value if a snapshot exists + * @dev See {OpenZeppelin - ERC20Snapshot} + * @param time where we want a snapshot + * @param snapshots the struct where are stored the snapshots + * @return snapshotExist true if a snapshot is found, false otherwise + * value 0 if no snapshot, balance value if a snapshot exists */ function _valueAt( uint256 time, @@ -300,10 +300,10 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev - Inside a struct Snapshots: - - Update the array ids to the current Snapshot time if this one is greater than the snapshot times stored in ids. - - Update the value to the corresponding value. + * @dev + * Inside a struct Snapshots: + * - Update the array ids to the current Snapshot time if this one is greater than the snapshot times stored in ids. + * - Update the value to the corresponding value. */ function _updateSnapshot( Snapshots storage snapshots, @@ -317,9 +317,9 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev - Set the currentSnapshotTime by retrieving the most recent snapshot - if a snapshot exists, clear all past scheduled snapshot + * @dev + * Set the currentSnapshotTime by retrieving the most recent snapshot + * if a snapshot exists, clear all past scheduled snapshot */ function _setCurrentSnapshot() internal { ( @@ -333,7 +333,7 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @return the last snapshot time inside a snapshot ids array + * @return the last snapshot time inside a snapshot ids array */ function _lastSnapshot( uint256[] storage ids @@ -346,8 +346,8 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev Find the snapshot index at the specified time - @return (true, index) if the snapshot exists, (false, 0) otherwise + * @dev Find the snapshot index at the specified time + * @return (true, index) if the snapshot exists, (false, 0) otherwise */ function _findScheduledSnapshotIndex( uint256 time @@ -372,8 +372,8 @@ abstract contract SnapshotModuleBase is Initializable { } /** - @dev find the most recent past snapshot - The complexity of this function is O(N) because we go through the whole list + * @dev find the most recent past snapshot + * The complexity of this function is O(N) because we go through the whole list */ function _findScheduledMostRecentPastSnapshot() private diff --git a/contracts/modules/security/AuthorizationModule.sol b/contracts/modules/security/AuthorizationModule.sol index 06d86a88..81738160 100644 --- a/contracts/modules/security/AuthorizationModule.sol +++ b/contracts/modules/security/AuthorizationModule.sol @@ -91,7 +91,7 @@ abstract contract AuthorizationModule is AccessControlUpgradeable { } - /* + /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole( diff --git a/contracts/modules/wrapper/controllers/ValidationModule.sol b/contracts/modules/wrapper/controllers/ValidationModule.sol index 60e9a91e..390bd721 100644 --- a/contracts/modules/wrapper/controllers/ValidationModule.sol +++ b/contracts/modules/wrapper/controllers/ValidationModule.sol @@ -29,8 +29,8 @@ abstract contract ValidationModule is } /* - @notice set a RuleEngine - @param ruleEngine_ the call will be reverted if the new value of ruleEngine is the same as the current one + * @notice set a RuleEngine + * @param ruleEngine_ the call will be reverted if the new value of ruleEngine is the same as the current one */ function setRuleEngine( IRuleEngine ruleEngine_ diff --git a/contracts/modules/wrapper/core/BaseModule.sol b/contracts/modules/wrapper/core/BaseModule.sol index b1f44401..ff3f4921 100644 --- a/contracts/modules/wrapper/core/BaseModule.sol +++ b/contracts/modules/wrapper/core/BaseModule.sol @@ -8,9 +8,9 @@ import "../../security/AuthorizationModule.sol"; import "../../../libraries/Errors.sol"; abstract contract BaseModule is AuthorizationModule { - /* - @notice - Get the current version of the smart contract + /** + * @notice + * Get the current version of the smart contract */ string public constant VERSION = "2.4.0"; /* Events */ @@ -51,8 +51,8 @@ abstract contract BaseModule is AuthorizationModule { } /* Methods */ - /* - @notice the tokenId will be changed even if the new value is the same as the current one + /** + * @notice the tokenId will be changed even if the new value is the same as the current one */ function setTokenId( string calldata tokenId_ @@ -61,8 +61,8 @@ abstract contract BaseModule is AuthorizationModule { emit TokenId(tokenId_, tokenId_); } - /* - @notice The terms will be changed even if the new value is the same as the current one + /** + * @notice The terms will be changed even if the new value is the same as the current one */ function setTerms( string calldata terms_ @@ -71,8 +71,8 @@ abstract contract BaseModule is AuthorizationModule { emit Term(terms_, terms_); } - /* - @notice The information will be changed even if the new value is the same as the current one + /** + * @notice The information will be changed even if the new value is the same as the current one */ function setInformation( string calldata information_ @@ -81,8 +81,8 @@ abstract contract BaseModule is AuthorizationModule { emit Information(information_, information_); } - /* - @notice The call will be reverted if the new value of flag is the same as the current one + /** + * @notice The call will be reverted if the new value of flag is the same as the current one */ function setFlag(uint256 flag_) public onlyRole(DEFAULT_ADMIN_ROLE) { if (flag == flag_) { diff --git a/contracts/modules/wrapper/core/ERC20BurnModule.sol b/contracts/modules/wrapper/core/ERC20BurnModule.sol index 43467e71..0b3729f9 100644 --- a/contracts/modules/wrapper/core/ERC20BurnModule.sol +++ b/contracts/modules/wrapper/core/ERC20BurnModule.sol @@ -8,8 +8,8 @@ import "../../security/AuthorizationModule.sol"; import "../../../interfaces/ICCIPToken.sol"; abstract contract ERC20BurnModule is ERC20Upgradeable, ICCIPBurnFromERC20, AuthorizationModule { /** - * @notice Emitted when the specified `value` amount of tokens owned by `owner`are destroyed with the given `reason` - */ + * @notice Emitted when the specified `value` amount of tokens owned by `owner`are destroyed with the given `reason` + */ event Burn(address indexed owner, uint256 value, string reason); /** * @notice Emitted when the specified `spender` burns the specified `value` tokens owned by the specified `owner` reducing the corresponding allowance. diff --git a/package-lock.json b/package-lock.json index cb2a943f..2c6f28af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "sol2uml": "^2.5.20", "solc": "^0.8.19", "solgraph": "^1.0.2", + "solidity-code-metrics": "^0.0.25", "solidity-coverage": "^0.8.5", "solidity-docgen": "^0.6.0-beta.35", "surya": "^0.4.11", @@ -14254,7 +14255,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", - "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -14584,7 +14584,6 @@ "version": "5.0.7", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", - "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -24740,6 +24739,48 @@ "node": ">=6" } }, + "node_modules/sloc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sloc/-/sloc-0.3.0.tgz", + "integrity": "sha512-fKmMA8q5OyeZpFCxBa3FuFywQcziQXKBw9B8jwDJ/Ra3H/pkZpQJl9g5s3MIjUo2jwUDHUnaxXgVXObXFdiJBw==", + "dev": true, + "dependencies": { + "async": "^3.2.4", + "cli-table": "^0.3.11", + "commander": "^11.0.0", + "readdirp": "^3.3.0" + }, + "bin": { + "sloc": "bin/sloc" + } + }, + "node_modules/sloc/node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/sloc/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/sloc/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/snake-case": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", @@ -25051,6 +25092,71 @@ "array.prototype.findlast": "^1.2.2" } }, + "node_modules/solidity-code-metrics": { + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/solidity-code-metrics/-/solidity-code-metrics-0.0.25.tgz", + "integrity": "sha512-of1wT4B5GL0/IahPttkEQUU3Bgq2PpLfQ1GcGdE8aMOhryWrEa4V5LYYd7cqKwyzWaRYIZ/pLUnaoJtCV2HfHA==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.16.1", + "glob": "^8.0.3", + "sloc": "^0.3.0", + "solidity-doppelganger": "^0.0.11", + "surya": "^0.4.6" + }, + "bin": { + "solidity-code-metrics": "src/cli.js" + } + }, + "node_modules/solidity-code-metrics/node_modules/@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dev": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/solidity-code-metrics/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/solidity-code-metrics/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/solidity-code-metrics/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/solidity-comments-extractor": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", @@ -25231,6 +25337,151 @@ "hardhat": "^2.8.0" } }, + "node_modules/solidity-doppelganger": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/solidity-doppelganger/-/solidity-doppelganger-0.0.11.tgz", + "integrity": "sha512-mLIuW1Gfcr49zAZuE3XiQoqvkIAOKJBsgg//3DUvX8G6PvQErfnEKSajXwrYmW27oawwB/qbdRYuFR0inpsJQQ==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.16.1", + "glob": "^8.0.3", + "yargs": "^17.0.1" + } + }, + "node_modules/solidity-doppelganger/node_modules/@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dev": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/solidity-doppelganger/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/solidity-doppelganger/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/solidity-doppelganger/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/solidity-doppelganger/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/solidity-doppelganger/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/solidity-doppelganger/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-doppelganger/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/solidity-doppelganger/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/solidity-doppelganger/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/solidity-doppelganger/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/solium": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/solium/-/solium-1.2.5.tgz", @@ -47389,6 +47640,41 @@ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, + "sloc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sloc/-/sloc-0.3.0.tgz", + "integrity": "sha512-fKmMA8q5OyeZpFCxBa3FuFywQcziQXKBw9B8jwDJ/Ra3H/pkZpQJl9g5s3MIjUo2jwUDHUnaxXgVXObXFdiJBw==", + "dev": true, + "requires": { + "async": "^3.2.4", + "cli-table": "^0.3.11", + "commander": "^11.0.0", + "readdirp": "^3.3.0" + }, + "dependencies": { + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + } + } + }, "snake-case": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", @@ -47646,6 +47932,61 @@ "array.prototype.findlast": "^1.2.2" } }, + "solidity-code-metrics": { + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/solidity-code-metrics/-/solidity-code-metrics-0.0.25.tgz", + "integrity": "sha512-of1wT4B5GL0/IahPttkEQUU3Bgq2PpLfQ1GcGdE8aMOhryWrEa4V5LYYd7cqKwyzWaRYIZ/pLUnaoJtCV2HfHA==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.16.1", + "glob": "^8.0.3", + "sloc": "^0.3.0", + "solidity-doppelganger": "^0.0.11", + "surya": "^0.4.6" + }, + "dependencies": { + "@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dev": true, + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "solidity-comments-extractor": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", @@ -47792,6 +48133,123 @@ "solidity-ast": "^0.4.38" } }, + "solidity-doppelganger": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/solidity-doppelganger/-/solidity-doppelganger-0.0.11.tgz", + "integrity": "sha512-mLIuW1Gfcr49zAZuE3XiQoqvkIAOKJBsgg//3DUvX8G6PvQErfnEKSajXwrYmW27oawwB/qbdRYuFR0inpsJQQ==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.16.1", + "glob": "^8.0.3", + "yargs": "^17.0.1" + }, + "dependencies": { + "@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dev": true, + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, "solium": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/solium/-/solium-1.2.5.tgz", diff --git a/package.json b/package.json index 2b9bdf58..e90123e4 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "test:hardhat:snapshot": "npx hardhat test test/standard/modules/SnapshotModule.test.js test/proxy/modules/SnapshotModule.test.js", "test:hardhat:enforcement": "npx hardhat test test/standard/modules/EnforcementModule.test.js test/proxy/modules/EnforcementModule.test.js", "test:hardhat:metaTx": "npx hardhat test test/standard/modules/MetaTxModule.test.js test/proxy/modules/MetaTxModule.test.js", - "test:hardhat:proxy": "npx hardhat test test/proxy/general/Proxy.test.js test/proxy/general/UpgradeProxy.test.js", + "test:hardhat:proxy": "npx hardhat test test/proxy/general/Beacon.test.js test/proxy/general/Transparent.test.js test/proxy/general/Proxy.test.js test/proxy/general/UpgradeProxy.test.js", "test:hardhat:deployment": "npx hardhat test test/deployment/deployment.test.js" }, "repository": { @@ -105,6 +105,7 @@ "sol2uml": "^2.5.20", "solc": "^0.8.19", "solgraph": "^1.0.2", + "solidity-code-metrics": "^0.0.25", "solidity-coverage": "^0.8.5", "solidity-docgen": "^0.6.0-beta.35", "surya": "^0.4.11", diff --git a/test/deploymentUtils.js b/test/deploymentUtils.js index 77c84a41..0c4c786a 100644 --- a/test/deploymentUtils.js +++ b/test/deploymentUtils.js @@ -29,6 +29,13 @@ async function deployCMTATStandalone (_, admin, deployerAddress) { return cmtat } +async function deployCMTATProxyImplementation (deployerAddress, forwarderIrrevocable) { + const cmtat = await CMTAT_PROXY.new(forwarderIrrevocable, + { from: deployerAddress } + ) + return cmtat +} + async function deployCMTATStandaloneWithParameter ( deployerAddress, forwarderIrrevocable, @@ -227,5 +234,6 @@ module.exports = { deployCMTATProxyWithParameter, deployCMTATStandaloneWithParameter, DEPLOYMENT_FLAG, - DEPLOYMENT_DECIMAL + DEPLOYMENT_DECIMAL, + deployCMTATProxyImplementation } diff --git a/test/proxy/general/Beacon.test.js b/test/proxy/general/Beacon.test.js new file mode 100644 index 00000000..2b54105a --- /dev/null +++ b/test/proxy/general/Beacon.test.js @@ -0,0 +1,88 @@ +const CMTAT_BEACON_FACTORY = artifacts.require( + 'CMTAT_BEACON_FACTORY') +const { should } = require('chai').should() +const { + expectRevertCustomError +} = require('../../../openzeppelin-contracts-upgradeable/test/helpers/customError.js') +const CMTAT = artifacts.require('CMTAT_PROXY') +const { DEFAULT_ADMIN_ROLE, CMTAT_DEPLOYER_ROLE } = require('../../utils') +const { ZERO_ADDRESS } = require('../../utils') +const { DEPLOYMENT_FLAG, deployCMTATProxyImplementation } = require('../../deploymentUtils') +const { upgrades } = require('hardhat') +const DEPLOYMENT_DECIMAL = 0 +const { BN, expectEvent } = require('@openzeppelin/test-helpers') +contract( + 'Proxy - Security Test', + function ([_, admin, attacker, deployerAddress]) { + beforeEach(async function () { + this.CMTAT_PROXY = await deployCMTATProxyImplementation(_, deployerAddress) + this.FACTORY = await CMTAT_BEACON_FACTORY.new(this.CMTAT_PROXY.address, admin, admin) + }) + + context('Attacker', function () { + // Here the argument to indicate if it is deployed with a proxy, set at false by the attacker + it('testCannotBeDeployedByAttacker', async function () { + // Act + await expectRevertCustomError( + this.FACTORY.deployCMTAT( + admin, + ZERO_ADDRESS, + 'CMTA Token', + 'CMTAT', + DEPLOYMENT_DECIMAL, + 'CMTAT_ISIN', + 'https://cmta.ch', + ZERO_ADDRESS, + 'CMTAT_info', + DEPLOYMENT_FLAG, { from: attacker }), + 'AccessControlUnauthorizedAccount', + [attacker, CMTAT_DEPLOYER_ROLE] + ) + }) + }) + + context('Deploy CMTAT with Factory', function () { + // Here the argument to indicate if it is deployed with a proxy, set at false by the attacker + it('testCanDeployCMTATWithFactory', async function () { + // Act + this.logs = await this.FACTORY.deployCMTAT( + admin, + ZERO_ADDRESS, + 'CMTA Token', + 'CMTAT', + DEPLOYMENT_DECIMAL, + 'CMTAT_ISIN', + 'https://cmta.ch', + ZERO_ADDRESS, + 'CMTAT_info', + DEPLOYMENT_FLAG, { + from: admin + }); + // Check Id increment + (this.logs.logs[1].args[1]).should.be.bignumber.equal(BN(0)) + // Assert + const CMTAT_ADDRESS = this.logs.logs[1].args[0] + const CMTAT_TRUFFLE = await CMTAT.at(CMTAT_ADDRESS) + // Act + Assert + await CMTAT_TRUFFLE.mint(admin, 100, { + from: admin + }) + this.logs = await this.FACTORY.deployCMTAT( + admin, + ZERO_ADDRESS, + 'CMTA Token', + 'CMTAT', + DEPLOYMENT_DECIMAL, + 'CMTAT_ISIN', + 'https://cmta.ch', + ZERO_ADDRESS, + 'CMTAT_info', + DEPLOYMENT_FLAG, { + from: admin + }); + // Check Id increment + (this.logs.logs[1].args[1]).should.be.bignumber.equal(BN(1)) + }) + }) + } +) diff --git a/test/proxy/general/Proxy.test.js b/test/proxy/general/Proxy.test.js index 7cd3d5fd..b2ceb818 100644 --- a/test/proxy/general/Proxy.test.js +++ b/test/proxy/general/Proxy.test.js @@ -28,6 +28,7 @@ contract( context('Attacker', function () { // Here the argument to indicate if it is deployed with a proxy, set at false by the attacker it('testCannotBeTakenControlByAttacker', async function () { + // Act await expectRevertCustomError( this.implementationContract.initialize( attacker, @@ -45,6 +46,7 @@ contract( 'InvalidInitialization', [] ) + // act + assert await expectRevertCustomError( this.implementationContract.pause({ from: attacker }), 'AccessControlUnauthorizedAccount', diff --git a/test/proxy/general/Transparent.test.js b/test/proxy/general/Transparent.test.js new file mode 100644 index 00000000..7cd6b4b9 --- /dev/null +++ b/test/proxy/general/Transparent.test.js @@ -0,0 +1,92 @@ +const CMTAT_TP_FACTORY = artifacts.require( + 'CMTAT_TP_FACTORY') +const { should } = require('chai').should() +const { + expectRevertCustomError +} = require('../../../openzeppelin-contracts-upgradeable/test/helpers/customError.js') +const CMTAT = artifacts.require('CMTAT_PROXY') +const { DEFAULT_ADMIN_ROLE, PAUSER_ROLE, CMTAT_DEPLOYER_ROLE } = require('../../utils.js') +const { ZERO_ADDRESS } = require('../../utils.js') +const DECIMAL = 0 +const { deployCMTATProxy, DEPLOYMENT_FLAG, deployCMTATProxyImplementation, } = require('../../deploymentUtils.js') +const { upgrades } = require('hardhat') +const DEPLOYMENT_DECIMAL = 0 +const { BN, expectEvent } = require('@openzeppelin/test-helpers') +contract( + 'Proxy - Security Test', + function ([_, admin, attacker, deployerAddress]) { + beforeEach(async function () { + this.CMTAT_PROXY = await deployCMTATProxyImplementation(_, deployerAddress) + this.FACTORY = await CMTAT_TP_FACTORY.new(this.CMTAT_PROXY.address, admin) + }) + + context('Attacker', function () { + // Here the argument to indicate if it is deployed with a proxy, set at false by the attacker + it('testCannotBeDeployedByAttacker', async function () { + // Act + await expectRevertCustomError( + this.FACTORY.deployCMTAT( + admin, + admin, + ZERO_ADDRESS, + 'CMTA Token', + 'CMTAT', + DEPLOYMENT_DECIMAL, + 'CMTAT_ISIN', + 'https://cmta.ch', + ZERO_ADDRESS, + 'CMTAT_info', + DEPLOYMENT_FLAG, { from: attacker }), + 'AccessControlUnauthorizedAccount', + [attacker, CMTAT_DEPLOYER_ROLE] + ) + }) + }) + + context('Deploy CMTAT with Factory', function () { + // Here the argument to indicate if it is deployed with a proxy, set at false by the attacker + it('testCanDeployCMTATWithFactory', async function () { + // Act + this.logs = await this.FACTORY.deployCMTAT( + admin, + admin, + ZERO_ADDRESS, + 'CMTA Token', + 'CMTAT', + DEPLOYMENT_DECIMAL, + 'CMTAT_ISIN', + 'https://cmta.ch', + ZERO_ADDRESS, + 'CMTAT_info', + DEPLOYMENT_FLAG, { + from: admin + }); + // Assert + // Check Id + (this.logs.logs[1].args[1]).should.be.bignumber.equal(BN(0)) + const CMTAT_ADDRESS = this.logs.logs[1].args[0] + const CMTAT_TRUFFLE = await CMTAT.at(CMTAT_ADDRESS) + await CMTAT_TRUFFLE.mint(admin, 100, { + from: admin + }) + // Second deployment + this.logs = await this.FACTORY.deployCMTAT( + admin, + admin, + ZERO_ADDRESS, + 'CMTA Token', + 'CMTAT', + DEPLOYMENT_DECIMAL, + 'CMTAT_ISIN', + 'https://cmta.ch', + ZERO_ADDRESS, + 'CMTAT_info', + DEPLOYMENT_FLAG, { + from: admin + }); + // Check Id increment + (this.logs.logs[1].args[1]).should.be.bignumber.equal(BN(1)) + }) + }) + } +) diff --git a/test/utils.js b/test/utils.js index ed8baa25..3c07dd6b 100644 --- a/test/utils.js +++ b/test/utils.js @@ -17,6 +17,7 @@ module.exports = { '0xaa2de0737115053bf7d3d68e733306557628aef4b4aefa746cbf344fc7267247', // keccak256("DEBT_CREDIT_EVENT_ROLE"); DEFAULT_ADMIN_ROLE: '0x0000000000000000000000000000000000000000000000000000000000000000', + CMTAT_DEPLOYER_ROLE: '0x13293a342e85bb7a675992804d0c6194d27d85f90a7401d0666e206fe3b06a03', ZERO_ADDRESS: '0x0000000000000000000000000000000000000000', RULE_MOCK_AMOUNT_MAX: '20', CMTAT_TRANSFER_REJECT: 'CMTAT: transfer rejected by validation module',