diff --git a/src/TwabController.sol b/src/TwabController.sol index 696d19f..5db48b7 100644 --- a/src/TwabController.sol +++ b/src/TwabController.sol @@ -21,6 +21,8 @@ error PeriodOffsetInFuture(uint32 periodOffset); // The minimum period length uint32 constant MINIMUM_PERIOD_LENGTH = 1 hours; +address constant SPONSORSHIP_ADDRESS = address(1); + /** * @title Time-Weighted Average Balance Controller * @author PoolTogether Inc. @@ -37,7 +39,6 @@ contract TwabController { using SafeCast for uint256; /// @notice Allows users to revoke their chances to win by delegating to the sponsorship address. - address public constant SPONSORSHIP_ADDRESS = address(1); /// @notice Sets the minimum period length for Observations. When a period elapses, a new Observation is recorded, otherwise the most recent Observation is updated. uint32 public immutable PERIOD_LENGTH; @@ -679,7 +680,8 @@ contract TwabController { ( ObservationLib.Observation memory _observation, bool _isNewObservation, - bool _isObservationRecorded + bool _isObservationRecorded, + TwabLib.AccountDetails memory accountDetails ) = TwabLib.increaseBalances(PERIOD_LENGTH, PERIOD_OFFSET, _account, _amount, _delegateAmount); // Always emit the balance change event @@ -692,8 +694,8 @@ contract TwabController { emit ObservationRecorded( _vault, _user, - _account.details.balance, - _account.details.delegateBalance, + accountDetails.balance, + accountDetails.delegateBalance, _isNewObservation, _observation ); @@ -717,7 +719,8 @@ contract TwabController { ( ObservationLib.Observation memory _observation, bool _isNewObservation, - bool _isObservationRecorded + bool _isObservationRecorded, + TwabLib.AccountDetails memory accountDetails ) = TwabLib.decreaseBalances( PERIOD_LENGTH, PERIOD_OFFSET, @@ -737,8 +740,8 @@ contract TwabController { emit ObservationRecorded( _vault, _user, - _account.details.balance, - _account.details.delegateBalance, + accountDetails.balance, + accountDetails.delegateBalance, _isNewObservation, _observation ); @@ -761,7 +764,8 @@ contract TwabController { ( ObservationLib.Observation memory _observation, bool _isNewObservation, - bool _isObservationRecorded + bool _isObservationRecorded, + TwabLib.AccountDetails memory accountDetails ) = TwabLib.decreaseBalances( PERIOD_LENGTH, PERIOD_OFFSET, @@ -780,8 +784,8 @@ contract TwabController { if (_isObservationRecorded) { emit TotalSupplyObservationRecorded( _vault, - _account.details.balance, - _account.details.delegateBalance, + accountDetails.balance, + accountDetails.delegateBalance, _isNewObservation, _observation ); @@ -804,7 +808,8 @@ contract TwabController { ( ObservationLib.Observation memory _observation, bool _isNewObservation, - bool _isObservationRecorded + bool _isObservationRecorded, + TwabLib.AccountDetails memory accountDetails ) = TwabLib.increaseBalances(PERIOD_LENGTH, PERIOD_OFFSET, _account, _amount, _delegateAmount); // Always emit the balance change event @@ -816,8 +821,8 @@ contract TwabController { if (_isObservationRecorded) { emit TotalSupplyObservationRecorded( _vault, - _account.details.balance, - _account.details.delegateBalance, + accountDetails.balance, + accountDetails.delegateBalance, _isNewObservation, _observation ); diff --git a/src/libraries/TwabLib.sol b/src/libraries/TwabLib.sol index 7891d69..5526e35 100644 --- a/src/libraries/TwabLib.sol +++ b/src/libraries/TwabLib.sol @@ -98,9 +98,9 @@ library TwabLib { uint96 _delegateAmount ) internal - returns (ObservationLib.Observation memory observation, bool isNew, bool isObservationRecorded) + returns (ObservationLib.Observation memory observation, bool isNew, bool isObservationRecorded, AccountDetails memory accountDetails) { - AccountDetails memory accountDetails = _account.details; + accountDetails = _account.details; isObservationRecorded = _delegateAmount != uint96(0); accountDetails.balance += _amount; @@ -136,9 +136,9 @@ library TwabLib { string memory _revertMessage ) internal - returns (ObservationLib.Observation memory observation, bool isNew, bool isObservationRecorded) + returns (ObservationLib.Observation memory observation, bool isNew, bool isObservationRecorded, AccountDetails memory accountDetails) { - AccountDetails memory accountDetails = _account.details; + accountDetails = _account.details; if (accountDetails.balance < _amount) { revert BalanceLTAmount(accountDetails.balance, _amount, _revertMessage); @@ -549,15 +549,14 @@ library TwabLib { AccountDetails memory _accountDetails, uint32 _targetTime ) private view returns (ObservationLib.Observation memory prevOrAtObservation) { - uint32 currentTime = uint32(block.timestamp); - - uint16 oldestTwabIndex; - // If there are no observations, return a zeroed observation if (_accountDetails.cardinality == 0) { return ObservationLib.Observation({ cumulativeBalance: 0, balance: 0, timestamp: PERIOD_OFFSET }); } + + uint32 currentTime = uint32(block.timestamp); + uint16 oldestTwabIndex; (oldestTwabIndex, prevOrAtObservation) = getOldestObservation(_observations, _accountDetails); diff --git a/test/TwabController.t.sol b/test/TwabController.t.sol index 170e024..f170eeb 100644 --- a/test/TwabController.t.sol +++ b/test/TwabController.t.sol @@ -10,6 +10,7 @@ import { CannotTransferToSponsorshipAddress, MINIMUM_PERIOD_LENGTH, PeriodLengthTooShort, + SPONSORSHIP_ADDRESS, PeriodOffsetInFuture } from "../src/TwabController.sol"; import { @@ -402,7 +403,7 @@ contract TwabControllerTest is BaseTest { assertEq(twabController.balanceOf(mockVault, alice), _amount); assertEq(twabController.delegateBalanceOf(mockVault, alice), 0); - address _sponsorshipAddress = twabController.SPONSORSHIP_ADDRESS(); + address _sponsorshipAddress = SPONSORSHIP_ADDRESS; assertEq(twabController.balanceOf(mockVault, _sponsorshipAddress), 0); assertEq(twabController.delegateBalanceOf(mockVault, _sponsorshipAddress), 0); @@ -438,7 +439,7 @@ contract TwabControllerTest is BaseTest { assertEq(twabController.balanceOf(mockVault, bob), _amount); assertEq(twabController.delegateBalanceOf(mockVault, alice), _amount); assertEq(twabController.delegateBalanceOf(mockVault, bob), _amount); - assertEq(twabController.delegateBalanceOf(mockVault, twabController.SPONSORSHIP_ADDRESS()), 0); + assertEq(twabController.delegateBalanceOf(mockVault, SPONSORSHIP_ADDRESS), 0); twabController.sponsor(bob); vm.stopPrank(); @@ -451,7 +452,7 @@ contract TwabControllerTest is BaseTest { // Delegate balances have changed assertEq(twabController.delegateBalanceOf(mockVault, alice), 0); assertEq(twabController.delegateBalanceOf(mockVault, bob), _amount); - assertEq(twabController.delegateBalanceOf(mockVault, twabController.SPONSORSHIP_ADDRESS()), 0); + assertEq(twabController.delegateBalanceOf(mockVault, SPONSORSHIP_ADDRESS), 0); } function testMint() external { @@ -657,7 +658,7 @@ contract TwabControllerTest is BaseTest { function testTransfer_rejectSponsorshipAddress() public { twabController.mint(alice, 100e18); - address sponsorship = twabController.SPONSORSHIP_ADDRESS(); + address sponsorship = SPONSORSHIP_ADDRESS; vm.expectRevert(abi.encodeWithSelector(CannotTransferToSponsorshipAddress.selector)); twabController.transfer(alice, sponsorship, 100e18); } @@ -735,7 +736,7 @@ contract TwabControllerTest is BaseTest { } function testDelegateToSponsorship() external { - address _sponsorshipAddress = twabController.SPONSORSHIP_ADDRESS(); + address _sponsorshipAddress = SPONSORSHIP_ADDRESS; assertEq(twabController.delegateOf(mockVault, alice), alice); diff --git a/test/TwabLib.t.sol b/test/TwabLib.t.sol index fe74050..623dc8f 100644 --- a/test/TwabLib.t.sol +++ b/test/TwabLib.t.sol @@ -48,11 +48,9 @@ contract TwabLibTest is BaseTest { uint32 _currentTimestamp = PERIOD_OFFSET + uint32(DRAW_LENGTH * 2); vm.warp(_currentTimestamp); - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(_amount, 0); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, _amount); assertEq(accountDetails.delegateBalance, 0); assertEq(accountDetails.nextObservationIndex, 0); @@ -75,11 +73,9 @@ contract TwabLibTest is BaseTest { uint32 _currentTimestamp = PERIOD_OFFSET + uint32(DRAW_LENGTH * 2); vm.warp(_currentTimestamp); - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(0, _amount); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, _amount); assertEq(accountDetails.nextObservationIndex, 1); @@ -104,11 +100,9 @@ contract TwabLibTest is BaseTest { // Increase delegateBalance twice twabLibMock.increaseBalances(0, _amount); - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(0, _amount); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, _totalAmount); assertEq(accountDetails.nextObservationIndex, 1); @@ -130,11 +124,9 @@ contract TwabLibTest is BaseTest { uint32 _secondTimestamp = PERIOD_OFFSET + uint32(DRAW_LENGTH * 2); vm.warp(_initialTimestamp); - (ObservationLib.Observation memory _observation1, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation1, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(0, _amount); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, _amount); assertEq(accountDetails.nextObservationIndex, 1); @@ -149,9 +141,7 @@ contract TwabLibTest is BaseTest { vm.warp(_secondTimestamp); ObservationLib.Observation memory _observation2; - (_observation2, _isNew, _isRecorded) = twabLibMock.increaseBalances(0, _amount); - - accountDetails = twabLibMock.getAccountDetails(); + (_observation2, _isNew, _isRecorded, accountDetails) = twabLibMock.increaseBalances(0, _amount); // Check balance assertEq(accountDetails.balance, 0); @@ -178,17 +168,17 @@ contract TwabLibTest is BaseTest { function testDecreaseBalanceHappyPath() public { uint96 _amount = 1000e18; - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded,) = twabLibMock .increaseBalances(_amount, 0); + + TwabLib.AccountDetails memory accountDetails; - (_observation, _isNew, _isRecorded) = twabLibMock.decreaseBalances( + (_observation, _isNew, _isRecorded, accountDetails) = twabLibMock.decreaseBalances( _amount, 0, "Revert message" ); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, 0); assertEq(accountDetails.nextObservationIndex, 0); @@ -202,13 +192,9 @@ contract TwabLibTest is BaseTest { uint32 _secondTimestamp = PERIOD_OFFSET + uint32(DRAW_LENGTH * 2); vm.warp(_initialTimestamp); - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(0, _amount); - TwabLib.AccountDetails memory accountDetails; - - // accountDetails = twabLibMock.getAccountDetails(); - // assertEq(accountDetails.balance, 0); // assertEq(accountDetails.delegateBalance, _amount); // assertEq(accountDetails.nextObservationIndex, 1); @@ -218,14 +204,12 @@ contract TwabLibTest is BaseTest { // assertTrue(_isNew); vm.warp(_secondTimestamp); - (_observation, _isNew, _isRecorded) = twabLibMock.decreaseBalances( + (_observation, _isNew, _isRecorded, accountDetails) = twabLibMock.decreaseBalances( 0, _amount, "Revert message" ); - accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0, "balance is zero"); assertEq(accountDetails.delegateBalance, 0, "delegate balance is zero"); assertEq(accountDetails.nextObservationIndex, 2, "observations exist"); @@ -248,11 +232,9 @@ contract TwabLibTest is BaseTest { uint32 _secondTimestamp = PERIOD_OFFSET + uint32(DRAW_LENGTH * 2); vm.warp(_initialTimestamp); - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(0, _amount); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, _amount); assertEq(accountDetails.nextObservationIndex, 1); @@ -267,7 +249,7 @@ contract TwabLibTest is BaseTest { vm.expectRevert( abi.encodeWithSelector(BalanceLTAmount.selector, 0, _amount + 1, "Revert message") ); - (_observation, _isNew, _isRecorded) = twabLibMock.decreaseBalances( + (_observation, _isNew, _isRecorded, accountDetails) = twabLibMock.decreaseBalances( _amount + 1, 0, "Revert message" @@ -282,7 +264,7 @@ contract TwabLibTest is BaseTest { "Revert message" ) ); - (_observation, _isNew, _isRecorded) = twabLibMock.decreaseBalances( + (_observation, _isNew, _isRecorded, accountDetails) = twabLibMock.decreaseBalances( 0, _amount + 1, "Revert message" @@ -298,11 +280,9 @@ contract TwabLibTest is BaseTest { uint32 _thirdTimestamp = PERIOD_OFFSET + uint32(DRAW_LENGTH * 3); vm.warp(_initialTimestamp); - (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded) = twabLibMock + (ObservationLib.Observation memory _observation, bool _isNew, bool _isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock .increaseBalances(0, _amount); - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, _amount); assertEq(accountDetails.nextObservationIndex, 1); @@ -313,14 +293,12 @@ contract TwabLibTest is BaseTest { vm.warp(_secondTimestamp); ObservationLib.Observation memory _observation2; - (_observation2, _isNew, _isRecorded) = twabLibMock.decreaseBalances( + (_observation2, _isNew, _isRecorded, accountDetails) = twabLibMock.decreaseBalances( 0, _halfAmount, "Revert message" ); - accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, _halfAmount); assertEq(accountDetails.nextObservationIndex, 2); @@ -330,14 +308,12 @@ contract TwabLibTest is BaseTest { assertTrue(_isNew); vm.warp(_thirdTimestamp); - (_observation, _isNew, _isRecorded) = twabLibMock.decreaseBalances( + (_observation, _isNew, _isRecorded, accountDetails) = twabLibMock.decreaseBalances( 0, _halfAmount, "Revert message" ); - accountDetails = twabLibMock.getAccountDetails(); - assertEq(accountDetails.balance, 0); assertEq(accountDetails.delegateBalance, 0); assertEq(accountDetails.nextObservationIndex, 3); @@ -746,10 +722,10 @@ contract TwabLibTest is BaseTest { TwabLib.Account memory account = twabLibMock.getAccount(); assertEq(account.details.cardinality, 0); - vm.warp(PERIOD_OFFSET + 1 seconds); - (, bool isNew, bool isRecorded) = twabLibMock.increaseBalances(0, _amount); + - TwabLib.AccountDetails memory accountDetails = twabLibMock.getAccountDetails(); + vm.warp(PERIOD_OFFSET + 1 seconds); + (, bool isNew, bool isRecorded, TwabLib.AccountDetails memory accountDetails) = twabLibMock.increaseBalances(0, _amount); // First observation creates new record assertTrue(isNew); @@ -761,8 +737,7 @@ contract TwabLibTest is BaseTest { // Second observation overwrites previous record vm.warp(PERIOD_OFFSET + (DRAW_LENGTH / 2)); - (, isNew, isRecorded) = twabLibMock.increaseBalances(0, _amount); - accountDetails = twabLibMock.getAccountDetails(); + (, isNew, isRecorded, accountDetails) = twabLibMock.increaseBalances(0, _amount); assertFalse(isNew); assertTrue(isRecorded); assertEq(accountDetails.nextObservationIndex, 1); diff --git a/test/mocks/TwabLibMock.sol b/test/mocks/TwabLibMock.sol index a757f2e..01a0429 100644 --- a/test/mocks/TwabLibMock.sol +++ b/test/mocks/TwabLibMock.sol @@ -15,25 +15,27 @@ contract TwabLibMock { function increaseBalances( uint96 _amount, uint96 _delegateAmount - ) external returns (ObservationLib.Observation memory, bool, bool) { + ) external returns (ObservationLib.Observation memory, bool, bool, TwabLib.AccountDetails memory) { ( ObservationLib.Observation memory observation, bool isNewObservation, - bool isObservationRecorded + bool isObservationRecorded, + TwabLib.AccountDetails memory accountDetails ) = TwabLib.increaseBalances(PERIOD_LENGTH, PERIOD_OFFSET, account, _amount, _delegateAmount); - return (observation, isNewObservation, isObservationRecorded); + return (observation, isNewObservation, isObservationRecorded, accountDetails); } function decreaseBalances( uint96 _amount, uint96 _delegateAmount, string memory _revertMessage - ) external returns (ObservationLib.Observation memory, bool, bool) { + ) external returns (ObservationLib.Observation memory, bool, bool, TwabLib.AccountDetails memory) { ( ObservationLib.Observation memory observation, bool isNewObservation, - bool isObservationRecorded + bool isObservationRecorded, + TwabLib.AccountDetails memory accountDetails ) = TwabLib.decreaseBalances( PERIOD_LENGTH, PERIOD_OFFSET, @@ -43,7 +45,7 @@ contract TwabLibMock { _revertMessage ); - return (observation, isNewObservation, isObservationRecorded); + return (observation, isNewObservation, isObservationRecorded, accountDetails); } function getTwabBetween(uint32 _startTime, uint32 _endTime) external view returns (uint256) { @@ -133,10 +135,10 @@ contract TwabLibMock { } function currentOverwritePeriodStartedAt( - uint32 PERIOD_LENGTH, - uint32 PERIOD_OFFSET + uint32 _PERIOD_LENGTH, + uint32 _PERIOD_OFFSET ) external view returns (uint32) { - uint32 start = TwabLib.currentOverwritePeriodStartedAt(PERIOD_LENGTH, PERIOD_OFFSET); + uint32 start = TwabLib.currentOverwritePeriodStartedAt(_PERIOD_LENGTH, _PERIOD_OFFSET); return start; }