From a80a565afb07909a7f9ec950e11a768384517896 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 29 Jan 2025 12:34:09 -0500 Subject: [PATCH 1/4] upgrading pause --- .../LenderCommitmentGroup_Smart.sol | 25 ++-- .../LenderPoolPauseableUpgradeable.sol | 129 ++++++++++++++++++ 2 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 0f14ba1fc..0a6454173 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -36,6 +36,7 @@ import "../../../libraries/uniswap/FullMath.sol"; import {LenderCommitmentGroupShares} from "./LenderCommitmentGroupShares.sol"; +import {LenderPoolPauseableUpgradeable} from "./LenderPoolPauseableUpgradeable.sol"; import {OracleProtectedChild} from "../../../oracleprotection/OracleProtectedChild.sol"; @@ -49,6 +50,8 @@ import { ILoanRepaymentCallbacks } from "../../../interfaces/ILoanRepaymentCallb import { IEscrowVault } from "../../../interfaces/IEscrowVault.sol"; + + import { IPausableTimestamp } from "../../../interfaces/IPausableTimestamp.sol"; import { ILenderCommitmentGroup } from "../../../interfaces/ILenderCommitmentGroup.sol"; import { Payment } from "../../../TellerV2Storage.sol"; @@ -88,7 +91,7 @@ contract LenderCommitmentGroup_Smart is Initializable, OracleProtectedChild, OwnableUpgradeable, - PausableUpgradeable, + LenderPoolPauseableUpgradeable, ReentrancyGuardUpgradeable { using AddressUpgradeable for address; @@ -456,7 +459,7 @@ contract LenderCommitmentGroup_Smart is uint256 _amount, address _sharesRecipient, uint256 _minSharesAmountOut - ) external whenForwarderNotPaused whenNotPaused nonReentrant onlyOracleApprovedAllowEOA + ) external whenForwarderNotPaused whenPoolNotPaused nonReentrant onlyOracleApprovedAllowEOA returns (uint256 sharesAmount_) { uint256 principalTokenBalanceBefore = principalToken.balanceOf(address(this)); @@ -526,7 +529,7 @@ contract LenderCommitmentGroup_Smart is uint256 _collateralTokenId, uint32 _loanDuration, uint16 _interestRate - ) external onlySmartCommitmentForwarder whenForwarderNotPaused whenNotPaused { + ) external onlySmartCommitmentForwarder whenForwarderNotPaused whenPoolNotPaused { require( _collateralTokenAddress == address(collateralToken), @@ -595,7 +598,7 @@ contract LenderCommitmentGroup_Smart is function prepareSharesForBurn( uint256 _amountPoolSharesTokens - ) external whenForwarderNotPaused whenNotPaused nonReentrant + ) external whenForwarderNotPaused whenPoolNotPaused nonReentrant returns (bool) { return poolSharesToken.prepareSharesForBurn(msg.sender, _amountPoolSharesTokens); @@ -616,7 +619,7 @@ contract LenderCommitmentGroup_Smart is uint256 _amountPoolSharesTokens, address _recipient, uint256 _minAmountOut - ) external whenForwarderNotPaused whenNotPaused nonReentrant onlyOracleApprovedAllowEOA + ) external whenForwarderNotPaused whenPoolNotPaused nonReentrant onlyOracleApprovedAllowEOA returns (uint256) { @@ -658,7 +661,7 @@ contract LenderCommitmentGroup_Smart is function liquidateDefaultedLoanWithIncentive( uint256 _bidId, int256 _tokenAmountDifference - ) external whenForwarderNotPaused whenNotPaused bidIsActiveForGroup(_bidId) nonReentrant onlyOracleApprovedAllowEOA { + ) external whenForwarderNotPaused whenPoolNotPaused bidIsActiveForGroup(_bidId) nonReentrant onlyOracleApprovedAllowEOA { @@ -1002,7 +1005,7 @@ contract LenderCommitmentGroup_Smart is @dev there is no need to increment totalPrincipalTokensRepaid here as that is accomplished by the repayLoanCallback */ - function withdrawFromEscrowVault ( uint256 _amount ) external whenForwarderNotPaused whenNotPaused { + function withdrawFromEscrowVault ( uint256 _amount ) external whenForwarderNotPaused whenPoolNotPaused { address _escrowVault = ITellerV2(TELLER_V2).getEscrowVault(); @@ -1098,15 +1101,15 @@ contract LenderCommitmentGroup_Smart is /** * @notice Lets the DAO/owner of the protocol implement an emergency stop mechanism. */ - function pauseLendingPool() public virtual onlyProtocolPauser whenNotPaused { - _pause(); + function pauseLendingPool() public virtual onlyProtocolPauser whenPoolNotPaused { + _pausePool(); } /** * @notice Lets the DAO/owner of the protocol undo a previously implemented emergency stop. */ - function unpauseLendingPool() public virtual onlyProtocolPauser whenPaused { + function unpauseLendingPool() public virtual onlyProtocolPauser whenPoolPaused { setLastUnpausedAt(); - _unpause(); + _unpausePool(); } } diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol new file mode 100644 index 000000000..56f1ba06f --- /dev/null +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract LenderPoolPauseableUpgradeable is Initializable, ContextUpgradeable { + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + + /* + In Solidity, bool values consume 1 byte in storage, even though they technically only need a single bit. + Solidity packs multiple bool values into a single 32-byte (256-bit) storage slot whenever possible. + */ + + bool private _poolPaused; + + bool private _borrowingPaused; + + bool private _stakingPaused; + + /** + * @dev Initializes the contract in unpaused state. + */ + function __Pausable_init() internal onlyInitializing { + __Pausable_init_unchained(); + } + + function __Pausable_init_unchained() internal onlyInitializing { + _poolPaused = false; + _borrowingPaused = false; + _stakingPaused = false; + } + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenPoolNotPaused() { + _requirePoolNotPaused(); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPoolPaused() { + _requirePoolPaused(); + _; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function poolPaused() public view virtual returns (bool) { + return _poolPaused; + } + + /** + * @dev Throws if the contract is paused. + */ + function _requirePoolNotPaused() internal view virtual { + require(!poolPaused(), "Pausable: paused"); + } + + /** + * @dev Throws if the contract is not paused. + */ + function _requirePoolPaused() internal view virtual { + require(poolPaused(), "Pausable: not paused"); + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pausePool() internal virtual whenPoolNotPaused { + _poolPaused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpausePool() internal virtual whenPoolPaused { + _poolPaused = false; + emit Unpaused(_msgSender()); + } + + /** + * @dev This empty reserved space is put in place to allow future versions to add new + * variables without shifting down storage in the inheritance chain. + * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps + */ + uint256[49] private __gap; +} From 1ba3f97c1ab07a2aa10637d9fd59a78e0346d591 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 29 Jan 2025 12:42:44 -0500 Subject: [PATCH 2/4] add better pausing controls --- .../LenderCommitmentGroup_Smart.sol | 54 +++++- .../LenderPoolPauseableUpgradeable.sol | 177 +++++++++++++----- 2 files changed, 182 insertions(+), 49 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 0a6454173..95860dc04 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -459,7 +459,7 @@ contract LenderCommitmentGroup_Smart is uint256 _amount, address _sharesRecipient, uint256 _minSharesAmountOut - ) external whenForwarderNotPaused whenPoolNotPaused nonReentrant onlyOracleApprovedAllowEOA + ) external whenForwarderNotPaused whenPoolNotPaused whenStakingNotPaused nonReentrant onlyOracleApprovedAllowEOA returns (uint256 sharesAmount_) { uint256 principalTokenBalanceBefore = principalToken.balanceOf(address(this)); @@ -529,7 +529,7 @@ contract LenderCommitmentGroup_Smart is uint256 _collateralTokenId, uint32 _loanDuration, uint16 _interestRate - ) external onlySmartCommitmentForwarder whenForwarderNotPaused whenPoolNotPaused { + ) external onlySmartCommitmentForwarder whenForwarderNotPaused whenPoolNotPaused whenBorrowingNotPaused { require( _collateralTokenAddress == address(collateralToken), @@ -619,7 +619,7 @@ contract LenderCommitmentGroup_Smart is uint256 _amountPoolSharesTokens, address _recipient, uint256 _minAmountOut - ) external whenForwarderNotPaused whenPoolNotPaused nonReentrant onlyOracleApprovedAllowEOA + ) external whenForwarderNotPaused whenPoolNotPaused whenStakingNotPaused nonReentrant onlyOracleApprovedAllowEOA returns (uint256) { @@ -661,7 +661,7 @@ contract LenderCommitmentGroup_Smart is function liquidateDefaultedLoanWithIncentive( uint256 _bidId, int256 _tokenAmountDifference - ) external whenForwarderNotPaused whenPoolNotPaused bidIsActiveForGroup(_bidId) nonReentrant onlyOracleApprovedAllowEOA { + ) external whenForwarderNotPaused whenPoolNotPaused whenLiquidationNotPaused bidIsActiveForGroup(_bidId) nonReentrant onlyOracleApprovedAllowEOA { @@ -1112,4 +1112,50 @@ contract LenderCommitmentGroup_Smart is setLastUnpausedAt(); _unpausePool(); } + + + /** + * @notice Lets the DAO/owner of the protocol implement an emergency stop mechanism. + */ + function pauseBorrowing() public virtual onlyProtocolPauser whenBorrowingNotPaused { + _pauseBorrowing(); + } + + /** + * @notice Lets the DAO/owner of the protocol undo a previously implemented emergency stop. + */ + function unpauseBorrowing() public virtual onlyProtocolPauser whenBorrowingPaused { + _unpauseBorrowing(); + } + + + /** + * @notice Lets the DAO/owner of the protocol implement an emergency stop mechanism. + */ + function pauseStaking() public virtual onlyProtocolPauser whenStakingNotPaused { + _pauseStaking(); + } + + /** + * @notice Lets the DAO/owner of the protocol undo a previously implemented emergency stop. + */ + function unpauseStaking() public virtual onlyProtocolPauser whenStakingPaused { + _unpauseStaking(); + } + + + /** + * @notice Lets the DAO/owner of the protocol implement an emergency stop mechanism. + */ + function pauseLiquidation() public virtual onlyProtocolPauser whenPoolNotPaused { + _pauseLiquidation(); + } + + /** + * @notice Lets the DAO/owner of the protocol undo a previously implemented emergency stop. + */ + function unpauseLiquidation() public virtual onlyProtocolPauser whenPoolPaused { + setLastUnpausedAt(); + _unpauseLiquidation(); + } } diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol index 56f1ba06f..9ea21ff0b 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol @@ -17,14 +17,28 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; */ abstract contract LenderPoolPauseableUpgradeable is Initializable, ContextUpgradeable { /** - * @dev Emitted when the pause is triggered by `account`. + * @dev Emitted when the pool pause is triggered by `account`. */ - event Paused(address account); + event PoolPaused(address account); + event PoolUnpaused(address account); /** - * @dev Emitted when the pause is lifted by `account`. + * @dev Emitted when borrowing is paused/unpaused. */ - event Unpaused(address account); + event BorrowingPaused(address account); + event BorrowingUnpaused(address account); + + /** + * @dev Emitted when staking is paused/unpaused. + */ + event StakingPaused(address account); + event StakingUnpaused(address account); + + /** + * @dev Emitted when liquidation is paused/unpaused. + */ + event LiquidationPaused(address account); + event LiquidationUnpaused(address account); /* @@ -38,6 +52,8 @@ abstract contract LenderPoolPauseableUpgradeable is Initializable, ContextUpgrad bool private _stakingPaused; + bool private _liquidationPaused; + /** * @dev Initializes the contract in unpaused state. */ @@ -51,75 +67,146 @@ abstract contract LenderPoolPauseableUpgradeable is Initializable, ContextUpgrad _stakingPaused = false; } - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ + + + // ========================== POOL PAUSE ========================== + modifier whenPoolNotPaused() { _requirePoolNotPaused(); _; } - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ modifier whenPoolPaused() { _requirePoolPaused(); _; } - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ function poolPaused() public view virtual returns (bool) { return _poolPaused; } - /** - * @dev Throws if the contract is paused. - */ function _requirePoolNotPaused() internal view virtual { - require(!poolPaused(), "Pausable: paused"); + require(!_poolPaused, "Pausable: Pool is paused"); } - /** - * @dev Throws if the contract is not paused. - */ function _requirePoolPaused() internal view virtual { - require(poolPaused(), "Pausable: not paused"); + require(_poolPaused, "Pausable: Pool is not paused"); } - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ function _pausePool() internal virtual whenPoolNotPaused { _poolPaused = true; - emit Paused(_msgSender()); + emit PoolPaused(_msgSender()); } - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ function _unpausePool() internal virtual whenPoolPaused { _poolPaused = false; - emit Unpaused(_msgSender()); + emit PoolUnpaused(_msgSender()); + } + + // ========================== BORROWING PAUSE ========================== + + modifier whenBorrowingNotPaused() { + _requireBorrowingNotPaused(); + _; + } + + modifier whenBorrowingPaused() { + _requireBorrowingPaused(); + _; + } + + function borrowingPaused() public view virtual returns (bool) { + return _borrowingPaused; + } + + function _requireBorrowingNotPaused() internal view virtual { + require(!_borrowingPaused, "Pausable: Borrowing is paused"); + } + + function _requireBorrowingPaused() internal view virtual { + require(_borrowingPaused, "Pausable: Borrowing is not paused"); + } + + function _pauseBorrowing() internal virtual whenBorrowingNotPaused { + _borrowingPaused = true; + emit BorrowingPaused(_msgSender()); + } + + function _unpauseBorrowing() internal virtual whenBorrowingPaused { + _borrowingPaused = false; + emit BorrowingUnpaused(_msgSender()); + } + + // ========================== STAKING PAUSE ========================== + + modifier whenStakingNotPaused() { + _requireStakingNotPaused(); + _; + } + + modifier whenStakingPaused() { + _requireStakingPaused(); + _; + } + + function stakingPaused() public view virtual returns (bool) { + return _stakingPaused; + } + + function _requireStakingNotPaused() internal view virtual { + require(!_stakingPaused, "Pausable: Staking is paused"); + } + + function _requireStakingPaused() internal view virtual { + require(_stakingPaused, "Pausable: Staking is not paused"); + } + + function _pauseStaking() internal virtual whenStakingNotPaused { + _stakingPaused = true; + emit StakingPaused(_msgSender()); + } + + function _unpauseStaking() internal virtual whenStakingPaused { + _stakingPaused = false; + emit StakingUnpaused(_msgSender()); + } + + // ========================== LIQUIDATION PAUSE ========================== + + modifier whenLiquidationNotPaused() { + _requireLiquidationNotPaused(); + _; + } + + modifier whenLiquidationPaused() { + _requireLiquidationPaused(); + _; + } + + function liquidationPaused() public view virtual returns (bool) { + return _liquidationPaused; } + function _requireLiquidationNotPaused() internal view virtual { + require(!_liquidationPaused, "Pausable: Liquidation is paused"); + } + + function _requireLiquidationPaused() internal view virtual { + require(_liquidationPaused, "Pausable: Liquidation is not paused"); + } + + function _pauseLiquidation() internal virtual whenLiquidationNotPaused { + _liquidationPaused = true; + emit LiquidationPaused(_msgSender()); + } + + function _unpauseLiquidation() internal virtual whenLiquidationPaused { + _liquidationPaused = false; + emit LiquidationUnpaused(_msgSender()); + } + + + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. From 5a7c21e10ced6f5c3c6baada3cdd69f83098da83 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 29 Jan 2025 12:45:18 -0500 Subject: [PATCH 3/4] improve initializer --- .../LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol index 9ea21ff0b..b618d08f6 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderPoolPauseableUpgradeable.sol @@ -65,6 +65,7 @@ abstract contract LenderPoolPauseableUpgradeable is Initializable, ContextUpgrad _poolPaused = false; _borrowingPaused = false; _stakingPaused = false; + _liquidationPaused = false; } @@ -206,7 +207,7 @@ abstract contract LenderPoolPauseableUpgradeable is Initializable, ContextUpgrad } - + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. From 3f795cba62c1af113f2fa44f1e372d3bc5a1346c Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 29 Jan 2025 12:46:41 -0500 Subject: [PATCH 4/4] fix modifiers --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 95860dc04..5e85516c9 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -1147,14 +1147,14 @@ contract LenderCommitmentGroup_Smart is /** * @notice Lets the DAO/owner of the protocol implement an emergency stop mechanism. */ - function pauseLiquidation() public virtual onlyProtocolPauser whenPoolNotPaused { + function pauseLiquidation() public virtual onlyProtocolPauser whenLiquidationNotPaused { _pauseLiquidation(); } /** * @notice Lets the DAO/owner of the protocol undo a previously implemented emergency stop. */ - function unpauseLiquidation() public virtual onlyProtocolPauser whenPoolPaused { + function unpauseLiquidation() public virtual onlyProtocolPauser whenLiquidationPaused { setLastUnpausedAt(); _unpauseLiquidation(); }