Skip to content

Commit

Permalink
WIP: writing history
Browse files Browse the repository at this point in the history
  • Loading branch information
jordaniza committed Oct 14, 2024
1 parent 1cd8643 commit 818eca4
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 50 deletions.
64 changes: 45 additions & 19 deletions src/escrow/increasing/LinearIncreasingEscrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ contract LinearIncreasingEscrow is

// if so we have to remove it
if (existingLock) {
// cannot change the start date of an exiting lock once it's passed
// cannot change the start date of an existing lock once it's passed
if (_oldLocked.start != _newLocked.start && block.timestamp >= _oldLocked.start) {
revert RetroactiveStartChange();
}
Expand Down Expand Up @@ -469,7 +469,7 @@ contract LinearIncreasingEscrow is

/// @dev iterates over the interval and looks for scheduled changes that have elapsed
///
function _populateHistory() internal returns (GlobalPoint memory, uint256 latestIndex) {
function _populateHistory() internal returns (GlobalPoint memory, uint256) {
GlobalPoint memory latestPoint = getLatestGlobalPoint();

uint48 interval = uint48(IClock(clock).checkpointInterval());
Expand All @@ -479,37 +479,62 @@ contract LinearIncreasingEscrow is
{
// step 1: round down to floor of interval
uint48 t_i = (latestCheckpoint / interval) * interval;

console.log("t_i", t_i);

for (uint256 i = 0; i < 255; ++i) {
// step 2: the first interval is always the next one after the last checkpoint
t_i += interval;

console.log("t_i + interval", t_i);

// bound to at least the present
if (t_i > block.timestamp) t_i = uint48(block.timestamp);

// we create a new "curve" by defining the coefficients starting from time t_i
console.log("bound t_i", t_i);

// fetch the changes for this interval
int biasChange = _scheduledCurveChanges[t_i][0];
int slopeChange = _scheduledCurveChanges[t_i][1];

console.log("biasChange", biasChange);
console.log("slopeChange", slopeChange);

// we create a new "curve" by defining the coefficients starting from time t_i
// our constant is the y intercept at t_i and is found by evalutating the curve between the last point and t_i
// todo: this aint really a coefficient is it?
// it's just the bias
// todo safe casting
latestPoint.coefficients[0] =
// evaluate the bias between the latest point and t_i
int256(_getBias(t_i - latestPoint.ts, latestPoint.coefficients)) +
// add net scheduled increases
_scheduledCurveChanges[t_i][0];
biasChange;

// here we add the net result of the coefficient changes to the slope
// which can be applied for the ensuring period
// this can be positive or negative depending on if new deposits outweigh tapering effects + withdrawals
latestPoint.coefficients[1] += _scheduledCurveChanges[t_i][1];
latestPoint.coefficients[1] += slopeChange;

// we create a new "curve" by defining the coefficients starting from time t_i
// the slope itself can't be < 0 so we bound it
if (latestPoint.coefficients[1] < 0) {
latestPoint.coefficients[1] = 0;
revert("ahhhh sheeeet");
}

// if the bias is negativo we also should bound it
if (latestPoint.coefficients[0] < 0) {
// think this is redundant as bias checks for this
latestPoint.coefficients[0] = 0;
}

// update the timestamp ahead of either breaking or the next iteration
latestPoint.ts = t_i;
currentIndex++;

bool hasScheduledChange = (biasChange != 0 || slopeChange != 0);

// write the point to storage if it's in the past
// write the point to storage if there are changes, otherwise continue
// interpolating in memory and can write to storage at the end
// otherwise we haven't reached an interval and so can just return the point
currentIndex++;
if (t_i == block.timestamp) break;
else _pointHistory[currentIndex] = latestPoint;
else if (hasScheduledChange) _pointHistory[currentIndex] = latestPoint;
}
}

Expand Down Expand Up @@ -552,19 +577,20 @@ contract LinearIncreasingEscrow is
return _latestPoint;
}

/// @dev Writes or overwrites the latest global point into storage at the index
/// @param _latestPoint The latest global point to write.
/// @param _index The returned index following the history backpop loop.
/// @dev Begins at 1 as corresponds to length of the pseudo-array.
function _writeNewGlobalPoint(GlobalPoint memory _latestPoint, uint256 _index) internal {
// If timestamp of latest global point is the same, overwrite the latest global point
// Else record the new global point into history
// Exclude index 0 (note: _index is always >= 1, see above)
// Two possible outcomes:
// Missing global checkpoints in prior weeks. In this case, _index = index + x, where x > 1
// TODO: doesn't look like that's the case here
// No missing global checkpoints, but timestamp != block.timestamp. Create new checkpoint.
// No missing global checkpoints, but timestamp == block.timestamp. Overwrite _latest checkpoint.
if (_index != 1 && _pointHistory[_index - 1].ts == block.timestamp) {
// _index = index + 1, so we do not increment index
// overwrite the current index, given that the passed one will be the i+1
_pointHistory[_index - 1] = _latestPoint;
} else {
// more than one global point may have been written, so we update index
// first point or a new point
_latestPointIndex = _index;
_pointHistory[_index] = _latestPoint;
}
Expand Down
8 changes: 8 additions & 0 deletions test/escrow/curve/linear/LinearBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ contract MockLinearIncreasingEscrow is LinearIncreasingEscrow {
function writeSchedule(uint48 _at, int256[3] memory _change) external {
_scheduledCurveChanges[_at] = _change;
}

function populateHistory() external returns (GlobalPoint memory, uint) {
return _populateHistory();
}

function writeNewGlobalPoint(GlobalPoint memory _latestPoint, uint256 _index) external {
_writeNewGlobalPoint(_latestPoint, _index);
}
}

contract LinearCurveBase is TestHelpers, ILockedBalanceIncreasing, IEscrowCurveEventsErrorsStorage {
Expand Down
30 changes: 0 additions & 30 deletions test/escrow/curve/linear/LinearEscrow.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,6 @@ contract TestLinearIncreasingCurve is LinearCurveBase {

// check we can't support same deposits

/// Populating history

// in the case of no history, should simply return the empty point and an index of 1

// correctly writes a single backfilled point with no scheduled curve changes

// correctly writes a single backfilled point with a scheduled curve change

// works if the schedulled change is negative

// works if the schedulled change is positive

// works exactly on the interval as expected

// works a complex case of 2 weeks of history + 2 weeks of future and correctly aggregates

/// updating the global history with the token

// reverts when increasing is true (this sucks)

// cannot apply an update if the point hasn't happened yet

// the last point must be caught up

// correctly removes the tokens accrued voting power to the global point at the correct time

// if the user is reducing, this reduction is added back in

// same with the slope

/// writing the point

// overwrites the last point if the timestamp is the same
Expand Down
95 changes: 95 additions & 0 deletions test/escrow/curve/linear/LinearWriteHistory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import {console2 as console} from "forge-std/console2.sol";

import {LinearIncreasingEscrow, IVotingEscrow, IEscrowCurve} from "src/escrow/increasing/LinearIncreasingEscrow.sol";
import {IVotingEscrowIncreasing, ILockedBalanceIncreasing} from "src/escrow/increasing/interfaces/IVotingEscrowIncreasing.sol";

import {LinearCurveBase} from "./LinearBase.sol";

contract TestLinearIncreasingPopulateHistory is LinearCurveBase {
function setUp() public override {
super.setUp();
//
}
// in the case of no history, should simply return the empty point and an index of 1
function testNoHistoryNoScheduleStartingAtZero() public {
(GlobalPoint memory point, uint index) = curve.populateHistory();

// expect that nothing is written
assertEq(point.coefficients[0], 0);
assertEq(point.coefficients[1], 0);
assertEq(index, 1);
}

// if we have no history we start at the timestamp
function testNoHistoryStartingAtTimestamp(uint32 _warp) public {
vm.warp(_warp);
(GlobalPoint memory point, uint index) = curve.populateHistory();

// expect that nothing is written
assertEq(point.coefficients[0], 0);
assertEq(point.coefficients[1], 0);
assertEq(index, 1);
}

// in the case of no history we will return nothing unless exactly on thhe boundary
function testNoHistorySingleSchedule(uint32 _warp) public {
uint interval = clock.checkpointInterval();
vm.assume(_warp >= interval);

vm.warp(_warp);

console.log("_warp: %s", _warp);

// fetch the next interval
uint48 priorInterval = uint48(clock.epochNextCheckpointTs()) -
uint48(clock.checkpointInterval());

console.log("priorInterval: %s", priorInterval);

// write a scheduled point
curve.writeSchedule(priorInterval, [int256(1000), int256(2), int256(0)]);

// populate the history
(GlobalPoint memory point, uint index) = curve.populateHistory();

// expect that nothing is written
assertEq(point.coefficients[0], 0);
assertEq(point.coefficients[1], 0);
assertEq(index, 1);
}

// test writing a global point while we're at it
function testWritePointIndex1() public {}

// correctly writes a single backfilled point with a scheduled curve change
function testHistorySingleSchedule() public {
//
}

function testMultipleEmptyIntervals() public {}

// works if the schedulled change is negative

// works if the schedulled change is positive

// works exactly on the interval as expected

// works a complex case of 2 weeks of history + 2 weeks of future and correctly aggregates

/// updating the global history with the token

// reverts when increasing is true (this sucks)

// cannot apply an update if the point hasn't happened yet

// the last point must be caught up

// correctly removes the tokens accrued voting power to the global point at the correct time

// if the user is reducing, this reduction is added back in

// same with the slope
}
2 changes: 1 addition & 1 deletion test/helpers/TestHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ contract TestHelpers is Test {
using ProxyLib for address;

DAO dao;
Clock clock;
Clock public clock;

address constant OSX_ANY_ADDR = address(type(uint160).max);

Expand Down

0 comments on commit 818eca4

Please sign in to comment.