Skip to content

Commit

Permalink
馃敄 v4.1.4 (#195)
Browse files Browse the repository at this point in the history
* Implement upgradeable proxies to compliance modules

* Add module proxy

* Fix lint issue

* Add initializer tests

* Update changelog and package version

---------

Co-authored-by: ali.arbak <hi@alinaciarbak.com.tr>
  • Loading branch information
Joachim-Lebrun and aliarbak committed Mar 20, 2024
1 parent 0fa344b commit d34e5c5
Show file tree
Hide file tree
Showing 29 changed files with 1,957 additions and 239 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,16 @@
# Change Log
All notable changes to this project will be documented in this file.

## [4.1.4]

### Added

- Introduced **AbstractModuleUpgradeable**: Enables compliance modules to be compatible with ERC-1967 and ERC-1822 standards.
- Introduced **ModuleProxy**: ERC-1967 compliant proxy contract for Upgradeable compliance modules.

### Update
- Upgraded all compliance modules to inherit from `AbstractModuleUpgradeable` and made them Upgradeable.

## [4.1.3]

### Update
Expand Down
80 changes: 80 additions & 0 deletions contracts/_testContracts/TestUpgradedCountryAllowModule.sol
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny s脿rl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.17;

import "../compliance/modular/modules/CountryAllowModule.sol";

contract TestUpgradedCountryAllowModule is CountryAllowModule {
/// new field
uint256 private _newField;

// setter for _newField
function setNewField(uint256 value) external onlyOwner {
_newField = value;
}

// getter for _newField
function getNewField() external view returns (uint256) {
return _newField;
}
}
147 changes: 147 additions & 0 deletions contracts/compliance/modular/modules/AbstractModuleUpgradeable.sol
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-3.0
//
// :+#####%%%%%%%%%%%%%%+
// .-*@@@%+.:+%@@@@@%%#***%@@%=
// :=*%@@@#=. :#@@% *@@@%=
// .-+*%@%*-.:+%@@@@@@+. -*+: .=#. :%@@@%-
// :=*@@@@%%@@@@@@@@@%@@@- .=#@@@%@%= =@@@@#.
// -=+#%@@%#*=:. :%@@@@%. -*@@#*@@@@@@@#=:- *@@@@+
// =@@%=:. :=: *@@@@@%#- =%*%@@@@#+-. =+ :%@@@%-
// -@@%. .+@@@ =+=-. @@#- +@@@%- =@@@@%:
// :@@@. .+@@#%: : .=*=-::.-%@@@+*@@= +@@@@#.
// %@@: +@%%* =%@@@@@@@@@@@#. .*@%- +@@@@*.
// #@@= .+@@@@%:=*@@@@@- :%@%: .*@@@@+
// *@@* +@@@#-@@%-:%@@* +@@#. :%@@@@-
// -@@% .:-=++*##%%%@@@@@@@@@@@@*. :@+.@@@%: .#@@+ =@@@@#:
// .@@@*-+*#%%%@@@@@@@@@@@@@@@@%%#**@@%@@@. *@=*@@# :#@%= .#@@@@#-
// -%@@@@@@@@@@@@@@@*+==-:-@@@= *@# .#@*-=*@@@@%= -%@@@* =@@@@@%-
// -+%@@@#. %@%%= -@@:+@: -@@* *@@*-:: -%@@%=. .*@@@@@#
// *@@@* +@* *@@##@@- #@*@@+ -@@= . :+@@@#: .-+@@@%+-
// +@@@%*@@:..=@@@@* .@@@* .#@#. .=+- .=%@@@*. :+#@@@@*=:
// =@@@@%@@@@@@@@@@@@@@@@@@@@@@%- :+#*. :*@@@%=. .=#@@@@%+:
// .%@@= ..... .=#@@+. .#@@@*: -*%@@@@%+.
// +@@#+===---:::... .=%@@*- +@@@+. -*@@@@@%+.
// -@@@@@@@@@@@@@@@@@@@@@@%@@@@= -@@@+ -#@@@@@#=.
// ..:::---===+++***###%%%@@@#- .#@@+ -*@@@@@#=.
// @@@@@@+. +@@*. .+@@@@@%=.
// -@@@@@= =@@%: -#@@@@%+.
// +@@@@@. =@@@= .+@@@@@*:
// #@@@@#:%@@#. :*@@@@#-
// @@@@@%@@@= :#@@@@+.
// :@@@@@@@#.:#@@@%-
// +@@@@@@-.*@@@*:
// #@@@@#.=@@@+.
// @@@@+-%@%=
// :@@@#%@%=
// +@@@@%-
// :#%%=
//
/**
* NOTICE
*
* The T-REX software is licensed under a proprietary license or the GPL v.3.
* If you choose to receive it under the GPL v.3 license, the following applies:
* T-REX is a suite of smart contracts implementing the ERC-3643 standard and
* developed by Tokeny to manage and transfer financial assets on EVM blockchains
*
* Copyright (C) 2023, Tokeny s脿rl.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.17;

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "./IModule.sol";

abstract contract AbstractModuleUpgradeable is IModule, Initializable, OwnableUpgradeable, UUPSUpgradeable {
struct AbstractModuleStorage {
/// compliance contract binding status
mapping(address => bool) complianceBound;
}

// keccak256(abi.encode(uint256(keccak256("ERC3643.storage.AbstractModule")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _ABSTRACT_MODULE_STORAGE_LOCATION =
0xf6cc97de1266c180cd39f3b311632644143ce7873d2927755382ad4b39e8ae00;

/**
* @dev Throws if `_compliance` is not a bound compliance contract address.
*/
modifier onlyBoundCompliance(address _compliance) {
AbstractModuleStorage storage s = _getAbstractModuleStorage();
require(s.complianceBound[_compliance], "compliance not bound");
_;
}

/**
* @dev Throws if called from an address that is not a bound compliance contract.
*/
modifier onlyComplianceCall() {
AbstractModuleStorage storage s = _getAbstractModuleStorage();
require(s.complianceBound[msg.sender], "only bound compliance can call");
_;
}

/**
* @dev See {IModule-bindCompliance}.
*/
function bindCompliance(address _compliance) external override {
AbstractModuleStorage storage s = _getAbstractModuleStorage();
require(_compliance != address(0), "invalid argument - zero address");
require(!s.complianceBound[_compliance], "compliance already bound");
require(msg.sender == _compliance, "only compliance contract can call");
s.complianceBound[_compliance] = true;
emit ComplianceBound(_compliance);
}

/**
* @dev See {IModule-unbindCompliance}.
*/
function unbindCompliance(address _compliance) external onlyComplianceCall override {
AbstractModuleStorage storage s = _getAbstractModuleStorage();
require(_compliance != address(0), "invalid argument - zero address");
require(msg.sender == _compliance, "only compliance contract can call");
s.complianceBound[_compliance] = false;
emit ComplianceUnbound(_compliance);
}

/**
* @dev See {IModule-isComplianceBound}.
*/
function isComplianceBound(address _compliance) external view override returns (bool) {
AbstractModuleStorage storage s = _getAbstractModuleStorage();
return s.complianceBound[_compliance];
}

// solhint-disable-next-line func-name-mixedcase
function __AbstractModule_init() internal onlyInitializing {
__Ownable_init();
__AbstractModule_init_unchained();
}

// solhint-disable-next-line no-empty-blocks, func-name-mixedcase
function __AbstractModule_init_unchained() internal onlyInitializing { }

// solhint-disable-next-line no-empty-blocks
function _authorizeUpgrade(address /*newImplementation*/) internal override virtual onlyOwner { }

function _getAbstractModuleStorage() private pure returns (AbstractModuleStorage storage s) {
// solhint-disable-next-line no-inline-assembly
assembly {
s.slot := _ABSTRACT_MODULE_STORAGE_LOCATION
}
}
}
12 changes: 10 additions & 2 deletions contracts/compliance/modular/modules/ConditionalTransferModule.sol
Expand Up @@ -64,13 +64,13 @@ pragma solidity 0.8.17;

import "../IModularCompliance.sol";
import "../../../token/IToken.sol";
import "./AbstractModule.sol";
import "../../../roles/AgentRole.sol";
import "./AbstractModuleUpgradeable.sol";

/**
* this module allows to require the pre-validation of a transfer before allowing it to be executed
*/
contract ConditionalTransferModule is AbstractModule {
contract ConditionalTransferModule is AbstractModuleUpgradeable {
/// Mapping between transfer details and their approval status (amount of transfers approved) per compliance
mapping(address => mapping(bytes32 => uint)) private _transfersApproved;

Expand All @@ -94,6 +94,14 @@ contract ConditionalTransferModule is AbstractModule {
*/
event ApprovalRemoved(address _from, address _to, uint _amount, address _token);

/**
* @dev initializes the contract and sets the initial state.
* @notice This function should only be called once during the contract deployment.
*/
function initialize() external initializer {
__AbstractModule_init();
}

/**
* @dev Approves transfers in batch
* once a transfer is approved, the sender is allowed to execute it
Expand Down
12 changes: 10 additions & 2 deletions contracts/compliance/modular/modules/CountryAllowModule.sol
Expand Up @@ -64,9 +64,9 @@ pragma solidity 0.8.17;

import "../IModularCompliance.sol";
import "../../../token/IToken.sol";
import "./AbstractModule.sol";
import "./AbstractModuleUpgradeable.sol";

contract CountryAllowModule is AbstractModule {
contract CountryAllowModule is AbstractModuleUpgradeable {
/// Mapping between country and their allowance status per compliance contract
mapping(address => mapping(uint16 => bool)) private _allowedCountries;

Expand All @@ -92,6 +92,14 @@ contract CountryAllowModule is AbstractModule {

/// functions

/**
* @dev initializes the contract and sets the initial state.
* @notice This function should only be called once during the contract deployment.
*/
function initialize() external initializer {
__AbstractModule_init();
}

/**
* @dev Adds country allowance in batch.
* Identities from those countries will be allowed to manipulate Tokens linked to this Compliance.
Expand Down
12 changes: 10 additions & 2 deletions contracts/compliance/modular/modules/CountryRestrictModule.sol
Expand Up @@ -64,9 +64,9 @@ pragma solidity 0.8.17;

import "../IModularCompliance.sol";
import "../../../token/IToken.sol";
import "./AbstractModule.sol";
import "./AbstractModuleUpgradeable.sol";

contract CountryRestrictModule is AbstractModule {
contract CountryRestrictModule is AbstractModuleUpgradeable {
/// Mapping between country and their restriction status per compliance contract
mapping(address => mapping(uint16 => bool)) private _restrictedCountries;

Expand All @@ -84,6 +84,14 @@ contract CountryRestrictModule is AbstractModule {
*/
event RemovedRestrictedCountry(address indexed _compliance, uint16 _country);

/**
* @dev initializes the contract and sets the initial state.
* @notice This function should only be called once during the contract deployment.
*/
function initialize() external initializer {
__AbstractModule_init();
}

/**
* @dev Adds country restriction.
* Identities from those countries will be forbidden to manipulate Tokens linked to this Compliance.
Expand Down
Expand Up @@ -65,11 +65,9 @@ pragma solidity 0.8.17;
import "../IModularCompliance.sol";
import "../../../token/IToken.sol";
import "../../../roles/AgentRole.sol";
import "./AbstractModule.sol";
import "./AbstractModuleUpgradeable.sol";

import "@openzeppelin/contracts/access/Ownable.sol";

contract ExchangeMonthlyLimitsModule is AbstractModule, Ownable {
contract ExchangeMonthlyLimitsModule is AbstractModuleUpgradeable {
/// Struct of transfer Counters
struct ExchangeTransferCounter {
uint256 monthlyCount;
Expand Down Expand Up @@ -112,6 +110,14 @@ contract ExchangeMonthlyLimitsModule is AbstractModule, Ownable {

error ONCHAINIDNotTaggedAsExchange(address _exchangeID);

/**
* @dev initializes the contract and sets the initial state.
* @notice This function should only be called once during the contract deployment.
*/
function initialize() external initializer {
__AbstractModule_init();
}

/**
* @dev Set the limit of tokens allowed to be transferred monthly.
* @param _exchangeID ONCHAINID of the exchange
Expand Down
12 changes: 10 additions & 2 deletions contracts/compliance/modular/modules/MaxBalanceModule.sol
Expand Up @@ -65,9 +65,9 @@ pragma solidity 0.8.17;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "../IModularCompliance.sol";
import "../../../token/IToken.sol";
import "./AbstractModule.sol";
import "./AbstractModuleUpgradeable.sol";

contract MaxBalanceModule is AbstractModule {
contract MaxBalanceModule is AbstractModuleUpgradeable {

/// state variables

Expand Down Expand Up @@ -103,6 +103,14 @@ contract MaxBalanceModule is AbstractModule {

/// functions

/**
* @dev initializes the contract and sets the initial state.
* @notice This function should only be called once during the contract deployment.
*/
function initialize() external initializer {
__AbstractModule_init();
}

/**
* @dev sets max balance limit for a bound compliance contract
* @param _max max amount of tokens owned by an individual
Expand Down

0 comments on commit d34e5c5

Please sign in to comment.