Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/CMTAT
Submodule CMTAT updated 1035 files
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
4 changes: 2 additions & 2 deletions script/CMTATWithRuleEngineScript.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions script/RuleEngineScript.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down
116 changes: 87 additions & 29 deletions src/RuleEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -16,6 +16,7 @@ contract RuleEngine is
RuleEngineValidation,
MetaTxModuleStandalone
{

/**
* @notice
* Get the current version of the smart contract
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
}

Expand Down Expand Up @@ -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 ============ */
Expand Down
10 changes: 9 additions & 1 deletion src/interfaces/IRuleEngineValidation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 11 additions & 3 deletions src/interfaces/IRuleValidation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);

}
1 change: 1 addition & 0 deletions src/modules/RuleEngineInvariantStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
48 changes: 41 additions & 7 deletions src/modules/RuleEngineValidation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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);
}

/**
Expand All @@ -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);
}
}
Loading