From ad6f1eff849c555612cb222b4ff5ef247df7a99f Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Fri, 4 Jul 2025 17:58:47 +0200 Subject: [PATCH 1/2] Update to cmtat v3.0.0 --- lib/CMTAT | 2 +- script/CMTATWithRuleEngineScript.s.sol | 4 +- script/RuleEngineScript.s.sol | 10 +- src/RuleEngine.sol | 116 +++++++++++++----- src/interfaces/IRuleEngineValidation.sol | 10 +- src/interfaces/IRuleValidation.sol | 14 ++- src/modules/RuleEngineInvariantStorage.sol | 1 + src/modules/RuleEngineValidation.sol | 48 ++++++-- .../operation/RuleConditionalTransfer.sol | 17 ++- src/rules/validation/RuleBlacklist.sol | 22 +++- src/rules/validation/RuleSanctionList.sol | 22 +++- src/rules/validation/RuleWhitelist.sol | 13 ++ src/rules/validation/RuleWhitelistWrapper.sol | 45 +++++++ .../RuleBlacklistInvariantStorage.sol | 7 +- .../RuleWhitelistInvariantStorage.sol | 3 + .../RuleSanctionListInvariantStorage.sol | 3 + .../abstract/RuleValidateTransfer.sol | 18 ++- .../abstract/RuleWhitelistCommon.sol | 5 +- test/HelperContract.sol | 35 +++--- test/RuleBlacklist/CMTATIntegration.t.sol | 12 +- test/RuleBlacklist/RuleBlacklist.t.sol | 1 - .../CMTATIntegration.t.sol | 2 +- .../CMTATIntegrationConditionalTransfer.t.sol | 8 +- .../CMTATIntegrationTest2.t.sol | 2 +- .../RuleConditionalTransfer.t.sol | 2 +- .../RuleConditionalTransferReset.t.sol | 2 +- .../RuleConditionalTransferRestriction.t.sol | 2 +- .../utils/CMTATIntegrationShare.sol | 44 +++---- .../utils/RuleCTDeployment.sol | 2 +- .../AccessControl/RuleEngineAccessControl.sol | 2 +- .../RuleEngineOperation.t.sol | 10 +- .../RuleEngineRestriction.t.sol | 2 +- .../RuleEngineRestriction.t.sol | 14 +-- .../RuleEngineValidation.t.sol | 6 +- .../RuleSanctionListAddTest.t.sol | 2 +- .../RuleSanctionListDeploymentTest.t.sol | 2 +- .../RuleSanctionListTest.t.sol | 12 +- test/RuleWhitelist/CMTATIntegration.t.sol | 8 +- .../CMTATIntegrationWhitelistWrapper.t.sol | 8 +- test/RuleWhitelist/RuleWhitelist.t.sol | 8 +- test/utils/CMTATDeployment.sol | 30 +++-- 41 files changed, 412 insertions(+), 164 deletions(-) diff --git a/lib/CMTAT b/lib/CMTAT index cab48f1..04dad82 160000 --- a/lib/CMTAT +++ b/lib/CMTAT @@ -1 +1 @@ -Subproject commit cab48f1e11de4ed19f8c6fcdadae2485c58630e7 +Subproject commit 04dad821f2adc29e323d89820046a39cf76fad1b diff --git a/script/CMTATWithRuleEngineScript.s.sol b/script/CMTATWithRuleEngineScript.s.sol index e0e611e..65aec2c 100644 --- a/script/CMTATWithRuleEngineScript.s.sol +++ b/script/CMTATWithRuleEngineScript.s.sol @@ -5,8 +5,8 @@ pragma solidity ^0.8.17; import "forge-std/Script.sol"; import "../test/HelperContract.sol"; -import "src/RuleEngine.sol"; -import "src/rules/validation/RuleWhitelist.sol"; +import {RuleEngine} from "src/RuleEngine.sol"; +import {RuleWhitelist} from "src/rules/validation/RuleWhitelist.sol"; /** @title Deploy a CMTAT, a RuleWhitelist and a RuleEngine diff --git a/script/RuleEngineScript.s.sol b/script/RuleEngineScript.s.sol index 77b4781..a182928 100644 --- a/script/RuleEngineScript.s.sol +++ b/script/RuleEngineScript.s.sol @@ -4,10 +4,10 @@ pragma solidity ^0.8.17; import "forge-std/Script.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; -import "src/RuleEngine.sol"; -import "src/rules/validation/RuleWhitelist.sol"; -import "CMTAT/modules/wrapper/controllers/ValidationModule.sol"; +//import "CMTAT/CMTAT_STANDALONE.sol"; +import {RuleEngine} from "src/RuleEngine.sol"; +import {RuleWhitelist} from "src/rules/validation/RuleWhitelist.sol"; +import {ValidationModuleRuleEngine} from "CMTAT/modules/wrapper/extensions/ValidationModule/ValidationModuleRuleEngine.sol"; /** @title Deploy a RuleWhitelist and a RuleEngine. The CMTAT is considred already deployed @@ -28,7 +28,7 @@ contract RuleEngineScript is Script { RULE_ENGINE.addRuleValidation(ruleWhitelist); // Configure the new ruleEngine for CMTAT (bool success, ) = address(CMTAT_Address).call( - abi.encodeCall(ValidationModule.setRuleEngine, RULE_ENGINE) + abi.encodeCall(ValidationModuleRuleEngine.setRuleEngine, RULE_ENGINE) ); require(success); vm.stopBroadcast(); diff --git a/src/RuleEngine.sol b/src/RuleEngine.sol index 6cc14f8..619d6ee 100644 --- a/src/RuleEngine.sol +++ b/src/RuleEngine.sol @@ -5,8 +5,8 @@ pragma solidity ^0.8.20; import "CMTAT/interfaces/engine/IRuleEngine.sol"; import "./modules/MetaTxModuleStandalone.sol"; import "./modules/RuleEngineOperation.sol"; -import "./modules/RuleEngineValidation.sol"; - +import {RuleEngineValidation} from "./modules/RuleEngineValidation.sol"; +import {IRuleValidation} from "./interfaces/IRuleValidation.sol"; /** * @title Implementation of a ruleEngine as defined by the CMTAT */ @@ -16,6 +16,7 @@ contract RuleEngine is RuleEngineValidation, MetaTxModuleStandalone { + /** * @notice * Get the current version of the smart contract @@ -42,21 +43,21 @@ contract RuleEngine is /** * @notice Go through all the rule to know if a restriction exists on the transfer - * @param _from the origin address - * @param _to the destination address - * @param _amount to transfer + * @param from the origin address + * @param to the destination address + * @param value to transfer * @return The restricion code or REJECTED_CODE_BASE.TRANSFER_OK **/ function detectTransferRestriction( - address _from, - address _to, - uint256 _amount + address from, + address to, + uint256 value ) public view override returns (uint8) { // Validation uint8 code = RuleEngineValidation.detectTransferRestrictionValidation( - _from, - _to, - _amount + from, + to, + value ); if (code != uint8(REJECTED_CODE_BASE.TRANSFER_OK)) { return code; @@ -66,7 +67,36 @@ contract RuleEngine is uint256 rulesLength = _rulesOperation.length; for (uint256 i = 0; i < rulesLength; ++i) { uint8 restriction = IRuleValidation(_rulesOperation[i]) - .detectTransferRestriction(_from, _to, _amount); + .detectTransferRestriction(from, to, value); + if (restriction > 0) { + return restriction; + } + } + + return uint8(REJECTED_CODE_BASE.TRANSFER_OK); + } + + function detectTransferRestrictionFrom( + address spender, + address from, + address to, + uint256 value + ) public view override returns (uint8) { + // Validation + uint8 code = RuleEngineValidation.detectTransferRestrictionValidationFrom(spender, + from, + to, + value + ); + if (code != uint8(REJECTED_CODE_BASE.TRANSFER_OK)) { + return code; + } + + // Operation + uint256 rulesLength = _rulesOperation.length; + for (uint256 i = 0; i < rulesLength; ++i) { + uint8 restriction = IRuleValidation(_rulesOperation[i]) + .detectTransferRestrictionFrom(spender,from, to, value); if (restriction > 0) { return restriction; } @@ -77,18 +107,36 @@ contract RuleEngine is /** * @notice Validate a transfer - * @param _from the origin address - * @param _to the destination address - * @param _amount to transfer + * @param from the origin address + * @param to the destination address + * @param value to transfer * @return True if the transfer is valid, false otherwise **/ - function validateTransfer( - address _from, - address _to, - uint256 _amount + function canTransfer( + address from, + address to, + uint256 value ) public view override returns (bool) { return - detectTransferRestriction(_from, _to, _amount) == + detectTransferRestriction(from, to, value) == + uint8(REJECTED_CODE_BASE.TRANSFER_OK); + } + + /** + * @notice Validate a transfer + * @param from the origin address + * @param to the destination address + * @param value to transfer + * @return True if the transfer is valid, false otherwise + **/ + function canTransferFrom( + address /*spender*/, + address from, + address to, + uint256 value + ) public view override returns (bool) { + return + detectTransferRestriction(from, to, value) == uint8(REJECTED_CODE_BASE.TRANSFER_OK); } @@ -130,19 +178,29 @@ contract RuleEngine is /* * @notice function protected by access control */ - function operateOnTransfer( + function transferred( + address spender, address from, address to, uint256 amount - ) external override onlyRole(TOKEN_CONTRACT_ROLE) returns (bool isValid) { - // Validate the transfer - if ( - !RuleEngineValidation.validateTransferValidation(from, to, amount) - ) { - return false; - } + ) external override onlyRole(TOKEN_CONTRACT_ROLE) { + // Validate transfer + require(RuleEngineValidation.canTransferValidation(from, to, amount),RuleEngine_InvalidTransfer(from, to, amount)); + + // Apply operation on RuleEngine + require(RuleEngineOperation._operateOnTransfer(from, to, amount),RuleEngine_InvalidTransfer(from, to, amount)); + } + + function transferred( + address from, + address to, + uint256 amount + ) external override onlyRole(TOKEN_CONTRACT_ROLE) { + // Validate transfer + require(RuleEngineValidation.canTransferValidation(from, to, amount),RuleEngine_InvalidTransfer(from, to, amount)); + // Apply operation on RuleEngine - return RuleEngineOperation._operateOnTransfer(from, to, amount); + require(RuleEngineOperation._operateOnTransfer(from, to, amount),RuleEngine_InvalidTransfer(from, to, amount)); } /* ============ ACCESS CONTROL ============ */ diff --git a/src/interfaces/IRuleEngineValidation.sol b/src/interfaces/IRuleEngineValidation.sol index 13db097..736be39 100644 --- a/src/interfaces/IRuleEngineValidation.sol +++ b/src/interfaces/IRuleEngineValidation.sol @@ -13,10 +13,18 @@ interface IRuleEngineValidation { uint256 _amount ) external view returns (uint8); + function detectTransferRestrictionValidationFrom( + address spender, + address _from, + address _to, + uint256 _amount + ) external view returns (uint8); + /** * @dev Returns true if the transfer is valid, and false otherwise. */ - function validateTransferValidation( + function canTransferValidationFrom( + address spender, address _from, address _to, uint256 _amount diff --git a/src/interfaces/IRuleValidation.sol b/src/interfaces/IRuleValidation.sol index 771ac31..538d514 100644 --- a/src/interfaces/IRuleValidation.sol +++ b/src/interfaces/IRuleValidation.sol @@ -2,13 +2,21 @@ pragma solidity ^0.8.20; -import "CMTAT/interfaces/draft-IERC1404/draft-IERC1404Wrapper.sol"; - -interface IRuleValidation is IERC1404Wrapper { +import {IERC1404Extend} from "CMTAT/interfaces/tokenization/draft-IERC1404.sol"; +import {IERC7551Compliance} from "CMTAT/interfaces//tokenization/draft-IERC7551.sol"; +interface IRuleValidation is IERC1404Extend, IERC7551Compliance { /** * @dev Returns true if the restriction code exists, and false otherwise. */ function canReturnTransferRestrictionCode( uint8 _restrictionCode ) external view returns (bool); + + function detectTransferRestrictionFrom( + address spender, + address _from, + address _to, + uint256 _amount + ) external view override returns (uint8); + } diff --git a/src/modules/RuleEngineInvariantStorage.sol b/src/modules/RuleEngineInvariantStorage.sol index 2421e64..e1e28e2 100644 --- a/src/modules/RuleEngineInvariantStorage.sol +++ b/src/modules/RuleEngineInvariantStorage.sol @@ -8,6 +8,7 @@ abstract contract RuleEngineInvariantStorage { error RuleEngine_RuleDoNotMatch(); error RuleEngine_AdminWithAddressZeroNotAllowed(); error RuleEngine_ArrayIsEmpty(); + error RuleEngine_InvalidTransfer(address from, address to, uint256 value); /// @notice Generate when a rule is added event AddRule(address indexed rule); diff --git a/src/modules/RuleEngineValidation.sol b/src/modules/RuleEngineValidation.sol index d049dd4..05cc697 100644 --- a/src/modules/RuleEngineValidation.sol +++ b/src/modules/RuleEngineValidation.sol @@ -7,7 +7,7 @@ import "./RuleInternal.sol"; import "./RuleEngineValidationCommon.sol"; import "../interfaces/IRuleEngineValidation.sol"; import "../interfaces/IRuleValidation.sol"; -import "CMTAT/interfaces/draft-IERC1404/draft-IERC1404EnumCode.sol"; +import "CMTAT/interfaces/tokenization/draft-IERC1404.sol"; /** * @title Implementation of a ruleEngine defined by the CMTAT @@ -16,8 +16,7 @@ abstract contract RuleEngineValidation is AccessControl, RuleInternal, RuleEngineValidationCommon, - IRuleEngineValidation, - IERC1404EnumCode + IRuleEngineValidation { /** * @notice Go through all the rule to know if a restriction exists on the transfer @@ -40,7 +39,24 @@ abstract contract RuleEngineValidation is } } - return uint8(REJECTED_CODE_BASE.TRANSFER_OK); + return uint8(IERC1404Extend.REJECTED_CODE_BASE.TRANSFER_OK); + } + + function detectTransferRestrictionValidationFrom( + address spender, + address _from, + address _to, + uint256 _amount + ) public view override returns (uint8) { + uint256 rulesLength = _rulesValidation.length; + for (uint256 i = 0; i < rulesLength; ++i) { + uint8 restriction = IRuleValidation(_rulesValidation[i]) + .detectTransferRestrictionFrom(spender, _from, _to, _amount); + if (restriction > 0) { + return restriction; + } + } + return uint8(IERC1404Extend.REJECTED_CODE_BASE.TRANSFER_OK); } /** @@ -50,13 +66,31 @@ abstract contract RuleEngineValidation is * @param _amount to transfer * @return True if the transfer is valid, false otherwise **/ - function validateTransferValidation( + function canTransferValidation( address _from, address _to, uint256 _amount - ) public view override returns (bool) { + ) public view returns (bool) { return detectTransferRestrictionValidation(_from, _to, _amount) == - uint8(REJECTED_CODE_BASE.TRANSFER_OK); + uint8(IERC1404Extend.REJECTED_CODE_BASE.TRANSFER_OK); + } + + /** + * @notice Validate a transfer + * @param _from the origin address + * @param _to the destination address + * @param _amount to transfer + * @return True if the transfer is valid, false otherwise + **/ + function canTransferValidationFrom( + address spender, + address _from, + address _to, + uint256 _amount + ) public view override returns (bool) { + return + detectTransferRestrictionValidationFrom(spender, _from, _to, _amount) == + uint8(IERC1404Extend.REJECTED_CODE_BASE.TRANSFER_OK); } } diff --git a/src/rules/operation/RuleConditionalTransfer.sol b/src/rules/operation/RuleConditionalTransfer.sol index ee5e5c0..34458f6 100644 --- a/src/rules/operation/RuleConditionalTransfer.sol +++ b/src/rules/operation/RuleConditionalTransfer.sol @@ -8,7 +8,7 @@ import "./../../modules/MetaTxModuleStandalone.sol"; import "./abstract/RuleConditionalTransferInvariantStorage.sol"; import "./abstract/RuleConditionalTransferOperator.sol"; import "../validation/abstract/RuleValidateTransfer.sol"; -import "CMTAT/interfaces/engine/IRuleEngine.sol"; +import {IRuleEngine} from "CMTAT/interfaces/engine/IRuleEngine.sol"; /** * @title RuleConditionalTransfer @@ -228,6 +228,21 @@ contract RuleConditionalTransfer is } } + /** + * @notice Check if the transfer is valid + * @param _from the origin address + * @param _to the destination address + * @return The restricion code or REJECTED_CODE_BASE.TRANSFER_OK + **/ + function detectTransferRestrictionFrom( + address /* spender*/, + address _from, + address _to, + uint256 _amount + ) public view override returns (uint8) { + return detectTransferRestriction(_from,_to, _amount ); + } + /** * @notice To know if the restriction code is valid for this rule or not. * @param _restrictionCode The target restriction code diff --git a/src/rules/validation/RuleBlacklist.sol b/src/rules/validation/RuleBlacklist.sol index 2ac9028..693961a 100644 --- a/src/rules/validation/RuleBlacklist.sol +++ b/src/rules/validation/RuleBlacklist.sol @@ -44,6 +44,20 @@ contract RuleBlacklist is } } + function detectTransferRestrictionFrom( + address spender, + address from, + address to, + uint256 amount + ) public view override returns (uint8) { + if(addressIsListed(spender)){ + return CODE_ADDRESS_SPENDER_IS_BLACKLISTED; + } else { + return detectTransferRestriction(from, to, amount); + } + } + + /** * @notice To know if the restriction code is valid for this rule or not. * @param _restrictionCode The target restriction code @@ -54,7 +68,8 @@ contract RuleBlacklist is ) external pure override returns (bool) { return _restrictionCode == CODE_ADDRESS_FROM_IS_BLACKLISTED || - _restrictionCode == CODE_ADDRESS_TO_IS_BLACKLISTED; + _restrictionCode == CODE_ADDRESS_TO_IS_BLACKLISTED + || _restrictionCode == CODE_ADDRESS_SPENDER_IS_BLACKLISTED; } /** @@ -69,7 +84,10 @@ contract RuleBlacklist is return TEXT_ADDRESS_FROM_IS_BLACKLISTED; } else if (_restrictionCode == CODE_ADDRESS_TO_IS_BLACKLISTED) { return TEXT_ADDRESS_TO_IS_BLACKLISTED; - } else { + } else if (_restrictionCode == CODE_ADDRESS_SPENDER_IS_BLACKLISTED) { + return TEXT_ADDRESS_SPENDER_IS_BLACKLISTED; + } + else { return TEXT_CODE_NOT_FOUND; } } diff --git a/src/rules/validation/RuleSanctionList.sol b/src/rules/validation/RuleSanctionList.sol index 4262cc5..4d85966 100644 --- a/src/rules/validation/RuleSanctionList.sol +++ b/src/rules/validation/RuleSanctionList.sol @@ -69,6 +69,23 @@ contract RuleSanctionList is return uint8(REJECTED_CODE_BASE.TRANSFER_OK); } + + function detectTransferRestrictionFrom( + address spender, + address _from, + address _to, + uint256 _amount + ) public view override returns (uint8) { + if(address(sanctionsList) != address(0)){ + if (sanctionsList.isSanctioned(spender)) { + return CODE_ADDRESS_SPENDER_IS_SANCTIONED; + } else { + return detectTransferRestriction(_from,_to,_amount); + } + } + return uint8(REJECTED_CODE_BASE.TRANSFER_OK); + } + /** * @notice To know if the restriction code is valid for this rule or not. * @param _restrictionCode The target restriction code @@ -79,7 +96,8 @@ contract RuleSanctionList is ) external pure override returns (bool) { return _restrictionCode == CODE_ADDRESS_FROM_IS_SANCTIONED || - _restrictionCode == CODE_ADDRESS_TO_IS_SANCTIONED; + _restrictionCode == CODE_ADDRESS_TO_IS_SANCTIONED|| + _restrictionCode == CODE_ADDRESS_SPENDER_IS_SANCTIONED; } /** @@ -94,6 +112,8 @@ contract RuleSanctionList is return TEXT_ADDRESS_FROM_IS_SANCTIONED; } else if (_restrictionCode == CODE_ADDRESS_TO_IS_SANCTIONED) { return TEXT_ADDRESS_TO_IS_SANCTIONED; + } else if (_restrictionCode == CODE_ADDRESS_SPENDER_IS_SANCTIONED) { + return TEXT_ADDRESS_SPENDER_IS_SANCTIONED; } else { return TEXT_CODE_NOT_FOUND; } diff --git a/src/rules/validation/RuleWhitelist.sol b/src/rules/validation/RuleWhitelist.sol index cd4d198..b8ac216 100644 --- a/src/rules/validation/RuleWhitelist.sol +++ b/src/rules/validation/RuleWhitelist.sol @@ -37,4 +37,17 @@ contract RuleWhitelist is RuleAddressList, RuleWhitelistCommon { return uint8(REJECTED_CODE_BASE.TRANSFER_OK); } } + + function detectTransferRestrictionFrom( + address spender, + address _from, + address _to, + uint256 _amount + ) public view override returns (uint8) { + if (addressIsListed(spender)) { + return CODE_ADDRESS_SPENDER_NOT_WHITELISTED; + } else { + return detectTransferRestriction(_from,_to,_amount); + } + } } diff --git a/src/rules/validation/RuleWhitelistWrapper.sol b/src/rules/validation/RuleWhitelistWrapper.sol index 4537cb9..619ecc8 100644 --- a/src/rules/validation/RuleWhitelistWrapper.sol +++ b/src/rules/validation/RuleWhitelistWrapper.sol @@ -73,6 +73,51 @@ contract RuleWhitelistWrapper is } } + function detectTransferRestrictionFrom( + address spender, + address _from, + address _to, + uint256 /*_amount*/ + ) public view override returns (uint8) { + address[] memory targetAddress = new address[](3); + bool[] memory isListed = new bool[](3); + bool[] memory result = new bool[](3); + targetAddress[0] = _from; + targetAddress[1] = _to; + targetAddress[2] = spender; + uint256 rulesLength = _rulesValidation.length; + // For each whitelist rule, we ask if from or to are in the whitelist + for (uint256 i = 0; i < rulesLength; ++i) { + // External call + isListed = RuleAddressList(_rulesValidation[i]) + .addressIsListedBatch(targetAddress); + if (isListed[0] && !result[0]) { + // Update if from is in the list + result[0] = true; + } + if (isListed[1] && !result[1]) { + // Update if to is in the list + result[1] = true; + } + if (isListed[2] && !result[2]) { + // Update if spender is in the list + result[2] = true; + } + if (result[0] && result[1] && result[2]) { + break; + } + } + if (!result[0]) { + return CODE_ADDRESS_FROM_NOT_WHITELISTED; + } else if (!result[1]) { + return CODE_ADDRESS_TO_NOT_WHITELISTED; + } else if (!result[2]) { + return CODE_ADDRESS_SPENDER_NOT_WHITELISTED; + } else { + return uint8(REJECTED_CODE_BASE.TRANSFER_OK); + } + } + /* ============ ACCESS CONTROL ============ */ /** * @dev Returns `true` if `account` has been granted `role`. diff --git a/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleBlacklistInvariantStorage.sol b/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleBlacklistInvariantStorage.sol index 71cb25a..f5d8d04 100644 --- a/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleBlacklistInvariantStorage.sol +++ b/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleBlacklistInvariantStorage.sol @@ -7,12 +7,15 @@ import "../../RuleCommonInvariantStorage.sol"; abstract contract RuleBlacklistInvariantStorage is RuleCommonInvariantStorage { /* ============ String message ============ */ string constant TEXT_ADDRESS_FROM_IS_BLACKLISTED = - "The sender is not in the whitelist"; + "The sender is blacklisted"; string constant TEXT_ADDRESS_TO_IS_BLACKLISTED = - "The recipient is not in the whitelist"; + "The recipient is blacklisted"; + string constant TEXT_ADDRESS_SPENDER_IS_BLACKLISTED = + "The spender is blacklisted"; /* ============ Code ============ */ // It is very important that each rule uses an unique code uint8 public constant CODE_ADDRESS_FROM_IS_BLACKLISTED = 41; uint8 public constant CODE_ADDRESS_TO_IS_BLACKLISTED = 42; + uint8 public constant CODE_ADDRESS_SPENDER_IS_BLACKLISTED = 43; } diff --git a/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleWhitelistInvariantStorage.sol b/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleWhitelistInvariantStorage.sol index f596f87..e2bcdbc 100644 --- a/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleWhitelistInvariantStorage.sol +++ b/src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleWhitelistInvariantStorage.sol @@ -10,9 +10,12 @@ abstract contract RuleWhitelistInvariantStorage is RuleCommonInvariantStorage { "The sender is not in the whitelist"; string constant TEXT_ADDRESS_TO_NOT_WHITELISTED = "The recipient is not in the whitelist"; + string constant TEXT_ADDRESS_SPENDER_NOT_WHITELISTED = + "The spender is not in the whitelist"; /* ============ Code ============ */ // It is very important that each rule uses an unique code uint8 public constant CODE_ADDRESS_FROM_NOT_WHITELISTED = 21; uint8 public constant CODE_ADDRESS_TO_NOT_WHITELISTED = 22; + uint8 public constant CODE_ADDRESS_SPENDER_NOT_WHITELISTED = 23; } diff --git a/src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol b/src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol index e040bff..e8798c4 100644 --- a/src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol +++ b/src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol @@ -21,9 +21,12 @@ abstract contract RuleSanctionlistInvariantStorage is "The sender is sanctioned"; string constant TEXT_ADDRESS_TO_IS_SANCTIONED = "The recipient is sanctioned"; + string constant TEXT_ADDRESS_SPENDER_IS_SANCTIONED = + "The spender is sanctioned"; /* ============ Code ============ */ // It is very important that each rule uses an unique code uint8 public constant CODE_ADDRESS_FROM_IS_SANCTIONED = 31; uint8 public constant CODE_ADDRESS_TO_IS_SANCTIONED = 32; + uint8 public constant CODE_ADDRESS_SPENDER_IS_SANCTIONED = 33; } diff --git a/src/rules/validation/abstract/RuleValidateTransfer.sol b/src/rules/validation/abstract/RuleValidateTransfer.sol index b6844b2..8430be6 100644 --- a/src/rules/validation/abstract/RuleValidateTransfer.sol +++ b/src/rules/validation/abstract/RuleValidateTransfer.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.20; -import "../../../interfaces/IRuleValidation.sol"; - +import {IRuleValidation} from "../../../interfaces/IRuleValidation.sol"; +import {IERC1404} from "CMTAT/interfaces/tokenization/draft-IERC1404.sol"; abstract contract RuleValidateTransfer is IRuleValidation { /** * @notice Validate a transfer @@ -12,14 +12,24 @@ abstract contract RuleValidateTransfer is IRuleValidation { * @param _amount to transfer * @return isValid => true if the transfer is valid, false otherwise **/ - function validateTransfer( + function canTransfer( address _from, address _to, uint256 _amount ) public view override returns (bool isValid) { - // does not work without this keyword "Undeclared identifier" + // does not work without `this` keyword => "Undeclared identifier" return this.detectTransferRestriction(_from, _to, _amount) == uint8(REJECTED_CODE_BASE.TRANSFER_OK); } + + function canTransferFrom( + address spender, + address from, + address to, + uint256 value + ) public view virtual override returns (bool) { + return this.detectTransferRestrictionFrom(spender, from, to, value) == + uint8(REJECTED_CODE_BASE.TRANSFER_OK); + } } diff --git a/src/rules/validation/abstract/RuleWhitelistCommon.sol b/src/rules/validation/abstract/RuleWhitelistCommon.sol index 9ae43b9..dedd371 100644 --- a/src/rules/validation/abstract/RuleWhitelistCommon.sol +++ b/src/rules/validation/abstract/RuleWhitelistCommon.sol @@ -19,7 +19,8 @@ abstract contract RuleWhitelistCommon is ) external pure override returns (bool) { return _restrictionCode == CODE_ADDRESS_FROM_NOT_WHITELISTED || - _restrictionCode == CODE_ADDRESS_TO_NOT_WHITELISTED; + _restrictionCode == CODE_ADDRESS_TO_NOT_WHITELISTED || + _restrictionCode == CODE_ADDRESS_SPENDER_NOT_WHITELISTED; } /** @@ -34,6 +35,8 @@ abstract contract RuleWhitelistCommon is return TEXT_ADDRESS_FROM_NOT_WHITELISTED; } else if (_restrictionCode == CODE_ADDRESS_TO_NOT_WHITELISTED) { return TEXT_ADDRESS_TO_NOT_WHITELISTED; + } else if (_restrictionCode == CODE_ADDRESS_SPENDER_NOT_WHITELISTED) { + return TEXT_ADDRESS_SPENDER_NOT_WHITELISTED; } else { return TEXT_CODE_NOT_FOUND; } diff --git a/test/HelperContract.sol b/test/HelperContract.sol index c9f4578..c5ebd4d 100644 --- a/test/HelperContract.sol +++ b/test/HelperContract.sol @@ -2,29 +2,30 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; +import "CMTAT/libraries/Errors.sol"; -import "src/modules/RuleEngineInvariantStorage.sol"; +import {RuleEngineInvariantStorage} from "src/modules/RuleEngineInvariantStorage.sol"; // RuleEngine -import "src/RuleEngine.sol"; +import {RuleEngine} from "src/RuleEngine.sol"; // RuleConditionalTransfer -import "src/rules/operation/abstract/RuleConditionalTransferInvariantStorage.sol"; -import "src/rules/operation/RuleConditionalTransfer.sol"; +import {RuleConditionalTransferInvariantStorage} from "src/rules/operation/abstract/RuleConditionalTransferInvariantStorage.sol"; +import {RuleConditionalTransfer} from "src/rules/operation/RuleConditionalTransfer.sol"; // RuleSanctionList -import "src/rules/validation/RuleSanctionList.sol"; +import {RuleSanctionList} from "src/rules/validation/RuleSanctionList.sol"; // RUleBlackList -import "src/rules/validation/RuleBlacklist.sol"; +import {RuleBlacklist} from "src/rules/validation/RuleBlacklist.sol"; +import {RuleBlacklistInvariantStorage} from "src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleBlacklistInvariantStorage.sol"; // RuleWhitelist -import "src/rules/validation/RuleWhitelist.sol"; -import "src/rules/validation/RuleWhitelistWrapper.sol"; -import "src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleWhitelistInvariantStorage.sol"; -import "src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleAddressListInvariantStorage.sol"; +import {RuleWhitelist} from "src/rules/validation/RuleWhitelist.sol"; +import {RuleWhitelistWrapper} from "src/rules/validation/RuleWhitelistWrapper.sol"; +import {RuleWhitelistInvariantStorage} from "src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleWhitelistInvariantStorage.sol"; +import {RuleAddressListInvariantStorage} from "src/rules/validation/abstract/RuleAddressList/invariantStorage/RuleAddressListInvariantStorage.sol"; -import "src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol"; -import "src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol"; +import {RuleSanctionlistInvariantStorage}from "src/rules/validation/abstract/RuleSanctionListInvariantStorage.sol"; // Rule interface -import "src/interfaces/IRuleValidation.sol"; -import "src/interfaces/IRuleOperation.sol"; +import {IRuleValidation} from "src/interfaces/IRuleValidation.sol"; +import {IRuleOperation} from "src/interfaces/IRuleOperation.sol"; // utils import "./utils/CMTATDeployment.sol"; @@ -74,7 +75,7 @@ abstract contract HelperContract is // CMTAT CMTATDeployment cmtatDeployment; - CMTAT_STANDALONE CMTAT_CONTRACT; + CMTATStandalone CMTAT_CONTRACT; // RuleEngine Mock RuleEngine public ruleEngineMock; @@ -85,7 +86,7 @@ abstract contract HelperContract is uint8 CODE_NONEXISTENT = 255; // Defined in CMTAT.sol uint8 constant TRANSFER_OK = 0; - string constant TEXT_TRANSFER_OK = "No restriction"; + string constant TEXT_TRANSFER_OK = "NoRestriction"; // Forwarder string ERC2771ForwarderDomain = "ERC2771ForwarderDomain"; diff --git a/test/RuleBlacklist/CMTATIntegration.t.sol b/test/RuleBlacklist/CMTATIntegration.t.sol index 98bf6d5..245b1a3 100644 --- a/test/RuleBlacklist/CMTATIntegration.t.sol +++ b/test/RuleBlacklist/CMTATIntegration.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; @@ -50,7 +50,7 @@ contract CMTATIntegration is Test, HelperContract { // Arrange /*vm.prank(ADDRESS1); vm.expectRevert( - abi.encodeWithSelector(Errors.CMTAT_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21)); */ + abi.encodeWithSelector(RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21)); */ // Act vm.prank(ADDRESS1); CMTAT_CONTRACT.transfer(ADDRESS2, 21); @@ -65,7 +65,7 @@ contract CMTATIntegration is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount @@ -84,7 +84,7 @@ contract CMTATIntegration is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount @@ -113,7 +113,7 @@ contract CMTATIntegration is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount @@ -240,7 +240,7 @@ contract CMTATIntegration is Test, HelperContract { // Act vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ZERO_ADDRESS, ADDRESS1, amount diff --git a/test/RuleBlacklist/RuleBlacklist.t.sol b/test/RuleBlacklist/RuleBlacklist.t.sol index 03305d9..92504d0 100644 --- a/test/RuleBlacklist/RuleBlacklist.t.sol +++ b/test/RuleBlacklist/RuleBlacklist.t.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; diff --git a/test/RuleConditionalTransfer/CMTATIntegration.t.sol b/test/RuleConditionalTransfer/CMTATIntegration.t.sol index 8f04ddf..1ba0080 100644 --- a/test/RuleConditionalTransfer/CMTATIntegration.t.sol +++ b/test/RuleConditionalTransfer/CMTATIntegration.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; import "./utils/CMTATIntegrationShare.sol"; diff --git a/test/RuleConditionalTransfer/CMTATIntegrationConditionalTransfer.t.sol b/test/RuleConditionalTransfer/CMTATIntegrationConditionalTransfer.t.sol index bfd35b5..b11cb0d 100644 --- a/test/RuleConditionalTransfer/CMTATIntegrationConditionalTransfer.t.sol +++ b/test/RuleConditionalTransfer/CMTATIntegrationConditionalTransfer.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; import "./utils/CMTATIntegrationShare.sol"; @@ -120,7 +120,7 @@ contract CMTATIntegrationConditionalTransfer is Test, HelperContract, CMTATInteg // Act vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21 @@ -139,7 +139,7 @@ contract CMTATIntegrationConditionalTransfer is Test, HelperContract, CMTATInteg // Act vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21 @@ -194,7 +194,7 @@ contract CMTATIntegrationConditionalTransfer is Test, HelperContract, CMTATInteg vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21 diff --git a/test/RuleConditionalTransfer/CMTATIntegrationTest2.t.sol b/test/RuleConditionalTransfer/CMTATIntegrationTest2.t.sol index 7e1252e..d05ba53 100644 --- a/test/RuleConditionalTransfer/CMTATIntegrationTest2.t.sol +++ b/test/RuleConditionalTransfer/CMTATIntegrationTest2.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; import "./utils/CMTATIntegrationShare.sol"; diff --git a/test/RuleConditionalTransfer/RuleConditionalTransfer.t.sol b/test/RuleConditionalTransfer/RuleConditionalTransfer.t.sol index 6703fa6..fae3211 100644 --- a/test/RuleConditionalTransfer/RuleConditionalTransfer.t.sol +++ b/test/RuleConditionalTransfer/RuleConditionalTransfer.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; /** * @title General functions of the RuleWhitelist */ diff --git a/test/RuleConditionalTransfer/RuleConditionalTransferReset.t.sol b/test/RuleConditionalTransfer/RuleConditionalTransferReset.t.sol index 139aa81..7b6404f 100644 --- a/test/RuleConditionalTransfer/RuleConditionalTransferReset.t.sol +++ b/test/RuleConditionalTransfer/RuleConditionalTransferReset.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; /** * @title General functions of the RuleWhitelist */ diff --git a/test/RuleConditionalTransfer/RuleConditionalTransferRestriction.t.sol b/test/RuleConditionalTransfer/RuleConditionalTransferRestriction.t.sol index 35eeb58..e08b17c 100644 --- a/test/RuleConditionalTransfer/RuleConditionalTransferRestriction.t.sol +++ b/test/RuleConditionalTransfer/RuleConditionalTransferRestriction.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; //ADmin, forwarder irrect /RuleEngine /** * @title General functions of the RuleEngine diff --git a/test/RuleConditionalTransfer/utils/CMTATIntegrationShare.sol b/test/RuleConditionalTransfer/utils/CMTATIntegrationShare.sol index be4cb79..30c0611 100644 --- a/test/RuleConditionalTransfer/utils/CMTATIntegrationShare.sol +++ b/test/RuleConditionalTransfer/utils/CMTATIntegrationShare.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import {CMTATStandalone} from "CMTAT/deployment/CMTATStandalone.sol"; import "../../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; /** * @title Integration testShare with the CMTAT */ @@ -41,7 +41,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21 @@ -152,7 +152,7 @@ contract CMTATIntegrationShare is Test, HelperContract { // Act vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, defaultValue @@ -184,7 +184,7 @@ contract CMTATIntegrationShare is Test, HelperContract { // Act vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, defaultValue @@ -238,7 +238,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, defaultValue @@ -279,7 +279,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(DEFAULT_ADMIN_ADDRESS); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ZERO_ADDRESS, ADDRESS1, 11 @@ -299,7 +299,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(DEFAULT_ADMIN_ADDRESS); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ZERO_ADDRESS, defaultValue @@ -311,7 +311,7 @@ contract CMTATIntegrationShare is Test, HelperContract { function testShareAutomaticTransferIfOptionsSet() internal { AUTOMATIC_TRANSFER memory automaticTransfertestShare = AUTOMATIC_TRANSFER({ isActivate: true, - cmtat: CMTAT_CONTRACT + cmtat: IERC20(address(CMTAT_CONTRACT)) }); vm.prank(CONDITIONAL_TRANSFER_OPERATOR_ADDRESS); ruleConditionalTransfer.setAutomaticTransfer(automaticTransfertestShare); @@ -344,7 +344,7 @@ contract CMTATIntegrationShare is Test, HelperContract { timeLimitBeforeAutomaticApproval: 90 days }); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -353,7 +353,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(CONDITIONAL_TRANSFER_OPERATOR_ADDRESS); ruleConditionalTransfer.setAutomaticApproval(automaticApproval_); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -362,7 +362,7 @@ contract CMTATIntegrationShare is Test, HelperContract { // Arrange _createTransferRequestShare(); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -371,7 +371,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.warp(block.timestamp + 90 days); // Act - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -389,7 +389,7 @@ contract CMTATIntegrationShare is Test, HelperContract { timeLimitBeforeAutomaticApproval: 90 days }); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -398,7 +398,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(CONDITIONAL_TRANSFER_OPERATOR_ADDRESS); ruleConditionalTransfer.setAutomaticApproval(automaticApproval_); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -407,7 +407,7 @@ contract CMTATIntegrationShare is Test, HelperContract { // Arrange _createTransferRequestShare(); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -416,7 +416,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.warp(block.timestamp + 91 days); // Act - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -435,7 +435,7 @@ contract CMTATIntegrationShare is Test, HelperContract { isActivate: true, timeLimitBeforeAutomaticApproval: 90 days }); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -444,7 +444,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(CONDITIONAL_TRANSFER_OPERATOR_ADDRESS); ruleConditionalTransfer.setAutomaticApproval(automaticApproval_); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -453,14 +453,14 @@ contract CMTATIntegrationShare is Test, HelperContract { // Arrange _createTransferRequestShare(); - resBool = ruleConditionalTransfer.validateTransfer( + resBool = ruleConditionalTransfer.canTransfer( ADDRESS1, ADDRESS2, defaultValue ); assertFalse(resBool); - resBool = CMTAT_CONTRACT.validateTransfer( + resBool = CMTAT_CONTRACT.canTransfer( ADDRESS1, ADDRESS2, defaultValue @@ -472,7 +472,7 @@ contract CMTATIntegrationShare is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, defaultValue diff --git a/test/RuleConditionalTransfer/utils/RuleCTDeployment.sol b/test/RuleConditionalTransfer/utils/RuleCTDeployment.sol index 0ad98b1..946c0d9 100644 --- a/test/RuleConditionalTransfer/utils/RuleCTDeployment.sol +++ b/test/RuleConditionalTransfer/utils/RuleCTDeployment.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; /** * @title Tests on the Access Control */ diff --git a/test/RuleEngine/AccessControl/RuleEngineAccessControl.sol b/test/RuleEngine/AccessControl/RuleEngineAccessControl.sol index 4ceb246..42aca5e 100644 --- a/test/RuleEngine/AccessControl/RuleEngineAccessControl.sol +++ b/test/RuleEngine/AccessControl/RuleEngineAccessControl.sol @@ -129,6 +129,6 @@ contract RuleEngineAccessControlTest is Test, HelperContract { TOKEN_CONTRACT_ROLE ) ); - ruleEngineMock.operateOnTransfer(ADDRESS1, ADDRESS2, 10); + ruleEngineMock.transferred(address(0), ADDRESS1, ADDRESS2, 10); } } diff --git a/test/RuleEngine/ruleEngineOperation/RuleEngineOperation.t.sol b/test/RuleEngine/ruleEngineOperation/RuleEngineOperation.t.sol index efc1f60..afec4da 100644 --- a/test/RuleEngine/ruleEngineOperation/RuleEngineOperation.t.sol +++ b/test/RuleEngine/ruleEngineOperation/RuleEngineOperation.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; /** * @title General functions of the RuleEngine */ @@ -134,7 +134,7 @@ contract RuleEngineOperationTest is Test, HelperContract { function testCannotSetEmptyRulesT1WithEmptyTab() public { // Arrange address[] memory RuleConditionalTransferTab = new address[](0); - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); assertFalse(resBool); // Act @@ -153,7 +153,7 @@ contract RuleEngineOperationTest is Test, HelperContract { // Assert // previous rule still present => invalid Transfer - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); assertFalse(resBool); } @@ -172,7 +172,7 @@ contract RuleEngineOperationTest is Test, HelperContract { ) ); - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); // Assert1 assertFalse(resCallBool); @@ -181,7 +181,7 @@ contract RuleEngineOperationTest is Test, HelperContract { // Assert2 // previous rule still present => invalid Transfer - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); assertFalse(resBool); } diff --git a/test/RuleEngine/ruleEngineOperation/RuleEngineRestriction.t.sol b/test/RuleEngine/ruleEngineOperation/RuleEngineRestriction.t.sol index a5aadee..336a9d6 100644 --- a/test/RuleEngine/ruleEngineOperation/RuleEngineRestriction.t.sol +++ b/test/RuleEngine/ruleEngineOperation/RuleEngineRestriction.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; //ADmin, forwarder irrect /RuleEngine /** * @title General functions of the RuleEngine diff --git a/test/RuleEngine/ruleEngineValidation/RuleEngineRestriction.t.sol b/test/RuleEngine/ruleEngineValidation/RuleEngineRestriction.t.sol index 12a1be7..02a55dd 100644 --- a/test/RuleEngine/ruleEngineValidation/RuleEngineRestriction.t.sol +++ b/test/RuleEngine/ruleEngineValidation/RuleEngineRestriction.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "../../HelperContract.sol"; import "src/RuleEngine.sol"; - +import "OZ/token/ERC20/IERC20.sol"; /** * @title tests concerning the restrictions and validation for the transfers */ @@ -159,7 +159,7 @@ contract RuleEngineRestrictionTest is Test, HelperContract { assertEq(resString, "Unknown restriction code"); } - function testValidateTransferOK() public { + function testcanTransferOK() public { // Arrange vm.prank(WHITELIST_OPERATOR_ADDRESS); ruleWhitelist1.addAddressToTheList(ADDRESS1); @@ -167,7 +167,7 @@ contract RuleEngineRestrictionTest is Test, HelperContract { ruleWhitelist1.addAddressToTheList(ADDRESS2); // Act - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); // Assert assertEq(resBool, true); @@ -175,7 +175,7 @@ contract RuleEngineRestrictionTest is Test, HelperContract { // ruleEngineValidation // Act - resBool = ruleEngineMock.validateTransferValidation( + resBool = ruleEngineMock.canTransferValidation( ADDRESS1, ADDRESS2, 20 @@ -185,9 +185,9 @@ contract RuleEngineRestrictionTest is Test, HelperContract { assertEq(resBool, true); } - function testValidateTransferRestricted() public { + function testcanTransferRestricted() public { // Act - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); // Assert assertFalse(resBool); @@ -195,7 +195,7 @@ contract RuleEngineRestrictionTest is Test, HelperContract { // ruleEngineValidation // Act - resBool = ruleEngineMock.validateTransferValidation( + resBool = ruleEngineMock.canTransferValidation( ADDRESS1, ADDRESS2, 20 diff --git a/test/RuleEngine/ruleEngineValidation/RuleEngineValidation.t.sol b/test/RuleEngine/ruleEngineValidation/RuleEngineValidation.t.sol index f39585e..a84cffc 100644 --- a/test/RuleEngine/ruleEngineValidation/RuleEngineValidation.t.sol +++ b/test/RuleEngine/ruleEngineValidation/RuleEngineValidation.t.sol @@ -132,7 +132,7 @@ contract RuleEngineValidationTest is Test, HelperContract { // Assert2 // false because the ruleWhitelist is still present - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); assertFalse(resBool); } @@ -148,7 +148,7 @@ contract RuleEngineValidationTest is Test, HelperContract { abi.encodeCall(ruleEngineMock.setRulesValidation, ruleWhitelistTab) ); - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); // Assert1 assertFalse(resCallBool); @@ -157,7 +157,7 @@ contract RuleEngineValidationTest is Test, HelperContract { // Assert2 // false because the ruleWhitelist is still present - resBool = ruleEngineMock.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleEngineMock.canTransfer(ADDRESS1, ADDRESS2, 20); assertFalse(resBool); } diff --git a/test/RuleSanctionList/RuleSanctionListAddTest.t.sol b/test/RuleSanctionList/RuleSanctionListAddTest.t.sol index 5e38475..2a92081 100644 --- a/test/RuleSanctionList/RuleSanctionListAddTest.t.sol +++ b/test/RuleSanctionList/RuleSanctionListAddTest.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; import "../utils/SanctionListOracle.sol"; - +import {RuleSanctionList, SanctionsList} from "src/rules/validation/RuleSanctionList.sol"; /** * @title General functions of the ruleSanctionList */ diff --git a/test/RuleSanctionList/RuleSanctionListDeploymentTest.t.sol b/test/RuleSanctionList/RuleSanctionListDeploymentTest.t.sol index 99870f4..ee2e75f 100644 --- a/test/RuleSanctionList/RuleSanctionListDeploymentTest.t.sol +++ b/test/RuleSanctionList/RuleSanctionListDeploymentTest.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import "../HelperContract.sol"; import "CMTAT/mocks/MinimalForwarderMock.sol"; import "../utils/SanctionListOracle.sol"; - +import {RuleSanctionList, SanctionsList} from "src/rules/validation/RuleSanctionList.sol"; /** * @title General functions of the ruleSanctionList */ diff --git a/test/RuleSanctionList/RuleSanctionListTest.t.sol b/test/RuleSanctionList/RuleSanctionListTest.t.sol index 749e950..7060d40 100644 --- a/test/RuleSanctionList/RuleSanctionListTest.t.sol +++ b/test/RuleSanctionList/RuleSanctionListTest.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; import "../utils/SanctionListOracle.sol"; - +import {RuleSanctionList, SanctionsList} from "src/rules/validation/RuleSanctionList.sol"; /** * @title General functions of the ruleSanctionList */ @@ -69,26 +69,26 @@ contract RuleSanctionlistTest is Test, HelperContract { assertEq(resString, TEXT_CODE_NOT_FOUND); } - function testValidateTransfer() public { + function testcanTransfer() public { // Act // ADDRESS1 -> ADDRESS2 - resBool = ruleSanctionList.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleSanctionList.canTransfer(ADDRESS1, ADDRESS2, 20); assertEq(resBool, true); // ADDRESS2 -> ADDRESS1 - resBool = ruleSanctionList.validateTransfer(ADDRESS2, ADDRESS1, 20); + resBool = ruleSanctionList.canTransfer(ADDRESS2, ADDRESS1, 20); assertEq(resBool, true); } function testTransferFromDetectedAsInvalid() public { // Act - resBool = ruleSanctionList.validateTransfer(ATTACKER, ADDRESS2, 20); + resBool = ruleSanctionList.canTransfer(ATTACKER, ADDRESS2, 20); // Assert assertFalse(resBool); } function testTransferToDetectedAsInvalid() public { // Act - resBool = ruleSanctionList.validateTransfer(ADDRESS1, ATTACKER, 20); + resBool = ruleSanctionList.canTransfer(ADDRESS1, ATTACKER, 20); // Assert assertFalse(resBool); } diff --git a/test/RuleWhitelist/CMTATIntegration.t.sol b/test/RuleWhitelist/CMTATIntegration.t.sol index f0c9274..246d9db 100644 --- a/test/RuleWhitelist/CMTATIntegration.t.sol +++ b/test/RuleWhitelist/CMTATIntegration.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; @@ -50,7 +50,7 @@ contract CMTATIntegration is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21 @@ -69,7 +69,7 @@ contract CMTATIntegration is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount @@ -88,7 +88,7 @@ contract CMTATIntegration is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount diff --git a/test/RuleWhitelist/CMTATIntegrationWhitelistWrapper.t.sol b/test/RuleWhitelist/CMTATIntegrationWhitelistWrapper.t.sol index 4b7f4a8..5786da9 100644 --- a/test/RuleWhitelist/CMTATIntegrationWhitelistWrapper.t.sol +++ b/test/RuleWhitelist/CMTATIntegrationWhitelistWrapper.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; +import "CMTAT/deployment/CMTATStandalone.sol"; import "../HelperContract.sol"; import "src/RuleEngine.sol"; @@ -75,7 +75,7 @@ contract CMTATIntegrationWhitelistWrapper is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, 21 @@ -94,7 +94,7 @@ contract CMTATIntegrationWhitelistWrapper is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount @@ -113,7 +113,7 @@ contract CMTATIntegrationWhitelistWrapper is Test, HelperContract { vm.prank(ADDRESS1); vm.expectRevert( abi.encodeWithSelector( - Errors.CMTAT_InvalidTransfer.selector, + RuleEngine_InvalidTransfer.selector, ADDRESS1, ADDRESS2, amount diff --git a/test/RuleWhitelist/RuleWhitelist.t.sol b/test/RuleWhitelist/RuleWhitelist.t.sol index 15ebc52..76af66b 100644 --- a/test/RuleWhitelist/RuleWhitelist.t.sol +++ b/test/RuleWhitelist/RuleWhitelist.t.sol @@ -131,21 +131,21 @@ contract RuleWhitelistTest is Test, HelperContract { assertEq(resString, TEXT_CODE_NOT_FOUND); } - function testValidateTransfer() public { + function testcanTransfer() public { // Arrange _addAddressesToTheList(); // Act // ADDRESS1 -> ADDRESS2 - resBool = ruleWhitelist.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleWhitelist.canTransfer(ADDRESS1, ADDRESS2, 20); assertEq(resBool, true); // ADDRESS2 -> ADDRESS1 - resBool = ruleWhitelist.validateTransfer(ADDRESS2, ADDRESS1, 20); + resBool = ruleWhitelist.canTransfer(ADDRESS2, ADDRESS1, 20); assertEq(resBool, true); } function testTransferDetectedAsInvalid() public { // Act - resBool = ruleWhitelist.validateTransfer(ADDRESS1, ADDRESS2, 20); + resBool = ruleWhitelist.canTransfer(ADDRESS1, ADDRESS2, 20); // Assert assertFalse(resBool); } diff --git a/test/utils/CMTATDeployment.sol b/test/utils/CMTATDeployment.sol index c8b5393..1f7d310 100644 --- a/test/utils/CMTATDeployment.sol +++ b/test/utils/CMTATDeployment.sol @@ -3,14 +3,17 @@ pragma solidity ^0.8.20; import "forge-std/Test.sol"; import "OZ/access/AccessControl.sol"; -import "CMTAT/CMTAT_STANDALONE.sol"; - +import {ICMTATConstructor, CMTATStandalone} from "CMTAT/deployment/CMTATStandalone.sol"; +import {IRuleEngine} from "CMTAT/interfaces/engine/IRuleEngine.sol"; +import {ISnapshotEngine} from "CMTAT/interfaces/engine/ISnapshotEngine.sol"; +import {IDocumentEngine} from "CMTAT/interfaces/engine/IDocumentEngine.sol"; +import {IERC1643CMTAT} from "CMTAT/interfaces/tokenization/draft-IERC1643CMTAT.sol"; contract CMTATDeployment { // Share with helper contract address constant ZERO_ADDRESS = address(0); address constant DEFAULT_ADMIN_ADDRESS = address(1); - CMTAT_STANDALONE public cmtat; + CMTATStandalone public cmtat; constructor() { // CMTAT @@ -20,24 +23,27 @@ contract CMTATDeployment { "CMTAT", 0 ); - ICMTATConstructor.BaseModuleAttributes - memory baseModuleAttributes = ICMTATConstructor - .BaseModuleAttributes( + ICMTATConstructor.ExtraInformationAttributes + memory ExtraInformationAttributes = ICMTATConstructor + .ExtraInformationAttributes( "CMTAT_ISIN", - "https://cmta.ch", + IERC1643CMTAT.DocumentInfo( + "Terms", + "https://cmta.ch", + 0x9ff867f6592aa9d6d039e7aad6bd71f1659720cbc4dd9eae1554f6eab490098b + ), "CMTAT_info" ); ICMTATConstructor.Engine memory engines = ICMTATConstructor.Engine( IRuleEngine(ZERO_ADDRESS), - IDebtEngine(ZERO_ADDRESS), - IAuthorizationEngine(ZERO_ADDRESS), - IERC1643(ZERO_ADDRESS) + ISnapshotEngine(ZERO_ADDRESS), + IDocumentEngine(ZERO_ADDRESS) ); - cmtat = new CMTAT_STANDALONE( + cmtat = new CMTATStandalone( ZERO_ADDRESS, DEFAULT_ADMIN_ADDRESS, erc20Attributes, - baseModuleAttributes, + ExtraInformationAttributes, engines ); } From 014d9434389540f7ef4417d4bee2a2c8ef02fb76 Mon Sep 17 00:00:00 2001 From: Ryan Sauge <71391932+rya-sge@users.noreply.github.com> Date: Fri, 4 Jul 2025 18:10:10 +0200 Subject: [PATCH 2/2] Update Readme --- CHANGELOG.md | 13 +++++++++++++ README.md | 8 ++++---- lib/openzeppelin-contracts | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cb1742..22cc998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,19 @@ Please follow [https://changelog.md/](https://changelog.md/) conventions. - Update surya doc by running the 3 scripts in [./doc/script](./doc/script) - Update changelog + + +## v2.1.0 + +- Update RuleEngine to CMTAT v3.0.0-rc5 + +- Add "partial" support of spender check introduced with CMTAT v3.0.0-rc5 + + - Change several functions + - `operateOnTransfer `-> `transferred(...)` + + - Add functions `detectTransferRestrictionFrom` and `canTransferFrom` + ## v2.0.5 - Fix a bug present in the Conditional Transfer rule and improve the corresponding tests. diff --git a/README.md b/README.md index 31f1bdd..0cb8dc0 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ The RuleEngine is an external contract used to apply transfer restrictions to an The toolchain includes the following components, where the versions are the latest ones that we tested: - Foundry [v1.9.4](https://github.com/foundry-rs/forge-std/releases/tag/v1.9.4) -- Solidity 0.8.27 (via solc-js) -- OpenZeppelin Contracts (submodule) [v5.0.2](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.0.2) -- CMTAT [v2.5.1](https://github.com/CMTA/CMTAT/releases/tag/v2.5.1) +- Solidity 0.8.30 (via solc-js) +- OpenZeppelin Contracts (submodule) [v5.3.0](https://github.com/OpenZeppelin/openzeppelin-contracts/releases/tag/v5.3.0) +- CMTAT [v3.0.0](https://github.com/CMTA/CMTAT/releases/tag/v3.0.0) ## How to include it @@ -134,7 +134,7 @@ Here a summary of the main documentation | Functionalities | [doc/functionalities.pdf](./doc/functionalities.pdf) | | Surya report | [doc/surya](./doc/surya/) | -See also [Taurus - Token Transfer Management: How to Apply Restrictions with CMTAT and ERC-1404](https://www.taurushq.com/blog/token-transfer-management-how-to-apply-restrictions-with-cmtat-and-erc-1404/) +See also [Taurus - Token Transfer Management: How to Apply Restrictions with CMTAT and ERC-1404](https://www.taurushq.com/blog/token-transfer-management-how-to-apply-restrictions-with-cmtat-and-erc-1404/) (CMTAT v2.4.0) ## Usage diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index dbb6104..e4f7021 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit dbb6104ce834628e473d2173bbc9d47f81a9eec3 +Subproject commit e4f70216d759d8e6a64144a9e1f7bbeed78e7079