From bdc453a92acc2b399422b25510656fffe3723c5a Mon Sep 17 00:00:00 2001 From: aliarbak Date: Wed, 3 Jan 2024 12:14:25 +0300 Subject: [PATCH 1/5] Remove _compliance parameter from setSupplyLimit function of the SupplyLimitModule (#183) --- CHANGELOG.md | 4 ++++ .../modular/modules/SupplyLimitModule.sol | 6 ++--- package-lock.json | 4 ++-- package.json | 2 +- test/compliances/module-supply-limit.test.ts | 24 ++++--------------- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80a4dc38..9753537c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. +## [4.1.2] +- **Compliance Modules**: + - Removed `_compliance` parameter from `setSupplyLimit` function of the `SupplyLimitModule` + ## [4.1.1] No changes, republishing package. diff --git a/contracts/compliance/modular/modules/SupplyLimitModule.sol b/contracts/compliance/modular/modules/SupplyLimitModule.sol index 15c14ece..00bafc30 100644 --- a/contracts/compliance/modular/modules/SupplyLimitModule.sol +++ b/contracts/compliance/modular/modules/SupplyLimitModule.sol @@ -84,9 +84,9 @@ contract SupplyLimitModule is AbstractModule { * Only the owner of the Compliance smart contract can call this function * emits an `SupplyLimitSet` event */ - function setSupplyLimit(address _compliance, uint256 _limit) external onlyComplianceCall { - _supplyLimits[_compliance] = _limit; - emit SupplyLimitSet(_compliance, _limit); + function setSupplyLimit(uint256 _limit) external onlyComplianceCall { + _supplyLimits[msg.sender] = _limit; + emit SupplyLimitSet(msg.sender, _limit); } /** diff --git a/package-lock.json b/package-lock.json index ba8037dd..0fb59117 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tokenysolutions/t-rex", - "version": "4.1.1", + "version": "4.1.2-beta1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tokenysolutions/t-rex", - "version": "4.1.0-beta5", + "version": "4.1.2-beta1", "license": "SEE LICENSE IN LICENSE.md", "devDependencies": { "@commitlint/cli": "^17.6.1", diff --git a/package.json b/package.json index 55686019..d354820d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tokenysolutions/t-rex", - "version": "4.1.1", + "version": "4.1.2", "description": "A fully compliant environment for the issuance and use of tokenized securities.", "main": "index.js", "directories": { diff --git a/test/compliances/module-supply-limit.test.ts b/test/compliances/module-supply-limit.test.ts index d2530ce8..f1b8a7ff 100644 --- a/test/compliances/module-supply-limit.test.ts +++ b/test/compliances/module-supply-limit.test.ts @@ -69,9 +69,7 @@ describe('Compliance Module: SupplyLimit', () => { it('should revert', async () => { const context = await loadFixture(deploySupplyLimitFixture); - await expect(context.suite.complianceModule.setSupplyLimit(context.suite.compliance.address, 100)).to.revertedWith( - 'only bound compliance can call', - ); + await expect(context.suite.complianceModule.setSupplyLimit(100)).to.revertedWith('only bound compliance can call'); }); }); @@ -80,10 +78,7 @@ describe('Compliance Module: SupplyLimit', () => { const context = await loadFixture(deploySupplyLimitFixture); const tx = await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(address _compliance, uint256 _limit)']).encodeFunctionData('setSupplyLimit', [ - context.suite.compliance.address, - 100, - ]), + new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [100]), context.suite.complianceModule.address, ); @@ -97,10 +92,7 @@ describe('Compliance Module: SupplyLimit', () => { it('should return', async () => { const context = await loadFixture(deploySupplyLimitFixture); await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(address _compliance, uint256 _limit)']).encodeFunctionData('setSupplyLimit', [ - context.suite.compliance.address, - 1600, - ]), + new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), context.suite.complianceModule.address, ); const supplyLimit = await context.suite.complianceModule.getSupplyLimit(context.suite.compliance.address); @@ -118,10 +110,7 @@ describe('Compliance Module: SupplyLimit', () => { const from = zeroAddress; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(address _compliance, uint256 _limit)']).encodeFunctionData('setSupplyLimit', [ - context.suite.compliance.address, - 1600, - ]), + new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), context.suite.complianceModule.address, ); @@ -137,10 +126,7 @@ describe('Compliance Module: SupplyLimit', () => { const from = zeroAddress; await context.suite.compliance.callModuleFunction( - new ethers.utils.Interface(['function setSupplyLimit(address _compliance, uint256 _limit)']).encodeFunctionData('setSupplyLimit', [ - context.suite.compliance.address, - 1600, - ]), + new ethers.utils.Interface(['function setSupplyLimit(uint256 _limit)']).encodeFunctionData('setSupplyLimit', [1600]), context.suite.complianceModule.address, ); From d6251aab3433d6f45a2914326ce2fabe3f583cd2 Mon Sep 17 00:00:00 2001 From: joachimlebrun Date: Mon, 12 Feb 2024 15:04:45 +0100 Subject: [PATCH 2/5] :bug: (proxy) modify IA storage slot --- contracts/proxy/AbstractProxy.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/proxy/AbstractProxy.sol b/contracts/proxy/AbstractProxy.sol index 90af9f34..1844e7b4 100644 --- a/contracts/proxy/AbstractProxy.sol +++ b/contracts/proxy/AbstractProxy.sol @@ -93,18 +93,19 @@ abstract contract AbstractProxy is IProxy, Initializable { address implemAuth; // solhint-disable-next-line no-inline-assembly assembly { - implemAuth := sload(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7) + implemAuth := sload(0x821f3e4d3d679f19eacc940c87acf846ea6eae24a63058ea750304437a62aafc) } return implemAuth; } /** - * @dev store the implementationAuthority contract address using the ERC-1822 implementation slot in storage + * @dev store the implementationAuthority contract address using the ERC-3643 implementation slot in storage + * the slot storage is the result of `keccak256("ERC-3643.proxy.beacon")` */ function _storeImplementationAuthority(address implementationAuthority) internal { // solhint-disable-next-line no-inline-assembly assembly { - sstore(0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7, implementationAuthority) + sstore(0x821f3e4d3d679f19eacc940c87acf846ea6eae24a63058ea750304437a62aafc, implementationAuthority) } } From ee783397fe57b632f851e7616fcf0f3581db1299 Mon Sep 17 00:00:00 2001 From: joachimlebrun Date: Mon, 12 Feb 2024 15:25:41 +0100 Subject: [PATCH 3/5] :bookmark: (version) update to version 4.1.3 --- CHANGELOG.md | 10 ++++++++++ contracts/token/TokenStorage.sol | 2 +- package.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9753537c..53268eda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ # Change Log All notable changes to this project will be documented in this file. +## [4.1.3] + +### Update + +- **AbstractProxy**: updated the storage slot for `TREXImplementationAuthority` from + `0xc5f16f0fcc639fa48a6947836d9850f504798523bf8c9a3a87d5876cf622bcf7` to + `0x821f3e4d3d679f19eacc940c87acf846ea6eae24a63058ea750304437a62aafc` to avoid issues with blockchain explorers + confusing the proxy pattern of T-REX for an ERC-1822 proxy (old storage slot was the slot used by ERC-1822) which + caused errors in displaying the right ABIs for proxies using this implementation. + ## [4.1.2] - **Compliance Modules**: - Removed `_compliance` parameter from `setSupplyLimit` function of the `SupplyLimitModule` diff --git a/contracts/token/TokenStorage.sol b/contracts/token/TokenStorage.sol index 046f44bc..a191be8e 100644 --- a/contracts/token/TokenStorage.sol +++ b/contracts/token/TokenStorage.sol @@ -76,7 +76,7 @@ contract TokenStorage { string internal _tokenSymbol; uint8 internal _tokenDecimals; address internal _tokenOnchainID; - string internal constant _TOKEN_VERSION = "4.1.1"; + string internal constant _TOKEN_VERSION = "4.1.3"; /// @dev Variables of freeze and pause functions mapping(address => bool) internal _frozen; diff --git a/package.json b/package.json index d354820d..9321c362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tokenysolutions/t-rex", - "version": "4.1.2", + "version": "4.1.3", "description": "A fully compliant environment for the issuance and use of tokenized securities.", "main": "index.js", "directories": { From 59013ee195a7c817a21886210302cb128e509ddb Mon Sep 17 00:00:00 2001 From: "ali.arbak" Date: Tue, 13 Feb 2024 15:27:45 +0300 Subject: [PATCH 4/5] Add pause and unpause functions to TREX Gateway --- CHANGELOG.md | 9 ++ contracts/factory/ITREXGateway.sol | 25 ++++ contracts/factory/TREXGateway.sol | 137 ++++++++++++++------- contracts/token/TokenStorage.sol | 2 +- package.json | 2 +- test/gateway.test.ts | 190 +++++++++++++++++++++++++++++ 6 files changed, 317 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53268eda..72e5af36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Change Log All notable changes to this project will be documented in this file. +## [4.1.4] + +### Added + +- **TREXGateway Enhancements**: + - New function `pause`: Pauses the TREX gateway contract. When paused, no one can deploy a new token. + - New function `unpause`: Unpauses the TREX gateway contract. + + ## [4.1.3] ### Update diff --git a/contracts/factory/ITREXGateway.sol b/contracts/factory/ITREXGateway.sol index b7d1da92..a24db14f 100644 --- a/contracts/factory/ITREXGateway.sol +++ b/contracts/factory/ITREXGateway.sol @@ -102,6 +102,12 @@ interface ITREXGateway { /// event emitted whenever a TREX token has been deployed by the TREX factory through the use of the Gateway event GatewaySuiteDeploymentProcessed(address indexed requester, address intendedOwner, uint256 feeApplied); + /// event emitted whenever the gateway is paused by an admin + event GatewayPaused(address _userAddress); + + /// event emitted whenever the gateway is unpaused by an admin + event GatewayUnpaused(address _userAddress); + /// Functions /** @@ -232,6 +238,7 @@ interface ITREXGateway { * The actual TREX suite deployment is then triggered via the factory contract, * and a unique salt is derived from the token owner's address and the token name for the deployment. * + * This function can not be executed if the gateway is paused. * @param _tokenDetails Struct containing details necessary for token deployment such as name, symbol, etc. * @param _claimDetails Struct containing details related to claims for the token. * emits GatewaySuiteDeploymentProcessed This event is emitted post-deployment, indicating the deployer, the token @@ -255,6 +262,7 @@ interface ITREXGateway { * Each TREX suite deployment is triggered via the factory contract, with a * unique salt derived from the token owner's address and token name. * + * This function can not be executed if the gateway is paused. * @param _tokenDetails Array of structs, each containing details necessary for token deployment such as name, symbol, etc. * @param _claimDetails Array of structs, each containing details related to claims for the respective token. * reverts with BatchMaxLengthExceeded if the length of either `_tokenDetails` or `_claimDetails` arrays exceeds 5. @@ -269,6 +277,23 @@ interface ITREXGateway { ITREXFactory.TokenDetails[] memory _tokenDetails, ITREXFactory.ClaimDetails[] memory _claimDetails) external; + /** + * @notice Pauses the gateway contract + * @dev When paused, no one can deploy a new token + * This function can only be called by a gateway agent or the contract owner + * emits a `GatewayPaused` event + */ + function pause() external; + + /** + * @notice Unpauses the gateway contract + * @dev When unpaused, users can deploy new tokens + * if they are registered as deployers or public deployments are allowed + * This function can only be called by a gateway agent or the contract owner + * emits a `GatewayUnpaused` event + */ + function unpause() external; + /** * @notice Retrieves the current public deployment status. * @dev Indicates whether public deployments of TREX contracts are currently allowed. diff --git a/contracts/factory/TREXGateway.sol b/contracts/factory/TREXGateway.sol index 847918fb..be937d69 100644 --- a/contracts/factory/TREXGateway.sol +++ b/contracts/factory/TREXGateway.sol @@ -61,10 +61,10 @@ */ pragma solidity 0.8.17; -import "./ITREXGateway.sol"; -import "../roles/AgentRole.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import './ITREXGateway.sol'; +import '../roles/AgentRole.sol'; +import '@openzeppelin/contracts/utils/Strings.sol'; +import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; /// A required parameter was set to the Zero address. error ZeroAddress(); @@ -102,12 +102,35 @@ error OnlyAdminCall(); /// Batch Size is too big, could run out of gas error BatchMaxLengthExceeded(uint16 lengthLimit); +/// Gateway is paused, you can not call this function +error TREXGatewayIsPaused(); + +/// Gateway is not paused, you can not call this function +error TREXGatewayIsNotPaused(); contract TREXGateway is ITREXGateway, AgentRole { + /// @dev Modifier to make a function callable only when the contract is not paused. + modifier whenNotPaused() { + if (_paused) { + revert TREXGatewayIsPaused(); + } + _; + } + + /// @dev Modifier to make a function callable only when the contract is paused. + modifier whenPaused() { + if (!_paused) { + revert TREXGatewayIsNotPaused(); + } + _; + } /// address of the TREX Factory that is managed by the Gateway address private _factory; + // emergency-stop variable + bool private _paused = false; + /// public deployment status variable bool private _publicDeploymentStatus; @@ -136,7 +159,7 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-setFactory}. */ function setFactory(address factory) external override onlyOwner { - if(factory == address(0)) { + if (factory == address(0)) { revert ZeroAddress(); } _factory = factory; @@ -147,10 +170,10 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-setPublicDeploymentStatus}. */ function setPublicDeploymentStatus(bool _isEnabled) external override onlyOwner { - if(_isEnabled == _publicDeploymentStatus && _isEnabled == true) { + if (_isEnabled == _publicDeploymentStatus && _isEnabled == true) { revert PublicDeploymentAlreadyEnabled(); } - if(_isEnabled == _publicDeploymentStatus && _isEnabled == false) { + if (_isEnabled == _publicDeploymentStatus && _isEnabled == false) { revert PublicDeploymentAlreadyDisabled(); } _publicDeploymentStatus = _isEnabled; @@ -168,10 +191,10 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-enableDeploymentFee}. */ function enableDeploymentFee(bool _isEnabled) external override onlyOwner { - if(_isEnabled == _deploymentFeeEnabled && _isEnabled == true) { + if (_isEnabled == _deploymentFeeEnabled && _isEnabled == true) { revert DeploymentFeesAlreadyEnabled(); } - if(_isEnabled == _deploymentFeeEnabled && _isEnabled == false) { + if (_isEnabled == _deploymentFeeEnabled && _isEnabled == false) { revert DeploymentFeesAlreadyDisabled(); } _deploymentFeeEnabled = _isEnabled; @@ -182,7 +205,7 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-setDeploymentFee}. */ function setDeploymentFee(uint256 _fee, address _feeToken, address _feeCollector) external override onlyOwner { - if(_feeToken == address(0) || _feeCollector == address(0)) { + if (_feeToken == address(0) || _feeCollector == address(0)) { revert ZeroAddress(); } _deploymentFee.fee = _fee; @@ -195,14 +218,14 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-batchAddDeployer}. */ function batchAddDeployer(address[] calldata deployers) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { + if (!isAgent(msg.sender) && msg.sender != owner()) { revert OnlyAdminCall(); } - if(deployers.length > 500) { + if (deployers.length > 500) { revert BatchMaxLengthExceeded(500); } for (uint256 i = 0; i < deployers.length; i++) { - if(isDeployer(deployers[i])) { + if (isDeployer(deployers[i])) { revert DeployerAlreadyExists(deployers[i]); } _deployers[deployers[i]] = true; @@ -214,10 +237,10 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-addDeployer}. */ function addDeployer(address deployer) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { + if (!isAgent(msg.sender) && msg.sender != owner()) { revert OnlyAdminCall(); } - if(isDeployer(deployer)) { + if (isDeployer(deployer)) { revert DeployerAlreadyExists(deployer); } _deployers[deployer] = true; @@ -228,14 +251,14 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-batchRemoveDeployer}. */ function batchRemoveDeployer(address[] calldata deployers) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { + if (!isAgent(msg.sender) && msg.sender != owner()) { revert OnlyAdminCall(); } - if(deployers.length > 500) { + if (deployers.length > 500) { revert BatchMaxLengthExceeded(500); } for (uint256 i = 0; i < deployers.length; i++) { - if(!isDeployer(deployers[i])) { + if (!isDeployer(deployers[i])) { revert DeployerDoesNotExist(deployers[i]); } delete _deployers[deployers[i]]; @@ -247,10 +270,10 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-removeDeployer}. */ function removeDeployer(address deployer) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { + if (!isAgent(msg.sender) && msg.sender != owner()) { revert OnlyAdminCall(); } - if(!isDeployer(deployer)) { + if (!isDeployer(deployer)) { revert DeployerDoesNotExist(deployer); } delete _deployers[deployer]; @@ -261,14 +284,14 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-batchApplyFeeDiscount}. */ function batchApplyFeeDiscount(address[] calldata deployers, uint16[] calldata discounts) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { + if (!isAgent(msg.sender) && msg.sender != owner()) { revert OnlyAdminCall(); } - if(deployers.length > 500) { + if (deployers.length > 500) { revert BatchMaxLengthExceeded(500); } for (uint256 i = 0; i < deployers.length; i++) { - if(discounts[i] > 10000) { + if (discounts[i] > 10000) { revert DiscountOutOfRange(); } _feeDiscount[deployers[i]] = discounts[i]; @@ -280,10 +303,10 @@ contract TREXGateway is ITREXGateway, AgentRole { * @dev See {ITREXGateway-applyFeeDiscount}. */ function applyFeeDiscount(address deployer, uint16 discount) external override { - if(!isAgent(msg.sender) && msg.sender != owner()) { + if (!isAgent(msg.sender) && msg.sender != owner()) { revert OnlyAdminCall(); } - if(discount > 10000) { + if (discount > 10000) { revert DiscountOutOfRange(); } _feeDiscount[deployer] = discount; @@ -295,9 +318,9 @@ contract TREXGateway is ITREXGateway, AgentRole { */ function batchDeployTREXSuite( ITREXFactory.TokenDetails[] memory _tokenDetails, - ITREXFactory.ClaimDetails[] memory _claimDetails) external override - { - if(_tokenDetails.length > 5) { + ITREXFactory.ClaimDetails[] memory _claimDetails + ) external override whenNotPaused { + if (_tokenDetails.length > 5) { revert BatchMaxLengthExceeded(5); } for (uint256 i = 0; i < _tokenDetails.length; i++) { @@ -305,57 +328,79 @@ contract TREXGateway is ITREXGateway, AgentRole { } } + /** + * @dev See {ITREXGateway-pause}. + */ + function pause() external override whenNotPaused { + if (!isAgent(msg.sender) && msg.sender != owner()) { + revert OnlyAdminCall(); + } + + _paused = true; + emit GatewayPaused(msg.sender); + } + + /** + * @dev See {ITREXGateway-unpause}. + */ + function unpause() external override whenPaused { + if (!isAgent(msg.sender) && msg.sender != owner()) { + revert OnlyAdminCall(); + } + + _paused = false; + emit GatewayUnpaused(msg.sender); + } + /** * @dev See {ITREXGateway-getPublicDeploymentStatus}. */ - function getPublicDeploymentStatus() external override view returns(bool) { + function getPublicDeploymentStatus() external view override returns (bool) { return _publicDeploymentStatus; } /** * @dev See {ITREXGateway-getFactory}. */ - function getFactory() external override view returns(address) { + function getFactory() external view override returns (address) { return _factory; } /** * @dev See {ITREXGateway-getDeploymentFee}. */ - function getDeploymentFee() external override view returns(Fee memory) { + function getDeploymentFee() external view override returns (Fee memory) { return _deploymentFee; } /** * @dev See {ITREXGateway-isDeploymentFeeEnabled}. */ - function isDeploymentFeeEnabled() external override view returns(bool) { + function isDeploymentFeeEnabled() external view override returns (bool) { return _deploymentFeeEnabled; } /** * @dev See {ITREXGateway-deployTREXSuite}. */ - function deployTREXSuite(ITREXFactory.TokenDetails memory _tokenDetails, ITREXFactory.ClaimDetails memory _claimDetails) - public override { - if(_publicDeploymentStatus == false && !isDeployer(msg.sender)) { + function deployTREXSuite( + ITREXFactory.TokenDetails memory _tokenDetails, + ITREXFactory.ClaimDetails memory _claimDetails + ) public override whenNotPaused { + if (_publicDeploymentStatus == false && !isDeployer(msg.sender)) { revert PublicDeploymentsNotAllowed(); } - if(_publicDeploymentStatus == true && msg.sender != _tokenDetails.owner && !isDeployer(msg.sender)) { + if (_publicDeploymentStatus == true && msg.sender != _tokenDetails.owner && !isDeployer(msg.sender)) { revert PublicCannotDeployOnBehalf(); } uint256 feeApplied = 0; - if(_deploymentFeeEnabled == true) { - if(_deploymentFee.fee > 0 && _feeDiscount[msg.sender] < 10000) { + if (_deploymentFeeEnabled == true) { + if (_deploymentFee.fee > 0 && _feeDiscount[msg.sender] < 10000) { feeApplied = calculateFee(msg.sender); - IERC20(_deploymentFee.feeToken).transferFrom( - msg.sender, - _deploymentFee.feeCollector, - feeApplied - ); + IERC20(_deploymentFee.feeToken).transferFrom(msg.sender, _deploymentFee.feeCollector, feeApplied); } } - string memory _salt = string(abi.encodePacked(Strings.toHexString(_tokenDetails.owner), _tokenDetails.name)); + string memory _salt = string(abi.encodePacked(Strings.toHexString(_tokenDetails.owner), _tokenDetails.name)); ITREXFactory(_factory).deployTREXSuite(_salt, _tokenDetails, _claimDetails); emit GatewaySuiteDeploymentProcessed(msg.sender, _tokenDetails.owner, feeApplied); } @@ -363,14 +408,14 @@ contract TREXGateway is ITREXGateway, AgentRole { /** * @dev See {ITREXGateway-isDeployer}. */ - function isDeployer(address deployer) public override view returns(bool) { + function isDeployer(address deployer) public view override returns (bool) { return _deployers[deployer]; } /** * @dev See {ITREXGateway-calculateFee}. */ - function calculateFee(address deployer) public override view returns(uint256) { + function calculateFee(address deployer) public view override returns (uint256) { return _deploymentFee.fee - ((_feeDiscount[deployer] * _deploymentFee.fee) / 10000); } } diff --git a/contracts/token/TokenStorage.sol b/contracts/token/TokenStorage.sol index a191be8e..1217bcde 100644 --- a/contracts/token/TokenStorage.sol +++ b/contracts/token/TokenStorage.sol @@ -76,7 +76,7 @@ contract TokenStorage { string internal _tokenSymbol; uint8 internal _tokenDecimals; address internal _tokenOnchainID; - string internal constant _TOKEN_VERSION = "4.1.3"; + string internal constant _TOKEN_VERSION = "4.1.4"; /// @dev Variables of freeze and pause functions mapping(address => bool) internal _frozen; diff --git a/package.json b/package.json index 9321c362..d0846aab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tokenysolutions/t-rex", - "version": "4.1.3", + "version": "4.1.4", "description": "A fully compliant environment for the issuance and use of tokenized securities.", "main": "index.js", "directories": { diff --git a/test/gateway.test.ts b/test/gateway.test.ts index 0c5113b6..c87c27f6 100644 --- a/test/gateway.test.ts +++ b/test/gateway.test.ts @@ -674,6 +674,37 @@ describe('TREXGateway', () => { }); }); describe('.deployTREXSuite()', () => { + describe('when contract is paused', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + await gateway.pause(); + + await expect( + gateway.connect(context.accounts.anotherWallet).deployTREXSuite( + { + owner: context.accounts.anotherWallet.address, + name: 'Token name', + symbol: 'SYM', + decimals: 8, + irs: ethers.constants.AddressZero, + ONCHAINID: ethers.constants.AddressZero, + irAgents: [], + tokenAgents: [], + complianceModules: [], + complianceSettings: [], + }, + { + claimTopics: [], + issuers: [], + issuerClaims: [], + }, + ), + ).to.be.revertedWithCustomError(gateway, 'TREXGatewayIsPaused'); + }); + }); describe('when called by not deployer', () => { describe('when public deployments disabled', () => { it('should revert', async () => { @@ -1030,6 +1061,41 @@ describe('TREXGateway', () => { }); }); describe('.batchDeployTREXSuite()', () => { + describe('when contract is paused', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + await gateway.pause(); + + const tokenDetailsArray = []; + const claimDetailsArray = []; + for (let i = 0; i < 5; i += 1) { + tokenDetailsArray.push({ + owner: context.accounts.anotherWallet.address, + name: `Token name ${i}`, + symbol: `SYM${i}`, + decimals: 8, + irs: ethers.constants.AddressZero, + ONCHAINID: ethers.constants.AddressZero, + irAgents: [], + tokenAgents: [], + complianceModules: [], + complianceSettings: [], + }); + claimDetailsArray.push({ + claimTopics: [], + issuers: [], + issuerClaims: [], + }); + } + + await expect( + gateway.connect(context.accounts.anotherWallet).batchDeployTREXSuite(tokenDetailsArray, claimDetailsArray), + ).to.be.revertedWithCustomError(gateway, 'TREXGatewayIsPaused'); + }); + }); describe('when called by not deployer', () => { describe('when public deployments disabled', () => { it('should revert for batch deployment', async () => { @@ -1361,4 +1427,128 @@ describe('TREXGateway', () => { }); }); }); + describe('.pause()', () => { + describe('when called by not admin', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + + await expect(gateway.connect(context.accounts.anotherWallet).pause()).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + }); + }); + describe('when called by owner', () => { + describe('if contract is already paused', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + + await gateway.pause(); + await expect(gateway.pause()).to.be.revertedWithCustomError(gateway, 'TREXGatewayIsPaused'); + }); + }); + describe('if contract is not paused', () => { + it('should pause', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + + const tx = await gateway.pause(); + expect(tx).to.emit(gateway, 'GatewayPaused').withArgs(context.accounts.deployer.address); + }); + }); + }); + describe('when called by agent', () => { + describe('if contract is already paused', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + + await gateway.addAgent(context.accounts.tokenAgent.address); + await gateway.pause(); + await expect(gateway.connect(context.accounts.tokenAgent).pause()).to.be.revertedWithCustomError(gateway, 'TREXGatewayIsPaused'); + }); + }); + describe('if contract is not paused', () => { + it('should pause', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + + await gateway.addAgent(context.accounts.tokenAgent.address); + const tx = await gateway.connect(context.accounts.tokenAgent).pause(); + expect(tx).to.emit(gateway, 'GatewayPaused').withArgs(context.accounts.tokenAgent.address); + }); + }); + }); + }); + describe('.unpause()', () => { + describe('when called by not admin', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [context.factories.trexFactory.address, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + await gateway.pause(); + + await expect(gateway.connect(context.accounts.anotherWallet).unpause()).to.be.revertedWithCustomError(gateway, 'OnlyAdminCall'); + }); + }); + describe('when called by owner', () => { + describe('if contract is not paused', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + await expect(gateway.unpause()).to.be.revertedWithCustomError(gateway, 'TREXGatewayIsNotPaused'); + }); + }); + describe('if contract is paused', () => { + it('should unpause', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + await gateway.pause(); + + const tx = await gateway.unpause(); + expect(tx).to.emit(gateway, 'GatewayUnpaused').withArgs(context.accounts.deployer.address); + }); + }); + }); + describe('when called by agent', () => { + describe('if contract is not paused', () => { + it('should revert', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + + await gateway.addAgent(context.accounts.tokenAgent.address); + await expect(gateway.connect(context.accounts.tokenAgent).unpause()).to.be.revertedWithCustomError(gateway, 'TREXGatewayIsNotPaused'); + }); + }); + describe('if contract is paused', () => { + it('should unpause', async () => { + const context = await loadFixture(deployFullSuiteFixture); + + const gateway = await ethers.deployContract('TREXGateway', [ethers.constants.AddressZero, false], context.accounts.deployer); + await context.factories.trexFactory.transferOwnership(gateway.address); + await gateway.pause(); + + await gateway.addAgent(context.accounts.tokenAgent.address); + const tx = await gateway.connect(context.accounts.tokenAgent).unpause(); + expect(tx).to.emit(gateway, 'GatewayUnpaused').withArgs(context.accounts.tokenAgent.address); + }); + }); + }); + }); }); From 544b66bb0b23ff73cdacd2dbda3588ce172f5aed Mon Sep 17 00:00:00 2001 From: "ali.arbak" Date: Tue, 13 Feb 2024 15:29:59 +0300 Subject: [PATCH 5/5] Fix lint issues --- contracts/factory/TREXGateway.sol | 40 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/contracts/factory/TREXGateway.sol b/contracts/factory/TREXGateway.sol index be937d69..43e5d719 100644 --- a/contracts/factory/TREXGateway.sol +++ b/contracts/factory/TREXGateway.sol @@ -61,10 +61,10 @@ */ pragma solidity 0.8.17; -import './ITREXGateway.sol'; -import '../roles/AgentRole.sol'; -import '@openzeppelin/contracts/utils/Strings.sol'; -import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import "./ITREXGateway.sol"; +import "../roles/AgentRole.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// A required parameter was set to the Zero address. error ZeroAddress(); @@ -109,22 +109,6 @@ error TREXGatewayIsPaused(); error TREXGatewayIsNotPaused(); contract TREXGateway is ITREXGateway, AgentRole { - /// @dev Modifier to make a function callable only when the contract is not paused. - modifier whenNotPaused() { - if (_paused) { - revert TREXGatewayIsPaused(); - } - _; - } - - /// @dev Modifier to make a function callable only when the contract is paused. - modifier whenPaused() { - if (!_paused) { - revert TREXGatewayIsNotPaused(); - } - _; - } - /// address of the TREX Factory that is managed by the Gateway address private _factory; @@ -146,6 +130,22 @@ contract TREXGateway is ITREXGateway, AgentRole { /// mapping for deployment discounts on fees mapping(address => uint16) private _feeDiscount; + /// @dev Modifier to make a function callable only when the contract is not paused. + modifier whenNotPaused() { + if (_paused) { + revert TREXGatewayIsPaused(); + } + _; + } + + /// @dev Modifier to make a function callable only when the contract is paused. + modifier whenPaused() { + if (!_paused) { + revert TREXGatewayIsNotPaused(); + } + _; + } + /// constructor of the contract, setting up the factory address and /// the public deployment status constructor(address factory, bool publicDeploymentStatus) {