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

Fixed bug in last observation timestamp #31

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/TwabController.sol
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
14 changes: 8 additions & 6 deletions src/libraries/TwabLib.sol
Expand Up @@ -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 <= lastObservationAt(PERIOD_OFFSET);
block.timestamp <= lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);

accountDetails.balance += _amount;
accountDetails.delegateBalance += _delegateAmount;
Expand Down Expand Up @@ -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 <= lastObservationAt(PERIOD_OFFSET);
block.timestamp <= lastObservationAt(PERIOD_LENGTH, PERIOD_OFFSET);

unchecked {
accountDetails.balance -= _amount;
Expand Down Expand Up @@ -251,7 +251,7 @@ library TwabLib {
return 0;
}
// if this is for an overflowed time period, return 0
if (isShutdownAt(_targetTime, PERIOD_OFFSET)) {
if (isShutdownAt(_targetTime, PERIOD_LENGTH, PERIOD_OFFSET)) {
return 0;
}
ObservationLib.Observation memory prevOrAtObservation = _getPreviousOrAtObservation(
Expand All @@ -271,9 +271,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 +283,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 +313,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
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
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
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
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;
}
}