Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pause and unpause functions to TREX Gateway #189

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
# 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

- **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`
Expand Down
25 changes: 25 additions & 0 deletions contracts/factory/ITREXGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't use the names "Paused" and "Unpaused" because they are used by the token contract. Otherwise, sharing the same event names as token events would cause ambiguity and collision on the indexer side.


/// event emitted whenever the gateway is unpaused by an admin
event GatewayUnpaused(address _userAddress);

/// Functions

/**
Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand Down
131 changes: 88 additions & 43 deletions contracts/factory/TREXGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,19 @@ 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();

contract TREXGateway is ITREXGateway, AgentRole {
/// Gateway is not paused, you can not call this function
error TREXGatewayIsNotPaused();

contract TREXGateway is ITREXGateway, AgentRole {
/// 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;

Expand All @@ -123,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) {
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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]];
Expand All @@ -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];
Expand All @@ -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];
Expand All @@ -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;
Expand All @@ -295,82 +318,104 @@ 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++) {
deployTREXSuite(_tokenDetails[i], _claimDetails[i]);
}
}

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

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