Skip to content

Commit

Permalink
Fixed bug in last observation timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
asselstine committed Mar 21, 2024
1 parent 258ab89 commit 725cc15
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/TwabController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,15 @@ contract TwabController {
* @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);
return TwabLib.isShutdownAt(timestamp, PERIOD_LENGTH, 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);
return TwabLib.lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);
}

/**
Expand Down
18 changes: 12 additions & 6 deletions src/libraries/TwabLib.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/console2.sol";

import "ring-buffer-lib/RingBufferLib.sol";

import { ObservationLib, MAX_CARDINALITY } from "./ObservationLib.sol";
Expand Down Expand Up @@ -109,7 +111,7 @@ library TwabLib {
// record a new observation if the delegateAmount is non-zero and time has not overflowed.
isObservationRecorded =
_delegateAmount != uint96(0) &&
block.timestamp <= lastObservationAt(PERIOD_OFFSET);
block.timestamp <= lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);

accountDetails.balance += _amount;
accountDetails.delegateBalance += _delegateAmount;
Expand Down Expand Up @@ -172,7 +174,7 @@ library TwabLib {
// record a new observation if the delegateAmount is non-zero and time has not overflowed.
isObservationRecorded =
_delegateAmount != uint96(0) &&
block.timestamp <= lastObservationAt(PERIOD_OFFSET);
block.timestamp <= lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);

unchecked {
accountDetails.balance -= _amount;
Expand Down Expand Up @@ -250,8 +252,10 @@ library TwabLib {
if (_targetTime < PERIOD_OFFSET) {
return 0;
}
console2.log("lastObservationAt", lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET));
// if this is for an overflowed time period, return 0
if (isShutdownAt(_targetTime, PERIOD_OFFSET)) {
if (isShutdownAt(_targetTime, PERIOD_LENGTH, PERIOD_OFFSET)) {
console2.log("IS SHUTDOWN", _targetTime);
return 0;
}
ObservationLib.Observation memory prevOrAtObservation = _getPreviousOrAtObservation(
Expand All @@ -271,9 +275,10 @@ library TwabLib {
*/
function isShutdownAt(
uint256 timestamp,
uint32 PERIOD_LENGTH,
uint32 PERIOD_OFFSET
) internal pure returns (bool) {
return timestamp > lastObservationAt(PERIOD_OFFSET);
return timestamp > lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);
}

/**
Expand All @@ -282,9 +287,10 @@ library TwabLib {
* @return The largest timestamp at which the TwabController can record a new observation.
*/
function lastObservationAt(
uint32 PERIOD_LENGTH,
uint32 PERIOD_OFFSET
) internal pure returns (uint256) {
return uint256(PERIOD_OFFSET) + type(uint32).max;
return uint256(PERIOD_OFFSET) + (type(uint32).max / PERIOD_LENGTH) * PERIOD_LENGTH;
}

/**
Expand All @@ -311,7 +317,7 @@ library TwabLib {
}

// if the range extends into the shutdown period, return 0
if (isShutdownAt(_endTime, PERIOD_OFFSET)) {
if (isShutdownAt(_endTime, PERIOD_LENGTH, PERIOD_OFFSET)) {
return 0;
}

Expand Down
17 changes: 15 additions & 2 deletions test/TwabController.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,13 @@ contract TwabControllerTest is BaseTest {
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/PERIOD_LENGTH)*PERIOD_LENGTH + uint256(PERIOD_OFFSET)), false, "at end of last period");
assertEq(twabController.isShutdownAt(type(uint32).max + uint256(PERIOD_OFFSET)), true, "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);
assertEq(twabController.lastObservationAt(), uint256(PERIOD_OFFSET) + (type(uint32).max/PERIOD_LENGTH)*PERIOD_LENGTH);
}

function testGetBalanceAt_beforeHistoryStarted() public {
Expand Down Expand Up @@ -948,6 +949,18 @@ contract TwabControllerTest is BaseTest {
assertEq(twabController.delegateOf(mockVault, alice), bob);
}

function testMint_lastObservation() public {
vm.startPrank(mockVault);
uint96 _amount = 1000e18;
uint lastAt = twabController.lastObservationAt();
console2.log("LAST AT", lastAt);
vm.warp(lastAt);
twabController.mint(alice, _amount);
vm.warp(lastAt + PERIOD_LENGTH);
assertEq(twabController.getBalanceAt(mockVault, alice, lastAt), _amount);
vm.stopPrank();
}

function testMint_toZero() public {
vm.startPrank(mockVault);

Expand Down
6 changes: 3 additions & 3 deletions test/TwabLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract TwabLibTest is BaseTest {
/* ============ increaseBalances ============ */

function testIncreaseBalance_endOfTimerange() public {
uint256 timestamp = PERIOD_OFFSET + uint256(type(uint32).max);
uint256 timestamp = twabLibMock.lastObservationAt();
vm.warp(timestamp);
twabLibMock.increaseBalances(1000e18, 1000e18);
vm.warp(uint256(type(uint48).max));
Expand All @@ -45,7 +45,7 @@ contract TwabLibTest is BaseTest {
function testDecreaseBalance_endOfTimerange() public {
vm.warp(PERIOD_OFFSET);
twabLibMock.increaseBalances(1000e18, 1000e18);
uint256 timestamp = PERIOD_OFFSET + uint256(type(uint32).max);
uint256 timestamp = twabLibMock.lastObservationAt();
vm.warp(timestamp);
twabLibMock.decreaseBalances(100e18, 100e18, "revert message");
vm.warp(uint256(type(uint48).max));
Expand Down Expand Up @@ -734,7 +734,7 @@ contract TwabLibTest is BaseTest {
function testGetBalanceAt_endOfTimerange() public {
twabLibMock.increaseBalances(0, 1e18);
vm.warp(type(uint48).max);
assertEq(twabLibMock.getBalanceAt(PERIOD_OFFSET + uint256(type(uint32).max)), 1e18);
assertEq(twabLibMock.getBalanceAt(twabLibMock.lastObservationAt()), 1e18);
}

function testGetBalanceAt_outOfTimerange() public {
Expand Down
1 change: 0 additions & 1 deletion test/invariants/handlers/TwabControllerHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,6 @@ contract TwabControllerHandler is CommonBase, StdCheats, StdUtils {
function reduceFullRangeTwabs() external view returns (uint256, uint256) {
uint256 vaultAcc = 0;
uint256 actorAcc = 0;
ObservationLib.Observation memory newestActorObservation;
ObservationLib.Observation memory newestObservation;

// For Each Vault
Expand Down
5 changes: 5 additions & 0 deletions test/mocks/TwabLibMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,9 @@ contract TwabLibMock {
bool isSafe = TwabLib.hasFinalized(PERIOD_LENGTH, PERIOD_OFFSET, _timestamp);
return isSafe;
}

function lastObservationAt() external view returns (uint) {
uint result = TwabLib.lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);
return result;
}
}

0 comments on commit 725cc15

Please sign in to comment.