diff --git a/.gitignore b/.gitignore index f2e28c6..9488106 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ .env +broadcast/ lib/ out/ -cache/ \ No newline at end of file +cache/ +.~lock.test.odt# +nethereum-gen.settings \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index f8ad408..1a03dbc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/CMTAT"] path = lib/CMTAT url = https://github.com/CMTA/CMTAT +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts diff --git a/README.md b/README.md index 38ec59a..84ebe69 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,20 @@ You can configure the verbosity with these flags : ## Deployment The official documentation is available here : [website](https://book.getfoundry.sh/reference/forge/deploy-commands) +### Script +To run the script for deployment, you need to create a .env file. The value for CMTAT.ADDRESS is require only to use the script **RuleEngine.s.sol** +Warning : put your private key in a .env file is not the best secure way. + +* File .env +``` +PRIVATE_KEY= +CMTAT_ADDRESS= --broadcast --verify -vvv +``` +Value of YOUR_RPC_URL with a local instance of anvil : http://127.0.0.1:8545 ### Local With anvil, you can create a local testnet node for deploying and testing smart contracts. diff --git a/doc/coverage-2022-10-25.png b/doc/coverage-2022-10-25.png new file mode 100644 index 0000000..eead834 Binary files /dev/null and b/doc/coverage-2022-10-25.png differ diff --git a/doc/lcov.info b/doc/lcov.info new file mode 100644 index 0000000..7ae89f0 --- /dev/null +++ b/doc/lcov.info @@ -0,0 +1,151 @@ +TN: +SF:script/RuleEngine.s.sol +FN:11,MyScript.run +FNDA:0,MyScript.run +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +FNF:1 +FNH:0 +LF:10 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/RuleEngine.sol +FN:17,RuleEngineMock.setRules +FNDA:10,RuleEngineMock.setRules +DA:18,10 +FN:21,RuleEngineMock.ruleLength +FNDA:3,RuleEngineMock.ruleLength +DA:22,3 +FN:25,RuleEngineMock.rule +FNDA:1,RuleEngineMock.rule +DA:26,1 +FN:29,RuleEngineMock.rules +FNDA:1,RuleEngineMock.rules +DA:30,1 +FN:33,RuleEngineMock.detectTransferRestriction +FNDA:7,RuleEngineMock.detectTransferRestriction +DA:39,11 +DA:40,11 +DA:41,11 +BRDA:41,0,0,7 +BRDA:41,0,1,4 +DA:42,7 +DA:45,4 +FN:48,RuleEngineMock.validateTransfer +FNDA:4,RuleEngineMock.validateTransfer +DA:54,4 +FN:57,RuleEngineMock.messageForTransferRestriction +FNDA:6,RuleEngineMock.messageForTransferRestriction +DA:58,6 +DA:59,6 +BRDA:59,1,0,- +BRDA:59,1,1,4 +DA:60,4 +DA:63,2 +FNF:7 +FNH:7 +LF:14 +LH:14 +BRF:4 +BRH:3 +end_of_record +TN: +SF:src/RuleWhiteList.sol +FN:21,RuleWhitelist.addAddressesToTheWhitelist +FNDA:11,RuleWhitelist.addAddressesToTheWhitelist +DA:24,11 +DA:25,23 +BRDA:25,0,0,- +BRDA:25,0,1,20 +DA:26,21 +BRDA:26,1,0,1 +BRDA:26,1,1,20 +DA:27,20 +DA:28,20 +FN:33,RuleWhitelist.removeAddressesFromTheWhitelist +FNDA:3,RuleWhitelist.removeAddressesFromTheWhitelist +DA:36,3 +DA:37,7 +BRDA:37,2,0,- +BRDA:37,2,1,6 +DA:38,6 +DA:39,6 +FN:45,RuleWhitelist.addAddressToTheWhitelist +FNDA:16,RuleWhitelist.addAddressToTheWhitelist +DA:46,16 +BRDA:46,3,0,1 +BRDA:46,3,1,15 +DA:47,15 +BRDA:47,4,0,1 +BRDA:47,4,1,14 +DA:48,14 +BRDA:48,5,0,14 +BRDA:48,5,1,14 +DA:49,14 +DA:50,14 +FN:54,RuleWhitelist.removeAddressFromTheWhitelist +FNDA:2,RuleWhitelist.removeAddressFromTheWhitelist +DA:55,2 +BRDA:55,6,0,1 +BRDA:55,6,1,1 +DA:56,1 +BRDA:56,7,0,1 +BRDA:56,7,1,1 +DA:57,1 +DA:58,1 +FN:62,RuleWhitelist.numberWhitelistedAddress +FNDA:14,RuleWhitelist.numberWhitelistedAddress +DA:63,14 +FN:66,RuleWhitelist.addressIsWhitelisted +FNDA:38,RuleWhitelist.addressIsWhitelisted +DA:67,38 +FN:71,RuleWhitelist.isTransferValid +FNDA:2,RuleWhitelist.isTransferValid +DA:75,2 +FN:78,RuleWhitelist.detectTransferRestriction +FNDA:14,RuleWhitelist.detectTransferRestriction +DA:82,16 +BRDA:82,8,0,6 +BRDA:82,8,1,10 +DA:83,6 +DA:85,10 +BRDA:85,9,0,3 +BRDA:85,9,1,7 +DA:86,3 +DA:89,7 +FN:93,RuleWhitelist.canReturnTransferRestrictionCode +FNDA:6,RuleWhitelist.canReturnTransferRestrictionCode +DA:95,6 +BRDA:94,10,0,4 +BRDA:94,10,1,2 +DA:102,4 +DA:104,2 +FN:107,RuleWhitelist.messageForTransferRestriction +FNDA:7,RuleWhitelist.messageForTransferRestriction +DA:108,7 +BRDA:108,11,0,4 +BRDA:108,11,1,3 +DA:109,4 +DA:112,3 +BRDA:112,12,0,2 +BRDA:112,12,1,1 +DA:113,2 +DA:117,1 +FNF:10 +FNH:10 +LF:34 +LH:34 +BRF:26 +BRH:24 +end_of_record diff --git a/doc/test-report-26.10.2022.png b/doc/test-report-26.10.2022.png new file mode 100644 index 0000000..dd606fd Binary files /dev/null and b/doc/test-report-26.10.2022.png differ diff --git a/doc/test.odt b/doc/test.odt new file mode 100644 index 0000000..03de3a7 Binary files /dev/null and b/doc/test.odt differ diff --git a/documentation/test.pdf b/doc/test.pdf similarity index 100% rename from documentation/test.pdf rename to doc/test.pdf diff --git a/documentation/test.odt b/documentation/test.odt deleted file mode 100644 index d75aed5..0000000 Binary files a/documentation/test.odt and /dev/null differ diff --git a/lcov.info b/lcov.info new file mode 100644 index 0000000..7a85bf3 --- /dev/null +++ b/lcov.info @@ -0,0 +1,151 @@ +TN: +SF:script/RuleEngine.s.sol +FN:11,MyScript.run +FNDA:0,MyScript.run +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +FNF:1 +FNH:0 +LF:10 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/RuleEngine.sol +FN:17,RuleEngineMock.setRules +FNDA:10,RuleEngineMock.setRules +DA:18,10 +FN:21,RuleEngineMock.ruleLength +FNDA:3,RuleEngineMock.ruleLength +DA:22,3 +FN:25,RuleEngineMock.rule +FNDA:1,RuleEngineMock.rule +DA:26,1 +FN:29,RuleEngineMock.rules +FNDA:1,RuleEngineMock.rules +DA:30,1 +FN:33,RuleEngineMock.detectTransferRestriction +FNDA:7,RuleEngineMock.detectTransferRestriction +DA:39,11 +DA:40,11 +DA:41,11 +BRDA:41,0,0,7 +BRDA:41,0,1,4 +DA:42,7 +DA:45,4 +FN:48,RuleEngineMock.validateTransfer +FNDA:4,RuleEngineMock.validateTransfer +DA:54,4 +FN:57,RuleEngineMock.messageForTransferRestriction +FNDA:6,RuleEngineMock.messageForTransferRestriction +DA:58,6 +DA:59,6 +BRDA:59,1,0,- +BRDA:59,1,1,4 +DA:60,4 +DA:63,2 +FNF:7 +FNH:7 +LF:14 +LH:14 +BRF:4 +BRH:3 +end_of_record +TN: +SF:src/RuleWhiteList.sol +FN:21,RuleWhitelist.addAddressesToTheWhitelist +FNDA:11,RuleWhitelist.addAddressesToTheWhitelist +DA:24,11 +DA:25,24 +BRDA:25,0,0,- +BRDA:25,0,1,21 +DA:26,22 +BRDA:26,1,0,1 +BRDA:26,1,1,21 +DA:27,21 +DA:28,21 +FN:33,RuleWhitelist.removeAddressesFromTheWhitelist +FNDA:3,RuleWhitelist.removeAddressesFromTheWhitelist +DA:36,3 +DA:37,7 +BRDA:37,2,0,- +BRDA:37,2,1,6 +DA:38,6 +DA:39,6 +FN:45,RuleWhitelist.addAddressToTheWhitelist +FNDA:16,RuleWhitelist.addAddressToTheWhitelist +DA:46,16 +BRDA:46,3,0,1 +BRDA:46,3,1,15 +DA:47,15 +BRDA:47,4,0,1 +BRDA:47,4,1,14 +DA:48,14 +BRDA:48,5,0,14 +BRDA:48,5,1,14 +DA:49,14 +DA:50,14 +FN:54,RuleWhitelist.removeAddressFromTheWhitelist +FNDA:2,RuleWhitelist.removeAddressFromTheWhitelist +DA:55,2 +BRDA:55,6,0,1 +BRDA:55,6,1,1 +DA:56,1 +BRDA:56,7,0,1 +BRDA:56,7,1,1 +DA:57,1 +DA:58,1 +FN:62,RuleWhitelist.numberWhitelistedAddress +FNDA:15,RuleWhitelist.numberWhitelistedAddress +DA:63,15 +FN:66,RuleWhitelist.addressIsWhitelisted +FNDA:39,RuleWhitelist.addressIsWhitelisted +DA:67,39 +FN:71,RuleWhitelist.isTransferValid +FNDA:2,RuleWhitelist.isTransferValid +DA:75,2 +FN:78,RuleWhitelist.detectTransferRestriction +FNDA:14,RuleWhitelist.detectTransferRestriction +DA:82,16 +BRDA:82,8,0,6 +BRDA:82,8,1,10 +DA:83,6 +DA:85,10 +BRDA:85,9,0,3 +BRDA:85,9,1,7 +DA:86,3 +DA:89,7 +FN:93,RuleWhitelist.canReturnTransferRestrictionCode +FNDA:8,RuleWhitelist.canReturnTransferRestrictionCode +DA:95,8 +BRDA:94,10,0,5 +BRDA:94,10,1,3 +DA:99,5 +DA:101,3 +FN:104,RuleWhitelist.messageForTransferRestriction +FNDA:7,RuleWhitelist.messageForTransferRestriction +DA:105,7 +BRDA:105,11,0,4 +BRDA:105,11,1,3 +DA:106,4 +DA:109,3 +BRDA:109,12,0,2 +BRDA:109,12,1,1 +DA:110,2 +DA:114,1 +FNF:10 +FNH:10 +LF:34 +LH:34 +BRF:26 +BRH:24 +end_of_record diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..36951d5 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 36951d58386b9fee81b237e6c6626c9115ccef3a diff --git a/remappings.txt b/remappings.txt index 9a3dd82..70e7ee9 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,2 +1,2 @@ -CMTAT/=lib/CMTAT/src/ +CMTAT/=lib/CMTAT/contracts/ openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ \ No newline at end of file diff --git a/script/CMTATWithRuleEngine.s.sol b/script/CMTATWithRuleEngine.s.sol new file mode 100644 index 0000000..8589914 --- /dev/null +++ b/script/CMTATWithRuleEngine.s.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: UNLICENSED +// Documentation : +// https://book.getfoundry.sh/tutorials/solidity-scripting +pragma solidity ^0.8.17; + +import "forge-std/Script.sol"; +import "CMTAT/CMTAT.sol"; +import "src/RuleEngine.sol"; + +contract MyScript is Script { + function run() external { + // Get env variable + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address OWNER = vm.addr(deployerPrivateKey); + address trustedForwarder = address(0x0); + vm.startBroadcast(deployerPrivateKey); + + // CMTAT + CMTAT CMTAT_CONTRACT = new CMTAT(); + console.log("CMTAT CMTAT_CONTRACT : ", address(CMTAT_CONTRACT)); + CMTAT_CONTRACT.initialize( + OWNER, + trustedForwarder, + "CMTA Token", + "CMTAT", + "CMTAT_ISIN", + "https://cmta.ch" + ); + // whitelist + RuleWhitelist ruleWhitelist = new RuleWhitelist(); + console.log("whitelist: ", address(ruleWhitelist)); + // ruleEngine + RuleEngine RULE_ENGINE = new RuleEngine(ruleWhitelist); + console.log("RuleEngine : ", address(RULE_ENGINE)); + CMTAT_CONTRACT.setRuleEngine(RULE_ENGINE); + vm.stopBroadcast(); + } +} \ No newline at end of file diff --git a/script/RuleEngine.s.sol b/script/RuleEngine.s.sol index 2aa9b77..d46385c 100644 --- a/script/RuleEngine.s.sol +++ b/script/RuleEngine.s.sol @@ -9,22 +9,20 @@ import "src/RuleEngine.sol"; contract MyScript is Script { function run() external { + // Get env variable uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address CMTAT_Address = vm.envAddress("CMTAT_ADDRESS"); vm.startBroadcast(deployerPrivateKey); - address OWNER = vm.addr(deployerPrivateKey); - address trustedForwarder = address(0x0); - CMTAT CMTAT_CONTRACT = new CMTAT(); - CMTAT_CONTRACT.initialize( - OWNER, - trustedForwarder, - "CMTA Token", - "CMTAT", - "CMTAT_ISIN", - "https://cmta.ch" - ); + //whitelist RuleWhitelist ruleWhitelist = new RuleWhitelist(); - RuleEngineMock ruleEngineMock = new RuleEngineMock(ruleWhitelist); - CMTAT_CONTRACT.setRuleEngine(ruleEngineMock); + console.log("whitelist: ", address(ruleWhitelist)); + // ruleEngine + RuleEngine RULE_ENGINE = new RuleEngine(ruleWhitelist); + console.log("RuleEngine: ", address(RULE_ENGINE)); + // Configure the new ruleEngine for CMTAT + (bool success, ) = address(CMTAT_Address).call( + abi.encodeCall(CMTAT.setRuleEngine, RULE_ENGINE)); + require(success); vm.stopBroadcast(); } } \ No newline at end of file diff --git a/src/AccessControlAbstract.sol b/src/AccessControlAbstract.sol new file mode 100644 index 0000000..c214ade --- /dev/null +++ b/src/AccessControlAbstract.sol @@ -0,0 +1,11 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.17; + +import "../lib/openzeppelin-contracts/contracts/access/AccessControl.sol"; + + +contract AccessControlAbstract is AccessControl{ + bytes32 public constant WHITELIST_ROLE = keccak256("WHITELIST_OPERATOR"); + bytes32 public constant RULE_ENGINE_ROLE = keccak256("RULE_ENGINE_OPERATOR"); +} \ No newline at end of file diff --git a/src/RuleEngine.sol b/src/RuleEngine.sol index 2e92dca..cbcbdbe 100644 --- a/src/RuleEngine.sol +++ b/src/RuleEngine.sol @@ -1,20 +1,22 @@ -//SPDX-License-Identifier: MPL-2.0 +// SPDX-License-Identifier: MPL-2.0 pragma solidity ^0.8.17; import "CMTAT/interfaces/IRule.sol"; import "CMTAT/interfaces/IRuleEngine.sol"; import "./RuleWhiteList.sol"; +import "./AccessControlAbstract.sol"; - -contract RuleEngineMock is IRuleEngine { +contract RuleEngine is IRuleEngine, AccessControlAbstract { IRule[] internal _rules; constructor(RuleWhitelist _ruleWhitelist ) { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(RULE_ENGINE_ROLE, msg.sender); _rules.push( _ruleWhitelist); } - function setRules(IRule[] calldata rules_) external override { + function setRules(IRule[] calldata rules_) onlyRole(RULE_ENGINE_ROLE) external override { _rules = rules_; } diff --git a/src/RuleWhiteList.sol b/src/RuleWhiteList.sol index c924ef5..e1ff00e 100644 --- a/src/RuleWhiteList.sol +++ b/src/RuleWhiteList.sol @@ -4,7 +4,8 @@ pragma solidity ^0.8.17; import "CMTAT/interfaces/IRule.sol"; import "./CodeList.sol"; -contract RuleWhitelist is IRule, CodeList { +import "./AccessControlAbstract.sol"; +contract RuleWhitelist is IRule, CodeList, AccessControlAbstract { // Number of addresses in the whitelist at the moment uint256 private numAddressesWhitelisted; @@ -18,19 +19,26 @@ contract RuleWhitelist is IRule, CodeList { mapping(address => bool) whitelist; - function addListAddressToTheWhitelist(address[] calldata listWhitelistedAddress) - public{ + constructor(){ + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(WHITELIST_ROLE, msg.sender); + } + + function addAddressesToTheWhitelist(address[] calldata listWhitelistedAddress) + public onlyRole(WHITELIST_ROLE) { for(uint256 i = 0; i < listWhitelistedAddress.length; ++i){ if(!whitelist[listWhitelistedAddress[i]]){ + require( listWhitelistedAddress[i] != address(0x0), "Address 0 is not allowed"); whitelist[listWhitelistedAddress[i]] = true; ++numAddressesWhitelisted; } } } - function removeListAddressToTheWhitelist(address[] calldata listWhitelistedAddress) public { + function removeAddressesFromTheWhitelist(address[] calldata listWhitelistedAddress) public onlyRole(WHITELIST_ROLE) { // require(whitelist[_removeWhitelistAddress], "Address is not in the whitelist"); + // we do not check address 0 for remove for(uint256 i = 0; i < listWhitelistedAddress.length; ++i){ if(whitelist[listWhitelistedAddress[i]]){ whitelist[listWhitelistedAddress[i]] = false; @@ -40,8 +48,8 @@ contract RuleWhitelist is IRule, CodeList { } - function addOneAddressToTheWhitelist(address _newWhitelistAddress) public { - require(_newWhitelistAddress != address(0), "Address 0 is not allowed"); + function addAddressToTheWhitelist(address _newWhitelistAddress) public onlyRole(WHITELIST_ROLE){ + require(_newWhitelistAddress != address(0x0), "Address 0 is not allowed"); require(!whitelist[_newWhitelistAddress], "Address is already in the whitelist"); if(!whitelist[_newWhitelistAddress]){ whitelist[_newWhitelistAddress] = true; @@ -49,7 +57,7 @@ contract RuleWhitelist is IRule, CodeList { } } - function removeOneAddressToTheWhitelist(address _removeWhitelistAddress) public { + function removeAddressFromTheWhitelist(address _removeWhitelistAddress) public onlyRole(WHITELIST_ROLE){ require(whitelist[_removeWhitelistAddress], "Address is not in the whitelist"); if(whitelist[_removeWhitelistAddress]){ whitelist[_removeWhitelistAddress] = false; @@ -93,9 +101,6 @@ contract RuleWhitelist is IRule, CodeList { _restrictionCode == CODE_ADDRESS_FROM_NOT_WHITELISTED || _restrictionCode == CODE_ADDRESS_TO_NOT_WHITELISTED - || - _restrictionCode == CODE_ADDRESS_FROM_NOT_WHITELISTED - ){ return true; } diff --git a/test/CMTAT.sol b/test/CMTAT.sol deleted file mode 100644 index 8e523f7..0000000 --- a/test/CMTAT.sol +++ /dev/null @@ -1,369 +0,0 @@ -//SPDX-License-Identifier: MPL-2.0 -pragma solidity ^0.8.17; - -import "forge-std/Test.sol"; -import "CMTAT/CMTAT.sol"; -import "CMTAT/modules/PauseModule.sol"; -import "./HelperContract.sol"; -import "src/RuleEngine.sol"; - -contract CMTATRuleEngineTest is Test, HelperContract, ValidationModule, RuleWhitelist { - RuleWhitelist ruleWhitelist = new RuleWhitelist(); - RuleEngineMock fakeRuleEngine = new RuleEngineMock(ruleWhitelist); - uint256 resUint256; - - // Arrange - function setUp() public { - vm.prank(OWNER); - CMTAT_CONTRACT = new CMTAT(); - CMTAT_CONTRACT.initialize( - OWNER, - ZERO_ADDRESS, - "CMTA Token", - "CMTAT", - "CMTAT_ISIN", - "https://cmta.ch" - ); - } - - function testCanBeChangedByOwner() public { - // Arrange - vm.prank(OWNER); - vm.expectEmit(true, false, false, false); - emit RuleEngineSet(address(fakeRuleEngine)); - // Act - CMTAT_CONTRACT.setRuleEngine(fakeRuleEngine); - } - - function testCannotCallByNonOwner() public { - // Arrange - vm.prank(ADDRESS1); - string memory message = string( - abi.encodePacked( - "AccessControl: account ", - vm.toString(ADDRESS1), - " is missing role ", - DEFAULT_ADMIN_ROLE_HASH - ) - ); - vm.expectRevert(bytes(message)); - // Act - CMTAT_CONTRACT.setRuleEngine(fakeRuleEngine); - } -} - - -contract RuleEngineCommonTest is Test, HelperContract, ValidationModule, RuleWhitelist { - // Defined in CMTAT.sol - uint8 constant TRANSFER_OK = 0; - string constant TEXT_TRANSFER_OK = "No restriction"; - - RuleEngineMock ruleEngineMock; - RuleWhitelist ruleWhitelist = new RuleWhitelist(); - uint256 resUint256; - bool resBool; - - // Arrange - function setUp() public { - // global arrange - vm.prank(OWNER); - CMTAT_CONTRACT = new CMTAT(); - CMTAT_CONTRACT.initialize( - OWNER, - ZERO_ADDRESS, - "CMTA Token", - "CMTAT", - "CMTAT_ISIN", - "https://cmta.ch" - ); - - // specific arrange - vm.prank(OWNER); - ruleEngineMock = new RuleEngineMock(ruleWhitelist); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS1, 31); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS2, 32); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS3, 33); - vm.prank(OWNER); - // We set the Rule Engine - CMTAT_CONTRACT.setRuleEngine(ruleEngineMock); - } - - // reverts if ADDRESS1 and ADDRESS2 are not whitelisted - function testCannotTransferWithoutAddressWhitelisted() public { - // Arrange - vm.prank(ADDRESS1); - vm.expectRevert(bytes("CMTAT: transfer rejected by validation module")); - // Act - CMTAT_CONTRACT.transfer(ADDRESS2, 21); - } - -} - - -contract RuleEngineTestOneAddress is Test, HelperContract, ValidationModule, RuleWhitelist { - // Defined in CMTAT.sol - uint8 constant TRANSFER_OK = 0; - string constant TEXT_TRANSFER_OK = "No restriction"; - - RuleEngineMock ruleEngineMock; - RuleWhitelist ruleWhitelist = new RuleWhitelist(); - uint256 resUint256; - bool resBool; - - // Arrange - function setUp() public { - // global arrange - vm.prank(OWNER); - CMTAT_CONTRACT = new CMTAT(); - CMTAT_CONTRACT.initialize( - OWNER, - ZERO_ADDRESS, - "CMTA Token", - "CMTAT", - "CMTAT_ISIN", - "https://cmta.ch" - ); - - // specific arrange - vm.prank(OWNER); - ruleEngineMock = new RuleEngineMock(ruleWhitelist); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS1, 31); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS2, 32); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS3, 33); - vm.prank(OWNER); - // We set the Rule Engine - CMTAT_CONTRACT.setRuleEngine(ruleEngineMock); - } - - - function testCannotTranferIfToIsNotWhitelisted() public { - // Arrange - // We add the sender to the whitelist - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS1); - // Arrange - Assert - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - assertEq(resBool, true); - // Act - uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( - ADDRESS1, - ADDRESS2, - 11 - ); - // Assert - assertEq(res1, CODE_ADDRESS_TO_NOT_WHITELISTED); - // Act - string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( - CODE_ADDRESS_TO_NOT_WHITELISTED - ); - // Assert - assertEq(message1, TEXT_ADDRESS_TO_NOT_WHITELISTED); - } - - function testCannotTranferIfFromIsNotWhitelisted() public { - // We add the recipient to the whitelist - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS2); - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); - // Assert - assertEq(resBool, true); - uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( - ADDRESS1, - ADDRESS2, - 11 - ); - // Assert - assertEq(res1, CODE_ADDRESS_FROM_NOT_WHITELISTED); - string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( - res1 - ); - // Assert - assertEq(message1, TEXT_ADDRESS_FROM_NOT_WHITELISTED); - } - - function testCannotTranferIf_From_To_NOT_Whitelisted() public { - uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( - ADDRESS1, - ADDRESS2, - 11 - ); - - // Assert - assertEq(res1, CODE_ADDRESS_FROM_NOT_WHITELISTED); - string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( - CODE_ADDRESS_FROM_NOT_WHITELISTED - ); - - // Assert - assertEq(message1, TEXT_ADDRESS_FROM_NOT_WHITELISTED); - } - - // can check if transfer is valid - function testCanCheckTransferIsValid() public { - // Act - // We add the sender and the recipient to the whitelist. - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS1); - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - // Assert - assertEq(resBool, true); - // Act - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS2); - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); - // Assert - assertEq(resBool, true); - // Act - uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( - ADDRESS1, - ADDRESS2, - 11 - ); - // Assert - assertEq(res1, TRANSFER_OK); - // Act - string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( - res1 - ); - // Assert - assertEq(message1, TEXT_TRANSFER_OK); - } - - // allows ADDRESS1 to transfer tokens to ADDRESS2 - function testAllowTransfer() public { - // Act - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS1); - // Assert - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - assertEq(resBool, true); - // Act - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS2); - // Assert - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); - assertEq(resBool, true); - // Act - ruleWhitelist.addOneAddressToTheWhitelist (ADDRESS3); - // Assert - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); - assertEq(resBool, true); - // Arrange - vm.prank(ADDRESS1); - // Act - CMTAT_CONTRACT.transfer(ADDRESS2, 11); - // Assert - resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS1); - assertEq(resUint256, 20); - // Assert - resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS2); - assertEq(resUint256, 43); - // Assert - resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS3); - assertEq(resUint256, 33); - } - -} - -contract RuleEngineArrayAddressTest is Test, HelperContract, ValidationModule, RuleWhitelist { - //Defined in CMTAT.sol - uint8 constant TRANSFER_OK = 0; - string constant TEXT_TRANSFER_OK = "No restriction"; - - RuleEngineMock ruleEngineMock; - RuleWhitelist ruleWhitelist = new RuleWhitelist(); - uint256 resUint256; - bool resBool; - - - // Arrange - function setUp() public { - // global arrange - vm.prank(OWNER); - CMTAT_CONTRACT = new CMTAT(); - CMTAT_CONTRACT.initialize( - OWNER, - ZERO_ADDRESS, - "CMTA Token", - "CMTAT", - "CMTAT_ISIN", - "https://cmta.ch" - ); - // specific arrange - vm.prank(OWNER); - ruleEngineMock = new RuleEngineMock(ruleWhitelist); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS1, 31); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS2, 32); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS3, 33); - vm.prank(OWNER); - - CMTAT_CONTRACT.setRuleEngine(ruleEngineMock); - } - - // can check if transfer is valid - function testCanCheckTransferIsValid() public { - // Arrange - // We add the sender and the recipient to the whitelist. - address[] memory whitelist = new address[](2); - whitelist[0] = ADDRESS1; - whitelist[1] = ADDRESS2; - (bool success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("addListAddressToTheWhitelist(address[])", whitelist) - ); - require(success); - // Act - uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( - ADDRESS1, - ADDRESS2, - 11 - ); - // Assert - assertEq(res1, TRANSFER_OK); - // Act - string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( - res1 - ); - // Assert - assertEq(message1, TEXT_TRANSFER_OK); - } - - - // allows ADDRESS1 to transfer tokens to ADDRESS2 - function testAllowTransfer() public { - // Arrange - address[] memory whitelist = new address[](2); - whitelist[0] = ADDRESS1; - whitelist[1] = ADDRESS2; - (bool success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("addListAddressToTheWhitelist(address[])", whitelist) - ); - require(success); - vm.prank(ADDRESS1); - // Act - CMTAT_CONTRACT.transfer(ADDRESS2, 11); - // Assert - resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS1); - assertEq(resUint256, 20); - // Assert - resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS2); - assertEq(resUint256, 43); - // Assert - resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS3); - assertEq(resUint256, 33); - } - - // reverts if ADDRESS1 transfers more tokens than rule allows - function testCannotTransferRuleAllows() public { - // Arrange - vm.prank(ADDRESS1); - vm.expectRevert(bytes("CMTAT: transfer rejected by validation module")); - // Act - CMTAT_CONTRACT.transfer(ADDRESS2, 21); - } -} - - - diff --git a/test/CMTATIntegration.t.sol b/test/CMTATIntegration.t.sol new file mode 100644 index 0000000..1e756e7 --- /dev/null +++ b/test/CMTATIntegration.t.sol @@ -0,0 +1,173 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "CMTAT/CMTAT.sol"; +import "CMTAT/modules/PauseModule.sol"; +import "./HelperContract.sol"; +import "src/RuleEngine.sol"; + + +contract CMTATIntegration is Test, HelperContract, RuleWhitelist { + // Defined in CMTAT.sol + uint8 constant TRANSFER_OK = 0; + string constant TEXT_TRANSFER_OK = "No restriction"; + + RuleEngine ruleEngineMock; + uint256 resUint256; + bool resBool; + + // Arrange + function setUp() public { + ruleWhitelist = new RuleWhitelist(); + // global arrange + vm.prank(OWNER); + CMTAT_CONTRACT = new CMTAT(); + CMTAT_CONTRACT.initialize( + OWNER, + ZERO_ADDRESS, + "CMTA Token", + "CMTAT", + "CMTAT_ISIN", + "https://cmta.ch" + ); + + // specific arrange + vm.prank(OWNER); + ruleEngineMock = new RuleEngine(ruleWhitelist); + vm.prank(OWNER); + CMTAT_CONTRACT.mint(ADDRESS1, 31); + vm.prank(OWNER); + CMTAT_CONTRACT.mint(ADDRESS2, 32); + vm.prank(OWNER); + CMTAT_CONTRACT.mint(ADDRESS3, 33); + vm.prank(OWNER); + // We set the Rule Engine + CMTAT_CONTRACT.setRuleEngine(ruleEngineMock); + } + + function testCannotTransferWithoutAddressWhitelisted() public { + // Arrange + vm.prank(ADDRESS1); + vm.expectRevert(bytes("CMTAT: transfer rejected by validation module")); + // Act + CMTAT_CONTRACT.transfer(ADDRESS2, 21); + } + + // allows ADDRESS1 to transfer tokens to ADDRESS2 + function testAllowTransfer() public { + // Arrange + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + (bool success, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + require(success); + vm.prank(ADDRESS1); + + // Act + CMTAT_CONTRACT.transfer(ADDRESS2, 11); + + // Assert + resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS1); + assertEq(resUint256, 20); + resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS2); + assertEq(resUint256, 43); + resUint256 = CMTAT_CONTRACT.balanceOf(ADDRESS3); + assertEq(resUint256, 33); + } + + function testCannotTranferIfFromIsNotWhitelisted() public { + // We add the recipient to the whitelist + ruleWhitelist.addAddressToTheWhitelist(ADDRESS2); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + // Assert + assertEq(resBool, true); + uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( + ADDRESS1, + ADDRESS2, + 11 + ); + // Assert + assertEq(res1, CODE_ADDRESS_FROM_NOT_WHITELISTED); + string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( + res1 + ); + // Assert + assertEq(message1, TEXT_ADDRESS_FROM_NOT_WHITELISTED); + } + + function testCannotTranferIfToIsNotWhitelisted() public { + // Arrange + // We add the sender to the whitelist + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + // Act + uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( + ADDRESS1, + ADDRESS2, + 11 + ); + // Assert + assertEq(res1, CODE_ADDRESS_TO_NOT_WHITELISTED); + // Act + string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( + CODE_ADDRESS_TO_NOT_WHITELISTED + ); + // Assert + assertEq(message1, TEXT_ADDRESS_TO_NOT_WHITELISTED); + } + + + function testCannotTranferIf_From_To_NOT_Whitelisted() public { + // Act + uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( + ADDRESS1, + ADDRESS2, + 11 + ); + + // Assert + assertEq(res1, CODE_ADDRESS_FROM_NOT_WHITELISTED); + // Act + string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( + CODE_ADDRESS_FROM_NOT_WHITELISTED + ); + + // Assert + assertEq(message1, TEXT_ADDRESS_FROM_NOT_WHITELISTED); + } + + function testCanCheckTransferIsValid() public { + // Arrange + // We add the sender and the recipient to the whitelist. + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + (bool success, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + require(success); + // Act + uint8 res1 = CMTAT_CONTRACT.detectTransferRestriction( + ADDRESS1, + ADDRESS2, + 11 + ); + // Assert + assertEq(res1, TRANSFER_OK); + // Act + string memory message1 = CMTAT_CONTRACT.messageForTransferRestriction( + res1 + ); + // Assert + assertEq(message1, TEXT_TRANSFER_OK); + } + + + +} + diff --git a/test/HelperContract.sol b/test/HelperContract.sol index e14d1e8..4bd0078 100644 --- a/test/HelperContract.sol +++ b/test/HelperContract.sol @@ -3,18 +3,21 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import "CMTAT/CMTAT.sol"; - +import "src/RuleWhiteList.sol"; abstract contract HelperContract { - CMTAT CMTAT_CONTRACT; address constant ZERO_ADDRESS = address(0); address constant OWNER = address(1); - address constant ADDRESS1 = address(2); - address constant ADDRESS2 = address(3); - address constant ADDRESS3 = address(4); - string constant PAUSER_ROLE_HASH = - "0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a"; //keccak256("PAUSER_ROLE"); - string constant DEFAULT_ADMIN_ROLE_HASH = + address constant WHITELIST_OPERATOR_ADDRESS = address(2); + address constant RULE_ENGINE_OPERATOR_ADDRESS = address(3); + address constant ATTACKER = address(4); + address constant ADDRESS1 = address(5); + address constant ADDRESS2 = address(6); + address constant ADDRESS3 = address(7); + string constant RULE_ENGINE_ROLE_HASH = "0xd5fd42827a296da4efc38d4948bb7299b7d1683149226f9a69c0d6f3b7d621ee"; + string constant WHITELIST_ROLE_HASH = "0xd5fd42827a296da4efc38d4948bb7299b7d1683149226f9a69c0d6f3b7d621ee"; + string constant DEFAULT_ADMIN_ROLE_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000"; - + RuleWhitelist ruleWhitelist; + CMTAT CMTAT_CONTRACT; constructor() {} } \ No newline at end of file diff --git a/test/RuleEngine.t.sol b/test/RuleEngine.t.sol deleted file mode 100644 index e69de29..0000000 diff --git a/test/RuleEngine/RuleEngine.t.sol b/test/RuleEngine/RuleEngine.t.sol new file mode 100644 index 0000000..766b210 --- /dev/null +++ b/test/RuleEngine/RuleEngine.t.sol @@ -0,0 +1,256 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "CMTAT/CMTAT.sol"; +import "../HelperContract.sol"; +import "src/RuleEngine.sol"; + + +contract RuleEngineTest is Test, HelperContract, RuleWhitelist { + RuleEngine ruleEngineMock; + uint8 resUint8; + uint256 resUint256; + bool resBool; + string resString; + uint8 CODE_NONEXISTENT = 255; + + // Arrange + function setUp() public { + ruleWhitelist = new RuleWhitelist(); + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + ruleEngineMock = new RuleEngine(ruleWhitelist); + resUint256 = ruleEngineMock.ruleLength(); + + // Assert + assertEq(resUint256, 1); + } + + function testCanSetRules() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + vm.prank(WHITELIST_OPERATOR_ADDRESS); + RuleWhitelist ruleWhitelist2 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](2); + ruleWhitelistTab[0] = IRule(ruleWhitelist1); + ruleWhitelistTab[1] = IRule(ruleWhitelist2); + + // Act + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Assert + assertEq(success, true); + resUint256 = ruleEngineMock.ruleLength(); + assertEq(resUint256, 2); + } + + function testCanDetectTransferRestrictionOK() public { + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](1); + ruleWhitelistTab[0] = ruleWhitelist1; + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Arrange - Assert + assertEq(success, true); + ruleWhitelist1.addAddressToTheWhitelist(ADDRESS1); + ruleWhitelist1.addAddressToTheWhitelist(ADDRESS2); + + // Act + resUint8 = ruleEngineMock.detectTransferRestriction(ADDRESS1, ADDRESS2, 20); + + // Assert + assertEq(resUint8, 0); + } + + function testCanDetectTransferRestrictionWithFrom() public { + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](1); + ruleWhitelistTab[0] = ruleWhitelist1; + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Arrange - Assert + assertEq(success, true); + + // Act + resUint8 = ruleEngineMock.detectTransferRestriction(ADDRESS1, ADDRESS2, 20); + + // Assert + assertEq(resUint8, CODE_ADDRESS_FROM_NOT_WHITELISTED); + } + + function testCanDetectTransferRestrictionWithTo() public { + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](1); + ruleWhitelistTab[0] = ruleWhitelist1; + ruleWhitelist1.addAddressToTheWhitelist(ADDRESS1); + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Arrange - Assert + assertEq(success, true); + + // Act + resUint8 = ruleEngineMock.detectTransferRestriction(ADDRESS1, ADDRESS2, 20); + + // Assert + assertEq(resUint8, CODE_ADDRESS_TO_NOT_WHITELISTED); + } + + + function testMessageForTransferRestrictionWithValidRC() public{ + // Act + resString = ruleEngineMock.messageForTransferRestriction(CODE_ADDRESS_FROM_NOT_WHITELISTED); + + // Assert + assertEq(resString, TEXT_ADDRESS_FROM_NOT_WHITELISTED); + } + + function testMessageForTransferRestrictionNoRule() public{ + // Act + resString = ruleEngineMock.messageForTransferRestriction(50); + + // Assert + assertEq(resString, "Unknown restriction code"); + } + + + function testMessageForTransferRestrictionWUnknownRestrictionCode() public{ + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](1); + ruleWhitelistTab[0] = ruleWhitelist1; + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + // Arrange - Assert + assertEq(success, true); + + // Act + resString = ruleEngineMock.messageForTransferRestriction(50); + + // Assert + assertEq(resString, "Unknown restriction code"); + } + + function testValidateTransferOK() public{ + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](1); + ruleWhitelistTab[0] = ruleWhitelist1; + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Arrange - Assert + assertEq(success, true); + ruleWhitelist1.addAddressToTheWhitelist(ADDRESS1); + ruleWhitelist1.addAddressToTheWhitelist(ADDRESS2); + + // Act + resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + + // Assert + assertEq(resBool, true); + } + + function testValidateTransferRestricted() public { + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](1); + ruleWhitelistTab[0] = ruleWhitelist1; + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Arrange - Assert + assertEq(success, true); + + // Act + resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + + // Assert + assertFalse(resBool); + } + + function testRuleLength() public { + // Act + resUint256 = ruleEngineMock.ruleLength(); + + // Assert + assertEq(resUint256, 1); + + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + RuleWhitelist ruleWhitelist2 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](2); + ruleWhitelistTab[0] = IRule(ruleWhitelist1); + ruleWhitelistTab[1] = IRule(ruleWhitelist2); + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Arrange - Assert + assertEq(success, true); + + // Act + resUint256 = ruleEngineMock.ruleLength(); + + // Assert + assertEq(resUint256, 2); + } + + function testGetRule() public { + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + RuleWhitelist ruleWhitelist2 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](2); + ruleWhitelistTab[0] = IRule(ruleWhitelist1); + ruleWhitelistTab[1] = IRule(ruleWhitelist2); + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + // Arrange - Assert + assertEq(success, true); + + // Act + IRule rule = ruleEngineMock.rule(0); + + // Assert + assertEq(address(rule), address(ruleWhitelist1)); + } + + function testGetRules() public { + // Arrange + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + RuleWhitelist ruleWhitelist2 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](2); + ruleWhitelistTab[0] = IRule(ruleWhitelist1); + ruleWhitelistTab[1] = IRule(ruleWhitelist2); + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + // Arrange - Assert + assertEq(success, true); + + // Act + IRule[] memory rules = ruleEngineMock.rules(); + + // Assert + assertEq(ruleWhitelistTab.length, rules.length); + for(uint256 i = 0; i < rules.length; ++i){ + assertEq(address(ruleWhitelistTab[i]), address(rules[i])); + } + + } +} \ No newline at end of file diff --git a/test/RuleEngine/RuleEngineAccessControl.t.sol b/test/RuleEngine/RuleEngineAccessControl.t.sol new file mode 100644 index 0000000..0bd4eeb --- /dev/null +++ b/test/RuleEngine/RuleEngineAccessControl.t.sol @@ -0,0 +1,129 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "CMTAT/CMTAT.sol"; +import "../HelperContract.sol"; +import "src/RuleEngine.sol"; + + +contract RuleEngineTest is Test, HelperContract, RuleWhitelist { + RuleEngine ruleEngineMock; + uint8 resUint8; + uint256 resUint256; + bool resBool; + string resString; + uint8 CODE_NONEXISTENT = 255; + + // Arrange + function setUp() public { + ruleWhitelist = new RuleWhitelist(); + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + ruleEngineMock = new RuleEngine(ruleWhitelist); + resUint256 = ruleEngineMock.ruleLength(); + // Assert + assertEq(resUint256, 1); + } + + function testCannnotAttackerSetRules() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + RuleWhitelist ruleWhitelist1 = new RuleWhitelist(); + vm.prank(WHITELIST_OPERATOR_ADDRESS); + RuleWhitelist ruleWhitelist2 = new RuleWhitelist(); + IRule[] memory ruleWhitelistTab = new IRule[](2); + ruleWhitelistTab[0] = IRule(ruleWhitelist1); + ruleWhitelistTab[1] = IRule(ruleWhitelist2); + + // Act + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + (bool success, ) = address(ruleEngineMock).call( + abi.encodeCall(RuleEngine.setRules, ruleWhitelistTab)); + + // Assert + assertEq(success, true); + resUint256 = ruleEngineMock.ruleLength(); + assertEq(resUint256, 2); + } + + function testCanGrantRoleAsAdmin() public { + // Act + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + vm.expectEmit(true, true, false, true); + emit RoleGranted(RULE_ENGINE_ROLE, ADDRESS1, RULE_ENGINE_OPERATOR_ADDRESS); + ruleEngineMock.grantRole(RULE_ENGINE_ROLE, ADDRESS1); + // Assert + bool res1 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertEq(res1, true); + } + + function testRevokeRoleAsAdmin() public { + // Arrange + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + ruleEngineMock.grantRole(RULE_ENGINE_ROLE, ADDRESS1); + // Arrange - Assert + bool res1 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertEq(res1, true); + + // Act + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + vm.expectEmit(true, true, false, true); + emit RoleRevoked(RULE_ENGINE_ROLE, ADDRESS1, RULE_ENGINE_OPERATOR_ADDRESS); + ruleEngineMock.revokeRole(RULE_ENGINE_ROLE, ADDRESS1); + // Assert + bool res2 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertFalse(res2); + } + + function testCannotGrantFromNonAdmin() public { + // Arrange - Assert + bool res1 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertFalse(res1); + + // Act + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ADDRESS2), + " is missing role ", + DEFAULT_ADMIN_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + vm.prank(ADDRESS2); + ruleEngineMock.grantRole(RULE_ENGINE_ROLE, ADDRESS1); + // Assert + bool res2 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertFalse(res2); + } + + function testCannotRevokeFromNonAdmin() public { + // Arrange - Assert + bool res1 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertFalse(res1); + + // Arrange + vm.prank(RULE_ENGINE_OPERATOR_ADDRESS); + ruleEngineMock.grantRole(RULE_ENGINE_ROLE, ADDRESS1); + // Arrange - Assert + bool res2 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertEq(res2, true); + + // Act + vm.prank(ADDRESS2); + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ADDRESS2), + " is missing role ", + DEFAULT_ADMIN_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + ruleEngineMock.revokeRole(RULE_ENGINE_ROLE, ADDRESS1); + + // Assert + bool res3 = ruleEngineMock.hasRole(RULE_ENGINE_ROLE, ADDRESS1); + assertEq(res3, true); + } +} \ No newline at end of file diff --git a/test/RuleWhitelist/RuleWhitelistAccessControl.t.sol b/test/RuleWhitelist/RuleWhitelistAccessControl.t.sol new file mode 100644 index 0000000..ff344ab --- /dev/null +++ b/test/RuleWhitelist/RuleWhitelistAccessControl.t.sol @@ -0,0 +1,231 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../HelperContract.sol"; +import "src/RuleEngine.sol"; + + +contract RuleWhitelistAccessControl is Test, HelperContract, RuleWhitelist { + //Defined in CMTAT.sol + uint8 constant TRANSFER_OK = 0; + string constant TEXT_TRANSFER_OK = "No restriction"; + uint256 resUint256; + uint8 resUint8; + bool resBool; + bool resCallBool; + string resString; + uint8 CODE_NONEXISTENT = 255; + + // Arrange + function setUp() public { + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist = new RuleWhitelist(); + } + + function testCannotAttackerAddAddressToTheWhitelist() public { + // Act + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ATTACKER), + " is missing role ", + WHITELIST_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + vm.prank(ATTACKER); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + + // Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, false); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + } + + function testCannotAttackerAddAddressesToTheWhitelist() public { + // Arrange + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + + // Act + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ATTACKER), + " is missing role ", + WHITELIST_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + vm.prank(ATTACKER); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + + // Assert + assertEq(resCallBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, false); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, false); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); + assertFalse(resBool); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + } + + function testRemoveAddressFromTheWhitelist() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + + // Act + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ATTACKER), + " is missing role ", + WHITELIST_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + vm.prank(ATTACKER); + ruleWhitelist.removeAddressFromTheWhitelist(ADDRESS1); + + // Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 1); + } + + function testRemoveAddressesFromTheWhitelist() public { + // Arrange + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + assertEq(resCallBool, true); + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, true); + + // Act + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ATTACKER), + " is missing role ", + WHITELIST_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + vm.prank(ATTACKER); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("removeAddressesFromTheWhitelist(address[])", whitelist) + ); + // Assert + assertEq(resCallBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, true); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 2); + } + + function testCanGrantRoleAsAdmin() public { + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + vm.expectEmit(true, true, false, true); + emit RoleGranted(WHITELIST_ROLE, ADDRESS1, WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.grantRole(WHITELIST_ROLE, ADDRESS1); + // Assert + bool res1 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertEq(res1, true); + } + + function testRevokeRoleAsAdmin() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.grantRole(WHITELIST_ROLE, ADDRESS1); + // Arrange - Assert + bool res1 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertEq(res1, true); + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + vm.expectEmit(true, true, false, true); + emit RoleRevoked(WHITELIST_ROLE, ADDRESS1, WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.revokeRole(WHITELIST_ROLE, ADDRESS1); + // Assert + bool res2 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertFalse(res2); + } + + function testCannotGrantFromNonAdmin() public { + // Arrange - Assert + bool res1 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertFalse(res1); + + // Act + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ADDRESS2), + " is missing role ", + DEFAULT_ADMIN_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + vm.prank(ADDRESS2); + ruleWhitelist.grantRole(WHITELIST_ROLE, ADDRESS1); + // Assert + bool res2 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertFalse(res2); + } + + function testCannotRevokeFromNonAdmin() public { + // Arrange - Assert + bool res1 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertFalse(res1); + + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.grantRole(WHITELIST_ROLE, ADDRESS1); + // Arrange - Assert + bool res2 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertEq(res2, true); + + // Act + vm.prank(ADDRESS2); + string memory message = string( + abi.encodePacked( + "AccessControl: account ", + vm.toString(ADDRESS2), + " is missing role ", + DEFAULT_ADMIN_ROLE_HASH + ) + ); + vm.expectRevert(bytes(message)); + ruleWhitelist.revokeRole(WHITELIST_ROLE, ADDRESS1); + + // Assert + bool res3 = ruleWhitelist.hasRole(WHITELIST_ROLE, ADDRESS1); + assertEq(res3, true); + } +} \ No newline at end of file diff --git a/test/RuleWhitelist/RuleWhitelistAdd.t.sol b/test/RuleWhitelist/RuleWhitelistAdd.t.sol new file mode 100644 index 0000000..3903e6c --- /dev/null +++ b/test/RuleWhitelist/RuleWhitelistAdd.t.sol @@ -0,0 +1,175 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../HelperContract.sol"; +import "src/RuleEngine.sol"; + + +contract RuleWhitelistAddTest is Test, HelperContract, RuleWhitelist { + //Defined in CMTAT.sol + uint8 constant TRANSFER_OK = 0; + string constant TEXT_TRANSFER_OK = "No restriction"; + uint256 resUint256; + uint8 resUint8; + bool resBool; + bool resCallBool; + string resString; + uint8 CODE_NONEXISTENT = 255; + + // Arrange + function setUp() public { + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist = new RuleWhitelist(); + } + + function testAddAddressToTheWhitelist() public { + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + + // Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 1); + } + + function testAddAddressesToTheWhitelist() public { + // Arrange + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + + // Assert + assertEq(resCallBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); + assertFalse(resBool); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 2); + } + + function testCannotAddAddressZeroToTheWhitelist() public { + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(address(0x0)); + assertEq(resBool, false); + + // Arrange + vm.expectRevert(bytes("Address 0 is not allowed")); + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(address(0x0)); + + // Assert + resBool = ruleWhitelist.addressIsWhitelisted(address(0x0)); + assertEq(resBool, false); + } + + function testCannotAddAddressesZeroToTheWhitelist() public { + // Arrange + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + address[] memory whitelist = new address[](3); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + // our target address + whitelist[2] = address(0x0); + + // Arrange + vm.expectRevert(bytes("Address 0 is not allowed")); + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + + // Assert - Main + // Seem that call returns true even if the function is reverted + // TODO : check the return value of call + // assertFalse(resCallBool); + resBool = ruleWhitelist.addressIsWhitelisted(address(0x0)); + assertEq(resBool, false); + + // Assert - Secondary + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, false); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, false); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + } + + function testAddAddressTwiceToTheWhitelist() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + /// Arrange + vm.expectRevert(bytes("Address is already in the whitelist")); + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + // no change + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 1); + } + + function testAddAddressesTwiceToTheWhitelist() public { + // Arrange + // Arrange - first addition + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + // Arrange - Assert + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 2); + // Arrange - second addition + address[] memory whitelistDuplicate = new address[](3); + // Duplicate address + whitelistDuplicate[0] = ADDRESS1; + whitelistDuplicate[1] = ADDRESS2; + // new address in the whitelist + whitelistDuplicate[2] = ADDRESS3; + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelistDuplicate) + ); + // Assert + // no change + assertEq(resCallBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, true); + // ADDRESS3 is whitelisted + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); + assertEq(resBool, true); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 3); + } +} \ No newline at end of file diff --git a/test/RuleWhitelist/RuleWhitelistRemove.t.sol b/test/RuleWhitelist/RuleWhitelistRemove.t.sol new file mode 100644 index 0000000..c17eeb0 --- /dev/null +++ b/test/RuleWhitelist/RuleWhitelistRemove.t.sol @@ -0,0 +1,132 @@ +//SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../HelperContract.sol"; +import "src/RuleEngine.sol"; + + +contract RuleWhitelistRemoveTest is Test, HelperContract, RuleWhitelist { + //Defined in CMTAT.sol + uint8 constant TRANSFER_OK = 0; + string constant TEXT_TRANSFER_OK = "No restriction"; + + uint256 resUint256; + uint8 resUint8; + bool resBool; + bool resCallBool; + string resString; + uint8 CODE_NONEXISTENT = 255; + + // Arrange + function setUp() public { + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist = new RuleWhitelist(); + } + + function testRemoveAddressFromTheWhitelist() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.removeAddressFromTheWhitelist(ADDRESS1); + + // Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertFalse(resBool); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + } + + function testRemoveAddressesFromTheWhitelist() public { + // Arrange + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + assertEq(resCallBool, true); + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, true); + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("removeAddressesFromTheWhitelist(address[])", whitelist) + ); + // Assert + assertEq(resCallBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertFalse(resBool); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertFalse(resBool); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + } + + function testRemoveAddressNotPresentFromTheWhitelist() public { + // Arrange + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertFalse(resBool); + vm.expectRevert(bytes("Address is not in the whitelist")); + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.removeAddressFromTheWhitelist(ADDRESS1); + + // Assert + // no change + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertFalse(resBool); + } + + function testRemoveAddressesNotPresentFromTheWhitelist() public { + // Arrange + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + assertEq(resCallBool, true); + + // Arrange - Assert + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertEq(resBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertEq(resBool, true); + + // Arrange + address[] memory whitelistRemove = new address[](3); + whitelistRemove[0] = ADDRESS1; + whitelistRemove[1] = ADDRESS2; + // Target Address - Not Prsent in the whitelist + whitelistRemove[2] = ADDRESS3; + + // Act + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("removeAddressesFromTheWhitelist(address[])", whitelistRemove) + ); + // Assert + assertEq(resCallBool, true); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + assertFalse(resBool); + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + assertFalse(resBool); + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + assertEq(resUint256, 0); + } +} \ No newline at end of file diff --git a/test/RulewhiteList.t.sol b/test/RuleWhitelist/Rulewhitelist.t.sol similarity index 54% rename from test/RulewhiteList.t.sol rename to test/RuleWhitelist/Rulewhitelist.t.sol index 345179a..b077c04 100644 --- a/test/RulewhiteList.t.sol +++ b/test/RuleWhitelist/Rulewhitelist.t.sol @@ -2,49 +2,22 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; -import "CMTAT/CMTAT.sol"; -import "CMTAT/modules/PauseModule.sol"; -import "./HelperContract.sol"; +import "../HelperContract.sol"; import "src/RuleEngine.sol"; -contract RuleWhiteListTest is Test, HelperContract, ValidationModule, RuleWhitelist { - //Defined in CMTAT.sol - uint8 constant TRANSFER_OK = 0; - string constant TEXT_TRANSFER_OK = "No restriction"; - - RuleEngineMock ruleEngineMock; - RuleWhitelist ruleWhitelist = new RuleWhitelist(); +contract RuleWhitelistTest is Test, HelperContract, RuleWhitelist { uint256 resUint256; + uint8 resUint8; bool resBool; + bool resCallBool; string resString; uint8 CODE_NONEXISTENT = 255; - + // Arrange function setUp() public { - // global arrange - vm.prank(OWNER); - CMTAT_CONTRACT = new CMTAT(); - CMTAT_CONTRACT.initialize( - OWNER, - ZERO_ADDRESS, - "CMTA Token", - "CMTAT", - "CMTAT_ISIN", - "https://cmta.ch" - ); - // specific arrange - vm.prank(OWNER); - ruleEngineMock = new RuleEngineMock(ruleWhitelist); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS1, 31); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS2, 32); - vm.prank(OWNER); - CMTAT_CONTRACT.mint(ADDRESS3, 33); - vm.prank(OWNER); - - CMTAT_CONTRACT.setRuleEngine(ruleEngineMock); + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist = new RuleWhitelist(); } function testReturnFalseIfAddressNotWhitelisted() public { @@ -66,92 +39,41 @@ contract RuleWhiteListTest is Test, HelperContract, ValidationModule, RuleWhitel assertFalse(resBool); } - function testAddressIsIndicatedAsWhitelisted() public { - // Arrange - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS1); - // Act - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - // Assert - assertEq(resBool, true); - } - - - - function testReturnTrueIfAddressIsWhitelisted() public { - // Arrange - address[] memory whitelist = new address[](2); - whitelist[0] = ADDRESS1; - whitelist[1] = ADDRESS2; - (bool success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("addListAddressToTheWhitelist(address[])", whitelist) - ); - require(success); - // Act + function testAddressIsIndicatedAsWhitelisted() public { + // Arrange - Assert resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - // Assert - assertEq(resBool, true); - // Act - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); - // Assert - assertEq(resBool, true); - // Act - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); - // Assert assertFalse(resBool); - } - function testAddOneAddressToTheWhitelist() public { - // Arrange - ruleWhitelist.addOneAddressToTheWhitelist(ADDRESS1); // Act - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - // Assert - assertEq(resBool, true); - } + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); - - function testAddListAddressToTheWhitelist() public { - // Arrange - address[] memory whitelist = new address[](2); - whitelist[0] = ADDRESS1; - whitelist[1] = ADDRESS2; - (bool success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("addListAddressToTheWhitelist(address[])", whitelist) - ); - require(success); // Assert resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); assertEq(resBool, true); - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); - assertEq(resBool, true); - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); - assertFalse(resBool); } - function testRemoveListAddressToTheWhitelist() public { + function testAddressesIsIndicatedAsWhitelisted() public { // Arrange address[] memory whitelist = new address[](2); whitelist[0] = ADDRESS1; whitelist[1] = ADDRESS2; - (bool success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("addListAddressToTheWhitelist(address[])", whitelist) + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) ); - require(success); - // Arrange - Assert + assertEq(resCallBool, true); + // Act resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); + // Assert assertEq(resBool, true); + // Act resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); + // Assert assertEq(resBool, true); - // Act - (success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("removeListAddressToTheWhitelist(address[])", whitelist) - ); - + resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS3); // Assert - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); - assertFalse(resBool); - resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS2); assertFalse(resBool); } @@ -161,11 +83,11 @@ contract RuleWhiteListTest is Test, HelperContract, ValidationModule, RuleWhitel // Assert assertEq(resBool, true); // Act - resBool = canReturnTransferRestrictionCode(CODE_ADDRESS_TO_NOT_WHITELISTED); + resBool = ruleWhitelist.canReturnTransferRestrictionCode(CODE_ADDRESS_TO_NOT_WHITELISTED); // Assert assertEq(resBool, true); // Act - resBool = canReturnTransferRestrictionCode(CODE_NONEXISTENT); + resBool = ruleWhitelist.canReturnTransferRestrictionCode(CODE_NONEXISTENT); // Assert assertFalse(resBool); } @@ -195,10 +117,11 @@ contract RuleWhiteListTest is Test, HelperContract, ValidationModule, RuleWhitel address[] memory whitelist = new address[](2); whitelist[0] = ADDRESS1; whitelist[1] = ADDRESS2; - (bool success, ) = address(ruleWhitelist).call( - abi.encodeWithSignature("addListAddressToTheWhitelist(address[])", whitelist) + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) ); - require(success); + assertEq(resCallBool, true); // Arrange - Assert resBool = ruleWhitelist.addressIsWhitelisted(ADDRESS1); assertEq(resBool, true); @@ -221,4 +144,64 @@ contract RuleWhiteListTest is Test, HelperContract, ValidationModule, RuleWhitel assertFalse(resBool); } + function testNumberWhitelistedAddress() public{ + // Act + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + // Assert + assertEq(resUint256, 0); + + // Arrange + address[] memory whitelist = new address[](2); + whitelist[0] = ADDRESS1; + whitelist[1] = ADDRESS2; + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("addAddressesToTheWhitelist(address[])", whitelist) + ); + assertEq(resCallBool, true); + // Act + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + // Assert + assertEq(resUint256, 2); + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + (resCallBool, ) = address(ruleWhitelist).call( + abi.encodeWithSignature("removeAddressesFromTheWhitelist(address[])", whitelist) + ); + // Arrange - Assert + assertEq(resCallBool, true); + // Act + resUint256 = ruleWhitelist.numberWhitelistedAddress(); + // Assert + assertEq(resUint256, 0); + } + + function testDetectTransferRestrictionFrom() public { + // Act + resUint8 = ruleWhitelist.detectTransferRestriction(ADDRESS1, ADDRESS2, 20); + // Assert + assertEq(resUint8, CODE_ADDRESS_FROM_NOT_WHITELISTED); + } + + function testDetectTransferRestrictionTo() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + // Act + resUint8 = ruleWhitelist.detectTransferRestriction(ADDRESS1, ADDRESS2, 20); + // Assert + assertEq(resUint8, CODE_ADDRESS_TO_NOT_WHITELISTED); + } + + function testDetectTransferRestrictionOk() public { + // Arrange + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS1); + vm.prank(WHITELIST_OPERATOR_ADDRESS); + ruleWhitelist.addAddressToTheWhitelist(ADDRESS2); + // Act + resUint8 = ruleWhitelist.detectTransferRestriction(ADDRESS1, ADDRESS2, 20); + // Assert + assertEq(resUint8, NO_ERROR); + } } \ No newline at end of file