Skip to content

Commit

Permalink
feat: move to timestamp-based period on mini TWAP
Browse files Browse the repository at this point in the history
  • Loading branch information
dyedm1 committed Sep 19, 2023
1 parent 0c4f905 commit b2c71fe
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 18 deletions.
19 changes: 9 additions & 10 deletions contracts/PanopticPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ contract PanopticPool is ERC1155Holder, Multicall {
/// May be configurable on a pool-by-pool basis in the future, but hardcoded for now
uint32 internal constant TWAP_WINDOW = 600;

// The minimum amount of time, in seconds, permitted between mini/median TWAP updates.
uint256 internal constant MEDIAN_PERIOD = 60;

/// @dev The maximum allowed ratio for a single chunk, defined as: shortLiquidity / netLiquidity
/// The long premium spread multiplier that corresponds with the MAX_SPREAD value depends on VEGOID,
/// which can be explored in this calculator: https://www.desmos.com/calculator/mdeqob2m04
Expand All @@ -180,8 +183,8 @@ contract PanopticPool is ERC1155Holder, Multicall {

/// @notice Mini-median storage slot
/// @dev The data for the last 8 interactions is stored as such:
/// LAST UPDATED BLOCK NUMBER (32 bits)
/// [BLOCK.NUMBER]
/// LAST UPDATED BLOCK TIMESTAMP (40 bits)
/// [BLOCK.TIMESTAMP]
// (00000000000000000000000000000000) // dynamic
//
/// @dev ORDERING of tick indices least --> greatest (24 bits)
Expand Down Expand Up @@ -210,9 +213,6 @@ contract PanopticPool is ERC1155Holder, Multicall {
// [Constants.MAX_V3POOL_TICK] [2]
// 000011011000100111101001
//
/// @dev [TickMath.MAX_TICK] [2]
/// 000011011000100111101001
//
/// @dev [CURRENT TICK] [4]
/// (000000000000000000000000) // dynamic
//
Expand Down Expand Up @@ -296,7 +296,7 @@ contract PanopticPool is ERC1155Holder, Multicall {

unchecked {
s_miniMedian =
(uint256(block.number) << 216) +
(uint256(block.timestamp) << 216) +
// magic number which adds (7,5,3,1,0,2,4,6) order and minTick in positions 7, 5, 3 and maxTick in 6, 4, 2
// see comment on s_miniMedian initialization for format of this magic number
(uint256(0xF590A6F276170D89E9F276170D89E9F276170D89E9000000000000)) +
Expand Down Expand Up @@ -1574,8 +1574,8 @@ contract PanopticPool is ERC1155Holder, Multicall {
function updateMedian(int24 currentTick) internal {
uint256 oldMedianData = s_miniMedian;
unchecked {
// only proceed if last block is more than 1 min old (ie. 5 blocks or older)
if (block.number > uint256(uint40(oldMedianData >> 216)) + 4) {
// only proceed if last entry is at least MEDIAN_PERIOD seconds old
if (block.timestamp >= uint256(uint40(oldMedianData >> 216)) + MEDIAN_PERIOD) {
uint24 orderMap = uint24(oldMedianData >> 192);

uint24 newOrderMap;
Expand All @@ -1601,9 +1601,8 @@ contract PanopticPool is ERC1155Holder, Multicall {

newOrderMap = newOrderMap + ((rank + 1) << (3 * (i + shift - 1)));
}

s_miniMedian =
(block.number << 216) +
(block.timestamp << 216) +
(uint256(newOrderMap) << 192) +
uint256(uint192(oldMedianData << 24)) +
uint256(uint24(currentTick));
Expand Down
21 changes: 13 additions & 8 deletions test/foundry/core/PanopticPool.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4993,7 +4993,7 @@ contract PanopticPoolTest is PositionUtils {
) public {
for (uint256 i = 0; i < 50; ++i) {
pokeTicks[i] = bound(pokeTicks[i], TickMath.MIN_TICK, TickMath.MAX_TICK);
blockTimes[i] = bound(blockTimes[i], 0, type(uint32).max);
blockTimes[i] = bound(blockTimes[i], 0, 21990232555);
}

_initWorldAtTick(x, int24(pokeTicks[0]));
Expand All @@ -5012,10 +5012,10 @@ contract PanopticPoolTest is PositionUtils {
0
];

uint256 lastBlockNumber = block.number;
uint256 lastTimestamp = block.timestamp;

for (uint256 i = 1; i < 10; ++i) {
vm.roll(block.number + blockTimes[i]);
vm.warp(block.timestamp + blockTimes[i]);

UniPoolPriceMock(address(pool)).updatePrice(int24(pokeTicks[i]));

Expand All @@ -5025,12 +5025,17 @@ contract PanopticPoolTest is PositionUtils {
expectedArray[8] = int24(pokeTicks[i]);
(priceArray, medianTick) = pp.getPriceArray();
for (uint256 j = 0; j < 8; ++j) {
// only shift array if an update occured, i.e more than 5 blocks passed since the last update
expectedArray[j] = block.number > lastBlockNumber + 4
console2.log(expectedArray[j]);
console2.log(expectedArray[j+1]);
console2.log(priceArray[j]);
console2.log(block.timestamp);
console2.log(lastTimestamp);
// only shift array if an update occured, i.e more than 60 seconds passed since the last update
expectedArray[j] = block.timestamp >= lastTimestamp + 60
? expectedArray[j + 1]
: expectedArray[j];

assertEq(priceArray[j], expectedArray[j]);
if (priceArray[j] != expectedArray[j]) revert();
}

// sort the array using quicksort and verify correctness of the median
Expand All @@ -5039,8 +5044,8 @@ contract PanopticPoolTest is PositionUtils {
assertEq(medianTick, (sortedPriceArray[3] + sortedPriceArray[4]) / 2);

// bump last updated block number if an update occured
if (block.number > lastBlockNumber + 4) {
lastBlockNumber = block.number;
if (block.timestamp >= lastTimestamp + 60) {
lastTimestamp = block.timestamp;
}
}
}
Expand Down

0 comments on commit b2c71fe

Please sign in to comment.