From 8a65a37d387fa2f257a6ea60084b6fe1fbddd2ce Mon Sep 17 00:00:00 2001 From: "Eugene Y. Q. Shen" Date: Sun, 1 Dec 2024 19:34:28 -0500 Subject: [PATCH] add ability to pause AggregateTokens (#104) --- nest/src/AggregateToken.sol | 65 ++++++++++++++++++++++++- nest/src/ComponentToken.sol | 4 +- nest/src/interfaces/IComponentToken.sol | 4 +- 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/nest/src/AggregateToken.sol b/nest/src/AggregateToken.sol index 6ecd4cd2..0b8fcd84 100644 --- a/nest/src/AggregateToken.sol +++ b/nest/src/AggregateToken.sol @@ -28,6 +28,8 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { uint256 askPrice; /// @dev Price at which users can sell the AggregateToken to receive `asset`, times the base uint256 bidPrice; + /// @dev True if the AggregateToken contract is paused for deposits, false otherwise + bool paused; } // keccak256(abi.encode(uint256(keccak256("plume.storage.AggregateToken")) - 1)) & ~bytes32(uint256(0xff)) @@ -47,6 +49,14 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { /// @notice Role for the price updater of the AggregateToken bytes32 public constant PRICE_UPDATER_ROLE = keccak256("PRICE_UPDATER_ROLE"); + // Events + + /// @notice Emitted when the AggregateToken contract is paused for deposits + event Paused(); + + /// @notice Emitted when the AggregateToken contract is unpaused for deposits + event Unpaused(); + // Errors /** @@ -80,6 +90,15 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { */ error InvalidAsset(IERC20 invalidAsset, IERC20 asset); + /// @notice Indicates a failure because the contract is paused for deposits + error DepositPaused(); + + /// @notice Indicates a failure because the contract is already paused for deposits + error AlreadyPaused(); + + /// @notice Indicates a failure because the contract is not paused for deposits + error NotPaused(); + // Initializer /** @@ -114,6 +133,7 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { $.componentTokenMap[asset_] = true; $.askPrice = askPrice; $.bidPrice = bidPrice; + $.paused = false; } // Override Functions @@ -143,6 +163,18 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { return super.asset(); } + /// @inheritdoc IComponentToken + function deposit( + uint256 assets, + address receiver, + address controller + ) public override(ComponentToken, IComponentToken) returns (uint256 shares) { + if (_getAggregateTokenStorage().paused) { + revert DepositPaused(); + } + return super.deposit(assets, receiver, controller); + } + /// @inheritdoc IComponentToken function redeem( uint256 shares, @@ -248,7 +280,7 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { emit ComponentTokenSellRequested(msg.sender, componentToken, componentTokenAmount, requestId); } - // Admin Setter Functions + // Admin Functions /** * @notice Set the price at which users can buy the AggregateToken using `asset` @@ -272,6 +304,32 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { _getAggregateTokenStorage().bidPrice = bidPrice; } + /** + * @notice Pause the AggregateToken contract for deposits + * @dev Only the owner can pause the AggregateToken contract for deposits + */ + function pause() external onlyRole(ADMIN_ROLE) { + AggregateTokenStorage storage $ = _getAggregateTokenStorage(); + if ($.paused) { + revert AlreadyPaused(); + } + $.paused = true; + emit Paused(); + } + + /** + * @notice Unpause the AggregateToken contract for deposits + * @dev Only the owner can unpause the AggregateToken contract for deposits + */ + function unpause() external onlyRole(ADMIN_ROLE) { + AggregateTokenStorage storage $ = _getAggregateTokenStorage(); + if (!$.paused) { + revert NotPaused(); + } + $.paused = false; + emit Unpaused(); + } + // Getter View Functions /// @notice Price at which users can buy the AggregateToken using `asset`, times the base @@ -289,6 +347,11 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder { return _getAggregateTokenStorage().componentTokenList; } + /// @notice Returns true if the AggregateToken contract is paused for deposits + function isPaused() external view returns (bool) { + return _getAggregateTokenStorage().paused; + } + /** * @notice Check if the given ComponentToken is in the component token list * @param componentToken ComponentToken to check diff --git a/nest/src/ComponentToken.sol b/nest/src/ComponentToken.sol index 3a540b77..d27e51a1 100644 --- a/nest/src/ComponentToken.sol +++ b/nest/src/ComponentToken.sol @@ -209,7 +209,9 @@ abstract contract ComponentToken is /// @dev Reverts with Unimplemented() until convertToAssets is implemented by the concrete contract /// @param owner Address to query the balance of /// @return assets Total value held by the owner - function assetsOf(address owner) public view virtual returns (uint256 assets) { + function assetsOf( + address owner + ) public view virtual returns (uint256 assets) { return convertToAssets(balanceOf(owner)); } diff --git a/nest/src/interfaces/IComponentToken.sol b/nest/src/interfaces/IComponentToken.sol index fe3b2ee3..1df421a1 100644 --- a/nest/src/interfaces/IComponentToken.sol +++ b/nest/src/interfaces/IComponentToken.sol @@ -102,7 +102,9 @@ interface IComponentToken { * @param owner Address to query the balance of * @return assets Total value held by the owner */ - function assetsOf(address owner) external view returns (uint256 assets); + function assetsOf( + address owner + ) external view returns (uint256 assets); /** * @notice Equivalent amount of shares for the given amount of assets