Skip to content

Commit

Permalink
rename twap packed data for better clarity
Browse files Browse the repository at this point in the history
  • Loading branch information
rayeaster committed Dec 21, 2023
1 parent c7b7f12 commit 0d95dd1
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 52 deletions.
8 changes: 4 additions & 4 deletions packages/contracts/contracts/ActivePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ contract ActivePool is

uint256 cachedSystemDebt = systemDebt + _amount;

_setValue(uint128(cachedSystemDebt)); // @audit update TWAP spot value
update(); // @audit update TWAP accumulator and weighted average
_setValue(uint128(cachedSystemDebt)); // @audit update TWAP global spot value and accumulator variable along with a timestamp
update(); // @audit update TWAP Observer accumulator and weighted average

systemDebt = cachedSystemDebt;
emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
Expand All @@ -222,8 +222,8 @@ contract ActivePool is

uint256 cachedSystemDebt = systemDebt - _amount;

_setValue(uint128(cachedSystemDebt)); // @audit update TWAP spot value
update(); // @audit update TWAP accumulator and weighted average
_setValue(uint128(cachedSystemDebt)); // @audit update TWAP global spot value and accumulator variable along with a timestamp
update(); // @audit update TWAP Observer accumulator and weighted average

systemDebt = cachedSystemDebt;
emit ActivePoolEBTCDebtUpdated(cachedSystemDebt);
Expand Down
62 changes: 31 additions & 31 deletions packages/contracts/contracts/Dependencies/TwapWeightedObserver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ contract TwapWeightedObserver is ITwapWeightedObserver {

constructor(uint128 initialValue) {
PackedData memory cachedData = PackedData({
priceCumulative0: initialValue,
observerCumuVal: initialValue,
accumulator: initialValue,
t0: uint64(block.timestamp),
lastUpdate: uint64(block.timestamp),
avgValue: initialValue
observerUpdTs: uint64(block.timestamp),
lastTrackUpdTs: uint64(block.timestamp),
avgSinceLastObs: initialValue
});

valueToTrack = initialValue;
Expand All @@ -31,7 +31,7 @@ contract TwapWeightedObserver is ITwapWeightedObserver {
function _setValue(uint128 newValue) internal {
uint128 _newAcc = _updateAcc(valueToTrack);

data.lastUpdate = uint64(block.timestamp);
data.lastTrackUpdTs = uint64(block.timestamp);
emit NewTrackValue(valueToTrack, newValue, block.timestamp, _newAcc);
valueToTrack = newValue;
}
Expand All @@ -47,7 +47,7 @@ contract TwapWeightedObserver is ITwapWeightedObserver {
/// @return Duration since last update
/// @dev Safe from overflow for tens of thousands of years
function timeToAccrue() public view returns (uint64) {
return uint64(block.timestamp) - data.lastUpdate;
return uint64(block.timestamp) - data.lastTrackUpdTs;
}

/// @notice Returns the accumulator value, adjusted according to the current value and block timestamp
Expand Down Expand Up @@ -77,55 +77,55 @@ contract TwapWeightedObserver is ITwapWeightedObserver {
function observe() external returns (uint256) {
// Here, we need to apply the new accumulator to skew the price in some way
// The weight of the skew should be proportional to the time passed
uint256 futureWeight = block.timestamp - data.t0;
uint256 futureWeight = block.timestamp - data.observerUpdTs;

if (futureWeight == 0) {
return data.avgValue;
return data.avgSinceLastObs;
}

// A reference period is 7 days
// For each second passed after update
// Let's virtally sync TWAP
// With a weight, that is higher, the more time has passed
uint128 priceCum0 = getLatestAccumulator();
uint128 virtualAvgValue = (priceCum0 - data.priceCumulative0) /
(uint64(block.timestamp) - data.t0);
(uint128 virtualAvgValue, uint128 obsAcc) = _calcUpdatedAvg();

uint256 maxWeight = PERIOD;
if (futureWeight > maxWeight) {
_update(virtualAvgValue, priceCum0, uint64(block.timestamp)); // May as well update
if (_checkUpdatePeriod()) {
_update(virtualAvgValue, obsAcc); // May as well update
// Return virtual
return virtualAvgValue;
}

uint256 weightedAvg = data.avgValue * (maxWeight - futureWeight);
uint256 weightedAvg = data.avgSinceLastObs * (PERIOD - futureWeight);
uint256 weightedVirtual = virtualAvgValue * (futureWeight);

uint256 weightedMean = (weightedAvg + weightedVirtual) / PERIOD;

return weightedMean;
}

function update() public {
// On epoch flip, we update as intended
if (block.timestamp >= data.t0 + PERIOD) {
uint128 latestAcc = getLatestAccumulator();
function _calcUpdatedAvg() internal view returns (uint128, uint128) {
uint128 latestAcc = getLatestAccumulator();
uint128 avgValue = (latestAcc - data.observerCumuVal) /
(uint64(block.timestamp) - data.observerUpdTs);
return (avgValue, latestAcc);
}

// Compute based on delta
uint128 avgValue = (latestAcc - data.priceCumulative0) /
(uint64(block.timestamp) - data.t0);
uint128 priceCum0 = latestAcc;
uint64 time0 = uint64(block.timestamp);
function _update(uint128 avgValue, uint128 obsAcc) internal {
data.avgSinceLastObs = avgValue;
data.observerCumuVal = obsAcc;
data.observerUpdTs = uint64(block.timestamp);
}

_update(avgValue, priceCum0, time0);
}
function _checkUpdatePeriod() internal returns (bool) {
return block.timestamp >= (data.observerUpdTs + PERIOD);
}

/// Internal update so we can call it both in _update and in observe
function _update(uint128 avgValue, uint128 priceCum0, uint64 time0) internal {
data.avgValue = avgValue;
data.priceCumulative0 = priceCum0;
data.t0 = time0;
/// @dev update time-weighted Observer
function update() public {
if (_checkUpdatePeriod()) {
(uint128 avgValue, uint128 latestAcc) = _calcUpdatedAvg();
_update(avgValue, latestAcc);
}
}

function getData() external view returns (PackedData memory) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@ interface IBaseTwapWeightedObserver {
struct PackedData {
// Slot 0
// Seconds in a year: 3.154e+7
uint128 priceCumulative0; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171
/// @dev Accumulator value recorded for TWAP Observer until last update
uint128 observerCumuVal; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171
/// @dev Accumulator for TWAP globally
uint128 accumulator; // 3.154e+7 * 80 * 100e27 = 2.5232e+38 | log_2(100e27 * 3.154e+7 * 80) = 127.568522171
// NOTE: We can further compress this slot but we will not be able to use only one (see u72 impl)
/// So what's the point of making the code more complex?

// Slot 1
uint64 t0; // Thousands of Years, if we use relative time we can use u32 | Relative to deploy time (as immutable)
uint64 lastUpdate; // Thousands of years
/// @dev last update timestamp for TWAP Observer
uint64 observerUpdTs; // Thousands of Years, if we use relative time we can use u32 | Relative to deploy time (as immutable)
/// @dev last update timestamp for TWAP global track(spot) value
uint64 lastTrackUpdTs; // Thousands of years
// Expect eBTC debt to never surpass 100e27, which is 100 BILLION eBTC
// log_2(100e27) = 96.3359147517 | log_2(100e27 / 1e18) = 36.5412090438
// We could use a u64
uint128 avgValue;
/// @dev average value since last observe
uint128 avgSinceLastObs;
}
}
10 changes: 5 additions & 5 deletions packages/contracts/foundry_test/ActivePool.twapAcc.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract ActivePoolTwapAccTest is eBTCBaseFixture {

function testBasicTwap() public {
uint256 entropy = 67842918170911949682054359726922204181906323355453850;
vm.warp((apTester.getData()).lastUpdate + apTester.PERIOD());
vm.warp((apTester.getData()).lastTrackUpdTs + apTester.PERIOD());
apTester.unprotectedSetTwapTrackVal(100);

while (entropy > 0) {
Expand All @@ -45,8 +45,8 @@ contract ActivePoolTwapAccTest is eBTCBaseFixture {
assertEq(_val, apTester.valueToTrack());

uint256 _accBefore = apTester.getLatestAccumulator();
vm.warp((apTester.getData()).lastUpdate + apTester.PERIOD());
uint256 _duration = block.timestamp - (apTester.getData()).lastUpdate;
vm.warp((apTester.getData()).lastTrackUpdTs + apTester.PERIOD());
uint256 _duration = block.timestamp - (apTester.getData()).lastTrackUpdTs;
uint256 _accAfter = apTester.getLatestAccumulator();
assertEq(_duration * _val, _accAfter - _accBefore);
}
Expand All @@ -63,7 +63,7 @@ contract ActivePoolTwapAccTest is eBTCBaseFixture {

uint256 _accBefore = apTester.getLatestAccumulator();
vm.warp(MANY_YEARS);
uint256 _duration = block.timestamp - (apTester.getData()).lastUpdate;
uint256 _duration = block.timestamp - (apTester.getData()).lastTrackUpdTs;
uint256 _accAfter = apTester.getLatestAccumulator();
assertEq(TEN_BILLION_USD * _duration, _accAfter - _accBefore);
}
Expand All @@ -74,7 +74,7 @@ contract ActivePoolTwapAccTest is eBTCBaseFixture {
assertEq(NORMAL_VALUE, apTester.valueToTrack());

// update the accumulator normally after period
vm.warp((apTester.getData()).t0 + apTester.PERIOD() + 123);
vm.warp((apTester.getData()).observerUpdTs + apTester.PERIOD() + 123);
apTester.update();
assertEq(NORMAL_VALUE, apTester.valueToTrack());
uint256 _obsv = apTester.observe();
Expand Down
8 changes: 4 additions & 4 deletions packages/contracts/foundry_test/BaseFixture.sol
Original file line number Diff line number Diff line change
Expand Up @@ -634,11 +634,11 @@ contract eBTCBaseFixture is
console.log("getLatestAccumulator: ", getLatestAccumulator.pretty());
console.log("observe: ", observe.pretty());
console.log("");
console.log("data.priceCumulative0: ", data.priceCumulative0);
console.log("data.priceCumulative0: ", data.observerCumuVal);
console.log("data.accumulator: ", data.accumulator);
console.log("data.t0: ", data.t0);
console.log("data.lastUpdate: ", data.lastUpdate);
console.log("data.avgValue: ", data.avgValue);
console.log("data.t0: ", data.observerUpdTs);
console.log("data.lastUpdate: ", data.lastTrackUpdTs);
console.log("data.avgValue: ", data.avgSinceLastObs);
console.log("");
}

Expand Down
8 changes: 4 additions & 4 deletions packages/contracts/utils/testHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,15 +321,15 @@ class TestHelper {
let _valToCheck = await contracts.activePool.valueToTrack();
console.log('twap valToCheck=' + _valToCheck);
let _period = await contracts.activePool.PERIOD();
let _diffTime = this.toBN(blockTimestampStart + diffTimeBtwBlocks - _twapDebtData["lastUpdate"])
let _diffTime = this.toBN(blockTimestampStart + diffTimeBtwBlocks - _twapDebtData["lastTrackUpdTs"])
console.log('twap diffTime=' + _diffTime);
let _acc = this.toBN(_twapDebtData["accumulator"]).add(this.toBN(_valToCheck).mul(_diffTime))
console.log('twap acc=' + _acc);
let _diffTimeT0 = this.toBN(blockTimestampStart + diffTimeBtwBlocks - _twapDebtData["t0"])
let _diffTimeT0 = this.toBN(blockTimestampStart + diffTimeBtwBlocks - _twapDebtData["observerUpdTs"])
console.log('twap diffTimeT0=' + _diffTimeT0);
let _avg = _acc.sub(this.toBN(_twapDebtData["priceCumulative0"])).div(_diffTimeT0);
let _avg = _acc.sub(this.toBN(_twapDebtData["observerCumuVal"])).div(_diffTimeT0);
console.log('twap _avg=' + _avg);
let _weightedMean = (this.toBN(_twapDebtData["avgValue"]).mul(_period.sub(_diffTimeT0)).add(_avg.mul(_diffTimeT0))).div(_period);
let _weightedMean = (this.toBN(_twapDebtData["avgSinceLastObs"]).mul(_period.sub(_diffTimeT0)).add(_avg.mul(_diffTimeT0))).div(_period);
console.log('twap _weightedMean=' + _weightedMean);
return _weightedMean;
}
Expand Down

0 comments on commit 0d95dd1

Please sign in to comment.