diff --git a/TOTAL_SUPPLY.md b/TOTAL_SUPPLY.md index e9b8563..adad29c 100644 --- a/TOTAL_SUPPLY.md +++ b/TOTAL_SUPPLY.md @@ -101,8 +101,12 @@ TODOs: decide and review scheduled adjustments fix the bug with the warmup period retire the bias if not needed +consider compressing the storage slots do a full test of the curve in other logic stuff Create a test on the user point with some hard coded values Create an exploit where the user double counts by depositing AT the boundary Multiple same block updates and if that's possible Test boundary updates: - Scheduled updates are processed - what about individual updates +Add the total supply and the binary search +write a full document +test the revert conditions diff --git a/src/escrow/increasing/LinearIncreasingEscrow.sol b/src/escrow/increasing/LinearIncreasingEscrow.sol index 939d0ff..4fbe80b 100644 --- a/src/escrow/increasing/LinearIncreasingEscrow.sol +++ b/src/escrow/increasing/LinearIncreasingEscrow.sol @@ -465,6 +465,8 @@ contract LinearIncreasingEscrow is uint256 tokenInterval = tokenPointIntervals[_tokenId]; oldPoint = _tokenPointHistory[_tokenId][tokenInterval]; + console.log("opcts", oldPoint.checkpointTs, newPoint.checkpointTs); + // we can't write checkpoints out of order as it would interfere with searching if (oldPoint.checkpointTs > newPoint.checkpointTs) revert InvalidCheckpoint(); @@ -483,13 +485,12 @@ contract LinearIncreasingEscrow is // problem we have now is that the coefficient[0] is later being used // todo this is janky AF newPoint.coefficients[1] = coefficients[1]; + // this bias is stored having been converted from fixed point + // be mindful about converting back // newPoint.bias = _getBias(elapsed, coefficients); - // the bug here is that newPoint.coefficients[0] = elapsed == 0 ? coefficients[0] : _getBiasUnbound(elapsed, coefficients); - // this bias is stored having been converted from fixed point - // be mindful about converting back } // if we're writing to a new point, increment the interval diff --git a/test/escrow/curve/linear/LinearScheduleChanges.t.sol b/test/escrow/curve/linear/LinearScheduleChanges.t.sol index 6e62589..fd519c8 100644 --- a/test/escrow/curve/linear/LinearScheduleChanges.t.sol +++ b/test/escrow/curve/linear/LinearScheduleChanges.t.sol @@ -37,7 +37,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { TokenPoint memory oldPoint; LockedBalance memory oldLocked; - // bound coefficients + // bound coefficients - int128 to avoid overflow _newPoint.coefficients[0] = int(int128(_boundCoeff[0])); _newPoint.coefficients[1] = int(int128(_boundCoeff[1])); @@ -53,19 +53,19 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { // start should be the same as the new point assertEq( startChanges[0], - _newPoint.coefficients[0], + _newPoint.coefficients[0] / 1e18, "startChanges[0] != _newPoint.coefficients[0]" ); assertEq( startChanges[1], - _newPoint.coefficients[1], + _newPoint.coefficients[1] / 1e18, "startChanges[1] != _newPoint.coefficients[1]" ); // end should be the same as the new point slope but in the negative assertEq( endChanges[1], - -_newPoint.coefficients[1], + -_newPoint.coefficients[1] / 1e18, "endChanges[1] != -_newPoint.coefficients[1]" ); } @@ -75,18 +75,18 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { // write some existing state to some location uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startChanges = [int(1), int(2), int(0)]; + int256[3] memory startChanges = [int(1e18), int(2e18), int(0)]; curve.writeSchedule(start, [startChanges[0], startChanges[1], 0]); - curve.writeSchedule(end, [int(4), int(5), int(6)]); + curve.writeSchedule(end, [int(4e18), int(5e18), int(6e18)]); TokenPoint memory newPoint; TokenPoint memory oldPoint; LockedBalance memory newLocked = LockedBalance({start: start, amount: 100}); - newPoint.coefficients[0] = 1; - newPoint.coefficients[1] = 2; + newPoint.coefficients[0] = 1e18; + newPoint.coefficients[1] = 2e18; oldPoint.coefficients[0] = startChanges[0]; oldPoint.coefficients[1] = startChanges[1]; @@ -113,18 +113,18 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startChanges = [int(1), int(2), 0]; + int256[3] memory startChanges = [int(1e18), int(2e18), 0]; curve.writeSchedule(start, [startChanges[0], startChanges[1], 0]); - curve.writeSchedule(end, [int(4), int(5), 0]); + curve.writeSchedule(end, [int(4e18), int(5e18), 0]); TokenPoint memory newPoint; TokenPoint memory oldPoint; LockedBalance memory newLocked = LockedBalance({start: start, amount: 100}); - newPoint.coefficients[0] = 10; - newPoint.coefficients[1] = 20; + newPoint.coefficients[0] = 10e18; + newPoint.coefficients[1] = 20e18; oldPoint.coefficients[0] = startChanges[0]; oldPoint.coefficients[1] = startChanges[1]; @@ -183,8 +183,8 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { oldLocked.amount = newLocked.amount; // now setup the new new point - newPoint.coefficients[0] = 10; - newPoint.coefficients[1] = 20; + newPoint.coefficients[0] = 10e18; + newPoint.coefficients[1] = 20e18; // setup the new start newLocked = LockedBalance({start: start, amount: 2}); @@ -220,7 +220,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { function testRewrite_startSameBeforeStart() public { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startCoeff = [int(1), int(2), 0]; + int256[3] memory startCoeff = [int(1e18), int(2e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -243,7 +243,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { function testRewrite_startSameAtStart(bool _exact) public { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startCoeff = [int(1), int(2), 0]; + int256[3] memory startCoeff = [int(1e18), int(2e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -256,8 +256,16 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { curve.scheduleCurveChanges(oldPoint, newPoint, oldLocked, newLocked); // expectation, the window should have passed to schedule changes so instead we simply adjust the end - assertEq(curve.scheduledCurveChanges(start)[0], startCoeff[0], "start[0] != startCoeff[0]"); - assertEq(curve.scheduledCurveChanges(start)[1], startCoeff[1], "start[1] != startCoeff[1]"); + assertEq( + curve.scheduledCurveChanges(start)[0], + startCoeff[0] / 1e18, + "start[0] != startCoeff[0]" + ); + assertEq( + curve.scheduledCurveChanges(start)[1], + startCoeff[1] / 1e18, + "start[1] != startCoeff[1]" + ); // end should be the negative of the new point assertEq(curve.scheduledCurveChanges(end)[0], 0, "end[0] != 0"); @@ -267,7 +275,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { function testRewrite_startSameAtEnd(bool _exact) public { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startCoeff = [int(1), int(2), 0]; + int256[3] memory startCoeff = [int(1e18), int(2e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -280,18 +288,30 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { curve.scheduleCurveChanges(oldPoint, newPoint, oldLocked, newLocked); // expectation, window missed completely - assertEq(curve.scheduledCurveChanges(start)[0], startCoeff[0], "start[0] != startCoeff[0]"); - assertEq(curve.scheduledCurveChanges(start)[1], startCoeff[1], "start[1] != startCoeff[1]"); + assertEq( + curve.scheduledCurveChanges(start)[0], + startCoeff[0] / 1e18, + "start[0] != startCoeff[0]" + ); + assertEq( + curve.scheduledCurveChanges(start)[1], + startCoeff[1] / 1e18, + "start[1] != startCoeff[1]" + ); // end should be the negative of the new point assertEq(curve.scheduledCurveChanges(end)[0], 0, "end[0] != 0"); - assertEq(curve.scheduledCurveChanges(end)[1], -startCoeff[1], "end[1] != -startCoeff[1]"); + assertEq( + curve.scheduledCurveChanges(end)[1], + -startCoeff[1] / 1e18, + "end[1] != -startCoeff[1]" + ); } function testRewrite_diffStartBeforeStart() public { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startCoeff = [int(1), int(2), 0]; + int256[3] memory startCoeff = [int(1e18), int(2e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -322,7 +342,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { function testRewrite_diffStartAtFirstStart(uint32 _warp) public { uint48 start = 100; vm.assume(_warp >= start); - int256[3] memory startCoeff = [int(1), int(2), 0]; + int256[3] memory startCoeff = [int(1e18), int(2e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -342,7 +362,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { function testAdd_sameStartBeforeStart() public { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startCoeff = [int(1), int(2), 0]; + int256[3] memory startCoeff = [int(1e18), int(2e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -357,12 +377,12 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { // but the old point is still there assertEq( curve.scheduledCurveChanges(start)[0], - startCoeff[0] + newPoint.coefficients[0], + (startCoeff[0] + newPoint.coefficients[0]) / 1e18, "start[0] != 11" ); assertEq( curve.scheduledCurveChanges(start)[1], - startCoeff[1] + newPoint.coefficients[1], + (startCoeff[1] + newPoint.coefficients[1]) / 1e18, "start[1] != 22" ); @@ -370,7 +390,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { assertEq(curve.scheduledCurveChanges(end)[0], 0, "end[0] != 0"); assertEq( curve.scheduledCurveChanges(end)[1], - -startCoeff[1] - newPoint.coefficients[1], + (-startCoeff[1] - newPoint.coefficients[1]) / 1e18, "end[1] != -22" ); } @@ -393,12 +413,12 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { // expected: both old points still there. end date is replaced for 1/2 assertEq( curve.scheduledCurveChanges(start)[0], - startCoeff[0] + startCoeff[0], + (startCoeff[0] + startCoeff[0]) / 1e18, "start[0] != 2" ); assertEq( curve.scheduledCurveChanges(start)[1], - startCoeff[1] + startCoeff[1], + (startCoeff[1] + startCoeff[1]) / 1e18, "start[1] != 4" ); @@ -406,7 +426,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { assertEq(curve.scheduledCurveChanges(end)[0], 0, "end[0] != 0"); assertEq( curve.scheduledCurveChanges(end)[1], - -startCoeff[1] - newPoint.coefficients[1], + (-startCoeff[1] - newPoint.coefficients[1]) / 1e18, "end[1] != -22" ); } @@ -429,19 +449,19 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { // expected: nothing changes from the initial state assertEq( curve.scheduledCurveChanges(start)[0], - startCoeff[0] + startCoeff[0], + (startCoeff[0] + startCoeff[0]) / 1e18, "start[0] != 2" ); assertEq( curve.scheduledCurveChanges(start)[1], - startCoeff[1] + startCoeff[1], + (startCoeff[1] + startCoeff[1]) / 1e18, "start[1] != 4" ); assertEq(curve.scheduledCurveChanges(end)[0], 0, "end[0] != 0"); assertEq( curve.scheduledCurveChanges(end)[1], - -startCoeff[1] - startCoeff[1], + (-startCoeff[1] - startCoeff[1]) / 1e18, "end[1] != -4" ); } @@ -466,26 +486,26 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { // expectation: the old lock has been removed at the start and moved to the new start // but the old point is still there - assertEq(curve.scheduledCurveChanges(start)[0], startCoeff[0], "start[0] != 1"); - assertEq(curve.scheduledCurveChanges(start)[1], startCoeff[1], "start[1] != 2"); + assertEq(curve.scheduledCurveChanges(start)[0], startCoeff[0] / 1e18, "start[0] != 1"); + assertEq(curve.scheduledCurveChanges(start)[1], startCoeff[1] / 1e18, "start[1] != 2"); assertEq( curve.scheduledCurveChanges(newLocked.start)[0], - newPoint.coefficients[0], + newPoint.coefficients[0] / 1e18, "newLocked.start[0] != 10" ); assertEq( curve.scheduledCurveChanges(newLocked.start)[1], - newPoint.coefficients[1], + newPoint.coefficients[1] / 1e18, "newLocked.start[1] != 20" ); // the end should be the aggregate of the first swapped point and the old point assertEq(curve.scheduledCurveChanges(end)[0], 0, "end[0] != 0"); - assertEq(curve.scheduledCurveChanges(end)[1], -startCoeff[1], "end[1] != -2"); + assertEq(curve.scheduledCurveChanges(end)[1], -startCoeff[1] / 1e18, "end[1] != -2"); assertEq(curve.scheduledCurveChanges(end + 1)[0], 0, "end[0] != 0"); assertEq( curve.scheduledCurveChanges(end + 1)[1], - -newPoint.coefficients[1], + -newPoint.coefficients[1] / 1e18, "end[1] != -20" ); } @@ -495,7 +515,7 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { uint48 start = 100; uint48 end = start + curve.maxTime(); - int256[3] memory startCoeff = [int(10), int(20), 0]; + int256[3] memory startCoeff = [int(10e18), int(20e18), 0]; ( TokenPoint memory oldPoint, TokenPoint memory newPoint, @@ -504,8 +524,8 @@ contract TestLinearIncreasingScheduleChanges is LinearCurveBase { ) = _initState(start, startCoeff); // rewrite our new point to be 1, 2 - newPoint.coefficients[0] = 1; - newPoint.coefficients[1] = 2; + newPoint.coefficients[0] = 1e18; + newPoint.coefficients[1] = 2e18; // write the second schedule change curve.scheduleCurveChanges(oldPoint, newPoint, oldLocked, newLocked); diff --git a/test/escrow/curve/linear/LinearTokenCheckpoint.t.sol b/test/escrow/curve/linear/LinearTokenCheckpoint.t.sol index c7d0b00..9146fa3 100644 --- a/test/escrow/curve/linear/LinearTokenCheckpoint.t.sol +++ b/test/escrow/curve/linear/LinearTokenCheckpoint.t.sol @@ -8,19 +8,28 @@ import {IVotingEscrowIncreasing, ILockedBalanceIncreasing} from "src/escrow/incr import {LinearCurveBase} from "./LinearBase.sol"; contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { + /// @dev some asserts on the state of the passed fuzz variables function _setValidateState( uint256 _tokenId, LockedBalance memory _oldLocked, uint32 _warp ) internal { // Common fuzz assumptions + // start is not zero (this is a sentinel for no lock) + // tokenId = 0 is not a valid token vm.assume(_oldLocked.start > 0 && _tokenId > 0); + // deposits in the past are not allowed vm.assume(_oldLocked.start >= _warp); + // bound the start so it doesn't overflow vm.assume(_oldLocked.start < type(uint48).max); + // bound the amount so it doesn't overflow vm.assume(_oldLocked.amount <= 2 ** 127 - 1); + vm.assume(_oldLocked.amount > 0); + + // warp to whereever vm.warp(_warp); - // write the first point + // write the first point, will have id == 1 and a start of @ at least warp + 1 curve.tokenCheckpoint(_tokenId, LockedBalance(0, 0), _oldLocked); } @@ -42,7 +51,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { ); // the old point should be zero zero - assertEq(oldPoint.bias, 0); + // assertEq(oldPoint.bias, 0); assertEq(oldPoint.checkpointTs, 0); assertEq(oldPoint.writtenTs, 0); assertEq(oldPoint.coefficients[0], 0); @@ -51,7 +60,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { // new point should have the correct values int256[3] memory coefficients = curve.getCoefficients(_newLocked.amount); - assertEq(newPoint.bias, _newLocked.amount, "bias incorrect"); + // assertEq(newPoint.bias, _newLocked.amount, "bias incorrect"); assertEq(newPoint.checkpointTs, _newLocked.start, "checkpointTs incorrect"); assertEq(newPoint.writtenTs, _warp, "writtenTs incorrect"); assertEq(newPoint.coefficients[0] / 1e18, coefficients[0], "constant incorrect"); @@ -84,8 +93,12 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { // expect the point is overwritten but nothing actually changes assertEq(curve.tokenPointIntervals(_tokenId), 1, "C1: token interval incorrect"); - assertEq(newPoint.bias, _oldLocked.amount, "C1: bias incorrect"); - assertEq(newPoint.checkpointTs, _oldLocked.start, "C1: checkpointTs incorrect"); + // assertEq(newPoint.bias, _oldLocked.amount, "C1: bias incorrect"); + assertEq( + newPoint.checkpointTs, + _oldLocked.start, + "C1: checkpoindon't want to interrupt his vacation but if he needs us to step in tTs incorrect" + ); assertEq(newPoint.writtenTs, _warp, "C1: writtenTs incorrect"); assertEq(newPoint.coefficients[0], oldPoint.coefficients[0], "C1: constant incorrect"); assertEq(newPoint.coefficients[1], oldPoint.coefficients[1], "C1: linear incorrect"); @@ -109,7 +122,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { // expectation: point overwritten but new curve assertEq(curve.tokenPointIntervals(_tokenId), 1, "C2: token interval incorrect"); - assertEq(newPoint.bias, _newLocked.amount, "C2: bias incorrect"); + // assertEq(newPoint.bias, _newLocked.amount, "C2: bias incorrect"); assertEq(newPoint.checkpointTs, _oldLocked.start, "C2: checkpointTs incorrect"); assertEq(newPoint.writtenTs, _warp, "C2: writtenTs incorrect"); assertEq( @@ -136,6 +149,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { // Variables TokenPoint memory oldPoint; TokenPoint memory newPoint; + // write a an amount greater 1 second before the previous lock LockedBalance memory _newLocked = LockedBalance( _oldLocked.amount + 1, _oldLocked.start - 1 @@ -167,7 +181,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { // expectation: new point written assertEq(curve.tokenPointIntervals(_tokenId), 2, "C4: token interval incorrect"); - assertEq(newPoint.bias, _newLocked.amount, "C4: bias incorrect"); + // assertEq(newPoint.bias, _newLocked.amount, "C4: bias incorrect"); assertEq(newPoint.checkpointTs, _newLocked.start, "C4: checkpointTs incorrect"); assertEq(newPoint.writtenTs, _warp, "C4: writtenTs incorrect"); assertEq( @@ -254,7 +268,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { (oldPoint, newPoint) = curve.tokenCheckpoint(_tokenId, _oldLocked, _newLocked); assertEq(curve.tokenPointIntervals(_tokenId), 2, "C7: token interval incorrect"); - assertEq(newPoint.bias, _newLocked.amount, "C7: bias incorrect"); + // assertEq(newPoint.bias, _newLocked.amount, "C7: bias incorrect"); assertEq(newPoint.checkpointTs, _newLocked.start, "C7: checkpointTs incorrect"); assertEq(newPoint.writtenTs, _warp, "C7: writtenTs incorrect"); assertEq( @@ -309,7 +323,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { (oldPoint, newPoint) = curve.tokenCheckpoint(_tokenId, _oldLocked, _newLocked); assertEq(curve.tokenPointIntervals(_tokenId), 2, "C9: token interval incorrect"); - assertEq(newPoint.bias, _newLocked.amount, "C9: bias incorrect"); + // assertEq(newPoint.bias, _newLocked.amount, "C9: bias incorrect"); assertEq(newPoint.checkpointTs, _newLocked.start, "C9: checkpointTs incorrect"); assertEq(newPoint.writtenTs, _warp, "C9: writtenTs incorrect"); assertEq( @@ -345,7 +359,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { (oldPoint, newPoint) = curve.tokenCheckpoint(_tokenId, _oldLocked, _newLocked); // expectation: new point written with new bias - assertEq(curve.tokenPointIntervals(_tokenId), 1, "C10: token interval incorrect"); + assertEq(curve.tokenPointIntervals(_tokenId), 2, "C10: token interval incorrect"); uint elapsed = (_oldLocked.start > block.timestamp) ? 0 @@ -354,7 +368,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { uint newBias = curve.getBias(elapsed, _newAmount); uint oldBias = curve.getBias(elapsed, _oldLocked.amount); - assertEq(newPoint.bias, newBias, "C10: new bias incorrect"); + // assertEq(newPoint.bias, newBias, "C10: new bias incorrect"); // if the new amount is the same, should be equivalent to the old lock if (_newAmount == _oldLocked.amount) { @@ -411,7 +425,7 @@ contract TestLinearIncreasingCurveTokenCheckpoint is LinearCurveBase { _exit == 0 ? 1 : 2, "exit: token interval incorrect" ); - assertEq(newPoint.bias, 0, "exit: bias incorrect"); + // assertEq(newPoint.bias, 0, "exit: bias incorrect"); assertEq(newPoint.checkpointTs, _oldLocked.start + _exit, "exit: checkpointTs incorrect"); assertEq(newPoint.writtenTs, warpTime, "exit: writtenTs incorrect"); assertEq(newPoint.coefficients[0], 0, "exit: constant incorrect");