From bf1d22ae6bd2e3469cda73b5b709584e861f2249 Mon Sep 17 00:00:00 2001 From: Brendan Asselstine Date: Wed, 24 Jan 2024 14:59:03 -0800 Subject: [PATCH 1/2] Added public functions to check whether shutdown --- src/TwabController.sol | 19 +++++++++++++++++++ src/libraries/TwabLib.sol | 38 +++++++++++++++++++++++++++++++------- test/TwabController.t.sol | 11 +++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/TwabController.sol b/src/TwabController.sol index b8ae107..d6fc375 100644 --- a/src/TwabController.sol +++ b/src/TwabController.sol @@ -171,6 +171,25 @@ contract TwabController { /* ============ External Read Functions ============ */ + /** + * @notice Returns whether the TwabController has been shutdown at the given timestamp + * If the twab is queried at or after this time, whether an absolute timestamp or time range, it will return 0. + * @dev This function will return true for any timestamp after the lastObservationAt() + * @param timestamp The timestamp to check + * @return True if the TwabController is shutdown at the given timestamp, false otherwise. + */ + function isShutdownAt(uint256 timestamp) external view returns (bool) { + return TwabLib.isShutdownAt(timestamp, PERIOD_OFFSET); + } + + /** + * @notice Computes the timestamp after which no more observations will be made. + * @return The largest timestamp at which the TwabController can record a new observation. + */ + function lastObservationAt() external view returns (uint256) { + return TwabLib.lastObservationAt(PERIOD_OFFSET); + } + /** * @notice Loads the current TWAB Account data for a specific vault stored for a user. * @dev Note this is a very expensive function diff --git a/src/libraries/TwabLib.sol b/src/libraries/TwabLib.sol index a3351c7..f6730e8 100644 --- a/src/libraries/TwabLib.sol +++ b/src/libraries/TwabLib.sol @@ -109,7 +109,7 @@ library TwabLib { // record a new observation if the delegateAmount is non-zero and time has not overflowed. isObservationRecorded = _delegateAmount != uint96(0) && - (block.timestamp - PERIOD_OFFSET) <= type(uint32).max; + block.timestamp <= lastObservationAt(PERIOD_OFFSET); accountDetails.balance += _amount; accountDetails.delegateBalance += _delegateAmount; @@ -172,7 +172,7 @@ library TwabLib { // record a new observation if the delegateAmount is non-zero and time has not overflowed. isObservationRecorded = _delegateAmount != uint96(0) && - (block.timestamp - PERIOD_OFFSET) <= type(uint32).max; + block.timestamp <= lastObservationAt(PERIOD_OFFSET); unchecked { accountDetails.balance -= _amount; @@ -250,19 +250,43 @@ library TwabLib { if (_targetTime < PERIOD_OFFSET) { return 0; } - uint256 offsetTargetTime = _targetTime - PERIOD_OFFSET; // if this is for an overflowed time period, return 0 - if (offsetTargetTime > type(uint32).max) { + if (isShutdownAt(_targetTime, PERIOD_OFFSET)) { return 0; } ObservationLib.Observation memory prevOrAtObservation = _getPreviousOrAtObservation( _observations, _accountDetails, - PeriodOffsetRelativeTimestamp.wrap(uint32(offsetTargetTime)) + PeriodOffsetRelativeTimestamp.wrap(uint32(_targetTime - PERIOD_OFFSET)) ); return prevOrAtObservation.balance; } + /** + * @notice Returns whether the TwabController has been shutdown at the given timestamp + * If the twab is queried at or after this time, whether an absolute timestamp or time range, it will return 0. + * @param timestamp The timestamp to check + * @param PERIOD_OFFSET The offset of the first period + * @return True if the TwabController is shutdown at the given timestamp, false otherwise. + */ + function isShutdownAt( + uint256 timestamp, + uint32 PERIOD_OFFSET + ) internal pure returns (bool) { + return timestamp > lastObservationAt(PERIOD_OFFSET); + } + + /** + * @notice Computes the largest timestamp at which the TwabController can record a new observation. + * @param PERIOD_OFFSET The offset of the first period + * @return The largest timestamp at which the TwabController can record a new observation. + */ + function lastObservationAt( + uint32 PERIOD_OFFSET + ) internal pure returns (uint256) { + return uint256(PERIOD_OFFSET) + type(uint32).max; + } + /** * @notice Looks up a users TWAB for a time range. The time must be before the current overwrite period. * @dev If the timestamps in the range are not exact matches of observations, the balance is extrapolated using the previous observation. @@ -289,8 +313,8 @@ library TwabLib { uint256 offsetStartTime = _startTime - PERIOD_OFFSET; uint256 offsetEndTime = _endTime - PERIOD_OFFSET; - // if the either time has overflowed, then return 0. - if (offsetStartTime > type(uint32).max || offsetEndTime > type(uint32).max) { + // if the range extends into the shutdown period, return 0 + if (isShutdownAt(_endTime, PERIOD_OFFSET)) { return 0; } diff --git a/test/TwabController.t.sol b/test/TwabController.t.sol index f041c43..1ea19dd 100644 --- a/test/TwabController.t.sol +++ b/test/TwabController.t.sol @@ -145,6 +145,17 @@ contract TwabControllerTest is BaseTest { vm.stopPrank(); } + function testIsShutdownAt() public { + assertEq(twabController.isShutdownAt(PERIOD_OFFSET), false, "at beginning"); + assertEq(twabController.isShutdownAt(PERIOD_OFFSET + PERIOD_LENGTH), false, "after first period"); + assertEq(twabController.isShutdownAt(type(uint32).max + uint256(PERIOD_OFFSET)), false, "at end"); + assertEq(twabController.isShutdownAt(type(uint32).max + uint256(PERIOD_OFFSET) + 1), true, "after end"); + } + + function testLastObservationAt() public { + assertEq(twabController.lastObservationAt(), uint256(PERIOD_OFFSET) + type(uint32).max); + } + function testGetBalanceAt_beforeHistoryStarted() public { // ensure times are finalized vm.warp(PERIOD_OFFSET + PERIOD_LENGTH); From 8f024f3deb427ed5cd14bfb0191d78c397f50271 Mon Sep 17 00:00:00 2001 From: Brendan Asselstine Date: Tue, 30 Jan 2024 14:57:31 -0800 Subject: [PATCH 2/2] Moved escape condition before unrelated logic --- src/libraries/TwabLib.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/TwabLib.sol b/src/libraries/TwabLib.sol index f6730e8..2193c28 100644 --- a/src/libraries/TwabLib.sol +++ b/src/libraries/TwabLib.sol @@ -310,14 +310,14 @@ library TwabLib { revert InvalidTimeRange(_startTime, _endTime); } - uint256 offsetStartTime = _startTime - PERIOD_OFFSET; - uint256 offsetEndTime = _endTime - PERIOD_OFFSET; - // if the range extends into the shutdown period, return 0 if (isShutdownAt(_endTime, PERIOD_OFFSET)) { return 0; } + uint256 offsetStartTime = _startTime - PERIOD_OFFSET; + uint256 offsetEndTime = _endTime - PERIOD_OFFSET; + ObservationLib.Observation memory endObservation = _getPreviousOrAtObservation( _observations, _accountDetails,