added csv serializer

This commit is contained in:
JulesCrown 2024-05-29 17:26:19 +02:00
parent 4b86637a40
commit 5d209e5e19
6 changed files with 325 additions and 171 deletions

View file

@ -2,7 +2,7 @@
src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read", path = "./"}]
fs_permissions = [{ access = "read-write", path = "./"}]
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
[rpc_endpoints]

View file

@ -57,7 +57,7 @@ contract BaseLineLP {
uint256 private lastDay;
uint256 private mintedToday;
mapping(Stage => TokenPosition) positions;
mapping(Stage => TokenPosition) public positions;
modifier checkDeadline(uint256 deadline) {
require(block.timestamp <= deadline, "Transaction too old");
@ -103,27 +103,6 @@ contract BaseLineLP {
}
function createPosition(Stage positionIndex, int24 tickLower, int24 tickUpper, uint256 amount) internal {
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower);
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper);
uint128 liquidity = LiquidityAmounts.getLiquidityForAmount1(
sqrtRatioAX96, sqrtRatioBX96, amount
);
pool.mint(address(this), tickLower, tickUpper, liquidity, abi.encode(poolKey));
// TODO: check slippage
// read position and start tracking in storage
bytes32 positionKey = PositionKey.compute(address(this), tickLower, tickUpper);
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128,,) = pool.positions(positionKey);
positions[positionIndex] = TokenPosition({
liquidity: liquidity,
tickLower: tickLower,
tickUpper: tickUpper,
feeGrowthInside0LastX128: feeGrowthInside0LastX128,
feeGrowthInside1LastX128: feeGrowthInside1LastX128
});
}
function outstanding() public view returns (uint256 _outstanding) {
_outstanding = harb.totalSupply() - harb.balanceOf(address(pool)) - harb.balanceOf(address(this));
}
@ -133,17 +112,47 @@ contract BaseLineLP {
}
function ethIn(Stage s) public view returns (uint256 _ethInPosition) {
function tokensIn(Stage s) public view returns (uint256 _ethInPosition, uint256 _harbInPosition) {
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(positions[s].tickLower);
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(positions[s].tickUpper);
if (token0isWeth) {
_ethInPosition = LiquidityAmounts.getAmount0ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity / 2
);
if (s == Stage.FLOOR) {
_ethInPosition = LiquidityAmounts.getAmount0ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity
);
_harbInPosition = 0;
} else if (s == Stage.ANCHOR) {
_ethInPosition = LiquidityAmounts.getAmount0ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity / 2
);
_harbInPosition = LiquidityAmounts.getAmount1ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity / 2
);
} else {
_ethInPosition = 0;
_harbInPosition = LiquidityAmounts.getAmount1ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity
);
}
} else {
_ethInPosition = LiquidityAmounts.getAmount1ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity / 2
);
if (s == Stage.FLOOR) {
_ethInPosition = LiquidityAmounts.getAmount1ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity
);
_harbInPosition = 0;
} else if (s == Stage.ANCHOR) {
_ethInPosition = LiquidityAmounts.getAmount1ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity / 2
);
_harbInPosition = LiquidityAmounts.getAmount0ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity / 2
);
} else {
_ethInPosition = 0;
_harbInPosition = LiquidityAmounts.getAmount0ForLiquidity(
sqrtRatioAX96, sqrtRatioBX96, positions[s].liquidity
);
}
}
}
@ -382,7 +391,7 @@ contract BaseLineLP {
// ## set new positions
// reduce Anchor by 10% of new ETH. It will be moved into Floor
uint256 initialEthInAnchor = ethIn(Stage.ANCHOR);
(uint256 initialEthInAnchor,) = tokensIn(Stage.ANCHOR);
ethInAnchor -= (ethInAnchor - initialEthInAnchor) * 10 / LIQUIDITY_RATIO_DIVISOR;
@ -440,8 +449,8 @@ contract BaseLineLP {
// TODO: set only discovery
return;
}
uint256 ethInAnchor = ethIn(Stage.ANCHOR);
uint256 ethInFloor = ethIn(Stage.FLOOR);
(uint256 ethInAnchor,) = tokensIn(Stage.ANCHOR);
(uint256 ethInFloor,) = tokensIn(Stage.FLOOR);
// use previous ration of Floor to Anchor
uint256 ethInNewAnchor = ethBalance / 10;

View file

@ -2,29 +2,27 @@
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import {TwabController} from "pt-v5-twab-controller/TwabController.sol";
import {PoolAddress, PoolKey} from "@aperture/uni-v3-lib/PoolAddress.sol";
import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
import "@aperture/uni-v3-lib/TickMath.sol";
import "../src/interfaces/IWETH9.sol";
import {WETH} from "solmate/tokens/WETH.sol";
import "../src/Harb.sol";
import {BaseLineLP} from "../src/BaseLineLP.sol";
import {Stake, ExceededAvailableStake} from "../src/Stake.sol";
import "./helpers/PoolSerializer.sol";
address constant TAX_POOL = address(2);
// default fee of 1%
uint24 constant FEE = uint24(10_000);
contract BaseLineLPTest is Test {
contract BaseLineLPTest is PoolSerializer {
uint256 mainnetFork;
IWETH9 weth;
Harb harb;
IUniswapV3Factory factory;
Stake stake;
BaseLineLP liquidityManager;
BaseLineLP lm;
IUniswapV3Pool pool;
bool token0isWeth;
@ -83,32 +81,32 @@ contract BaseLineLPTest is Test {
stake = new Stake(address(harb));
harb.setStakingPool(address(stake));
liquidityManager = new BaseLineLP(factoryAddress, address(weth), address(harb));
harb.setLiquidityManager(address(liquidityManager));
lm = new BaseLineLP(factoryAddress, address(weth), address(harb));
harb.setLiquidityManager(address(lm));
createCSVHeader();
}
function testLP(address account) public {
vm.assume(account != address(0));
vm.assume(account != address(1)); // TWAB sponsorship address
vm.assume(account != address(2)); // tax pool address
vm.deal(account, 15 ether);
function test_LP() public {
address account = makeAddr("alice");
vm.deal(account, 20 ether);
vm.prank(account);
(bool sent, ) = address(liquidityManager).call{value: 10 ether}("");
(bool sent, ) = address(lm).call{value: 10 ether}("");
require(sent, "Failed to send Ether");
// Try to shift liquidity manager's state, expect failure due to initial state
vm.expectRevert();
liquidityManager.shift();
lm.shift();
// Setup of liquidity
liquidityManager.slide();
lm.slide();
appendPossitions(lm, pool, token0isWeth);
// Small buy into Anchor
vm.prank(account);
weth.deposit{value: 5 ether}();
weth.deposit{value: 10 ether}();
vm.prank(account);
weth.approve(address(this), 5 ether);
weth.approve(address(this), 10 ether);
pool.swap(
account, // Recipient of the output tokens
token0isWeth,
@ -117,7 +115,8 @@ contract BaseLineLPTest is Test {
abi.encode(account, int256(0.5 ether), true)
);
liquidityManager.shift();
lm.shift();
appendPossitions(lm, pool, token0isWeth);
// large buy into discovery
pool.swap(
@ -128,12 +127,13 @@ contract BaseLineLPTest is Test {
abi.encode(account, int256(3 ether), true)
);
liquidityManager.shift();
lm.shift();
appendPossitions(lm, pool, token0isWeth);
// sell into anchor
vm.prank(account);
harb.approve(address(this), 2000000 ether);
harb.approve(address(this), 12000000 ether);
pool.swap(
account, // Recipient of the output tokens
!token0isWeth,
@ -142,24 +142,29 @@ contract BaseLineLPTest is Test {
abi.encode(account, int256(300000 ether), false)
);
liquidityManager.slide();
lm.slide();
appendPossitions(lm, pool, token0isWeth);
// large sell into floor
harb.setLiquidityManager(address(account));
vm.prank(account);
harb.mint(900000 ether);
harb.setLiquidityManager(address(liquidityManager));
harb.mint(3600000 ether);
harb.setLiquidityManager(address(lm));
pool.swap(
account, // Recipient of the output tokens
!token0isWeth,
int256(900000 ether),
int256(3600000 ether),
!token0isWeth ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
abi.encode(account, int256(900000 ether), false)
abi.encode(account, int256(3600000 ether), false)
);
// add to CSV
liquidityManager.slide();
lm.slide();
appendPossitions(lm, pool, token0isWeth);
writeCsv();
}
/*//////////////////////////////////////////////////////////////
CALLBACKS
//////////////////////////////////////////////////////////////*/

View file

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import {BaseLineLP} from "../../src/BaseLineLP.sol";
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";
contract PoolSerializer is Test {
string csv;
// Helper function to convert uint to string
function uintToStr(uint256 _i) internal pure returns (string memory _uintAsString) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k-1;
uint8 temp = (48 + uint8(_i - _i / 10 * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
// Helper function to convert int to string
function intToStr(int256 _i) internal pure returns (string memory) {
if (_i == 0) {
return "0";
}
bool negative = _i < 0;
uint256 j = uint256(negative ? -_i : _i);
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
if (negative) {
len++;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
uint256 l = uint256(negative ? -_i : _i);
while (l != 0) {
k = k-1;
uint8 temp = (48 + uint8(l - l / 10 * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
l /= 10;
}
if (negative) {
bstr[0] = '-';
}
return string(bstr);
}
function createCSVHeader() public {
csv = "FLOOR liquidity, FLOOR tickLower, FLOOR tickUpper, FLOOR ETH, FLOOR HARB, ANCHOR liquidity, ANCHOR tickLower, ANCHOR tickUpper, ANCHOR ETH, ANCHOR HARB, DISCOVERY liquidity, DISCOVERY tickLower, DISCOVERY tickUpper, DISCOVERY ETH, DISCOVERY HARB, CURRENT TICK";
}
function appendPossitions(BaseLineLP lm, IUniswapV3Pool pool, bool token0isWeth) public {
(, int24 tickLower, int24 tickUpper,,) = lm.positions(BaseLineLP.Stage.FLOOR);
(uint128 liquidity,,, uint128 tokensOwed0, uint128 tokensOwed1
) = pool.positions(keccak256(abi.encodePacked(address(lm), tickLower, tickUpper)));
string memory floorData = string(abi.encodePacked(
uintToStr(liquidity), ",",
intToStr(tickLower), ",",
intToStr(tickUpper), ",",
uintToStr(token0isWeth ? tokensOwed0 : tokensOwed1), ",",
uintToStr(token0isWeth ? tokensOwed1 : tokensOwed0), ","
));
(liquidity, tickLower, tickUpper,,) = lm.positions(BaseLineLP.Stage.ANCHOR);
(uint256 ethAmount, uint256 harbAmount) = lm.tokensIn(BaseLineLP.Stage.ANCHOR);
string memory anchorData = string(abi.encodePacked(
uintToStr(liquidity), ",",
intToStr(tickLower), ",",
intToStr(tickUpper), ",",
uintToStr(ethAmount), ",",
uintToStr(harbAmount), ","
));
(liquidity, tickLower, tickUpper,,) = lm.positions(BaseLineLP.Stage.DISCOVERY);
(ethAmount, harbAmount) = lm.tokensIn(BaseLineLP.Stage.DISCOVERY);
(, int24 currentTick, , , , , ) = pool.slot0();
string memory discoveryData = string(abi.encodePacked(
uintToStr(liquidity), ",",
intToStr(tickLower), ",",
intToStr(tickUpper), ",",
uintToStr(ethAmount), ",",
uintToStr(harbAmount), ",",
intToStr(currentTick)
));
csv = string(abi.encodePacked(csv, "\n", floorData, anchorData, discoveryData));
}
function writeCsv() public {
string memory path = "./out/positions.csv";
vm.writeFile(path, csv);
}
}

View file

@ -7,11 +7,6 @@ type Stats @entity {
outstandingSupply: BigInt! # uint256
activeSupply: BigInt! # uint256
totalUbiClaimed: BigInt! # field to track total UBI claimed
ubiClaimedLastWeek: BigInt!
ubiClaimedLastDay: BigInt! # field to track UBI claimed in the last 24
ubiClaimedLastHour: BigInt!
ringBuffer: [BigInt!]! # Ring buffer to store daily totals
ringBufferPointer: Int! # Pointer to the current day in the ring buffer
lastUpdatedHour: Int! # The last updated day boundary (timestamp in days)
@ -19,12 +14,22 @@ type Stats @entity {
totalMinted: BigInt!
mintedLastWeek: BigInt!
mintedLastDay: BigInt!
mintedLastHour: BigInt!
mintNextHourProjected: BigInt!
totalBurned: BigInt!
burnedLastWeek: BigInt!
burnedLastDay: BigInt!
burnedLastHour: BigInt!
burnNextHourProjected: BigInt!
totalTaxPaid: BigInt!
taxPaidLastWeek: BigInt!
taxPaidLastDay: BigInt!
taxNextHourProjected: BigInt!
totalUbiClaimed: BigInt!
ubiClaimedLastWeek: BigInt!
ubiClaimedLastDay: BigInt!
ubiNextHourProjected: BigInt!
}
enum PositionStatus {

View file

@ -15,22 +15,32 @@ function getOrCreateStats(): Stats {
stats = new Stats(Bytes.fromHexString("0x01") as Bytes);
stats.outstandingSupply = BigInt.zero();
stats.activeSupply = BigInt.zero();
stats.totalUbiClaimed = BigInt.zero();
stats.ubiClaimedLastWeek = BigInt.zero();
stats.ubiClaimedLastDay = BigInt.zero();
stats.ubiClaimedLastHour = BigInt.zero();
// Minted
stats.totalMinted = BigInt.zero();
stats.mintedLastWeek = BigInt.zero();
stats.mintedLastDay = BigInt.zero();
stats.mintedLastHour = BigInt.zero();
stats.mintNextHourProjected = BigInt.zero();
// Burned
stats.totalBurned = BigInt.zero();
stats.burnedLastWeek = BigInt.zero();
stats.burnedLastDay = BigInt.zero();
stats.burnedLastHour = BigInt.zero();
stats.burnNextHourProjected = BigInt.zero();
stats.ringBuffer = new Array<BigInt>(168 * 3).fill(BigInt.zero());
// Tax paid
stats.totalTaxPaid = BigInt.zero();
stats.taxPaidLastWeek = BigInt.zero();
stats.taxPaidLastDay = BigInt.zero();
stats.taxNextHourProjected = BigInt.zero();
// UBI claimed
stats.totalUbiClaimed = BigInt.zero();
stats.ubiClaimedLastWeek = BigInt.zero();
stats.ubiClaimedLastDay = BigInt.zero();
stats.ubiNextHourProjected = BigInt.zero();
stats.ringBuffer = new Array<BigInt>(168 * 4).fill(BigInt.zero());
stats.ringBufferPointer = 0;
stats.lastUpdatedHour = 0;
}
@ -38,71 +48,6 @@ function getOrCreateStats(): Stats {
}
export function handleTransfer(event: TransferEvent): void {
let ZERO_ADDRESS = Address.fromString("0x0000000000000000000000000000000000000000");
// Update Stats entity
let stats = getOrCreateStats();
// Get a copy of the ring buffer
let ringBuffer = stats.ringBuffer;
// Determine if it's a mint or burn event
if (event.params.from == ZERO_ADDRESS) {
// Mint event
stats.totalMinted = stats.totalMinted.plus(event.params.value);
// Add the minted amount to the current hour's total in the ring buffer
let mintBufferIndex = (stats.ringBufferPointer * 3) + 1; // Minted tokens are at index 1
ringBuffer[mintBufferIndex] = ringBuffer[mintBufferIndex].plus(event.params.value);
// Calculate the elapsed time in the current hour
let currentTimestamp = event.block.timestamp.toI32();
let startOfHour = (currentTimestamp / 3600) * 3600;
let elapsedSeconds = currentTimestamp - startOfHour;
// Project the current hour's total based on the average rate of minted tokens in the current hour
let projectedTotal = ringBuffer[mintBufferIndex].times(BigInt.fromI32(3600)).div(BigInt.fromI32(elapsedSeconds));
// Calculate the medium between the previous hour and the projection
let previousHourTotal = ringBuffer[((stats.ringBufferPointer - 1 + 168) % 168) * 3 + 1];
stats.mintedLastHour = previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2));
log.info("transfer handler: Recalculated totals. projected total: {}. previous hour total: {}. medium: {}.", [
projectedTotal.toString(),
previousHourTotal.toString(),
stats.burnedLastHour.toString(),
]);
} else if (event.params.to == ZERO_ADDRESS) {
// Burn event
stats.totalBurned = stats.totalBurned.plus(event.params.value);
// Add the burned amount to the current hour's total in the ring buffer
let burnBufferIndex = (stats.ringBufferPointer * 3) + 2; // Burned tokens are at index 2
ringBuffer[burnBufferIndex] = ringBuffer[burnBufferIndex].plus(event.params.value);
// Calculate the elapsed time in the current hour
let currentTimestamp = event.block.timestamp.toI32();
let startOfHour = (currentTimestamp / 3600) * 3600;
let elapsedSeconds = currentTimestamp - startOfHour;
// Project the current hour's total based on the average rate of burned tokens in the current hour
let projectedTotal = ringBuffer[burnBufferIndex].times(BigInt.fromI32(3600)).div(BigInt.fromI32(elapsedSeconds));
// Calculate the medium between the previous hour and the projection
let previousHourTotal = ringBuffer[((stats.ringBufferPointer - 1 + 168) % 168) * 3 + 2];
stats.burnedLastHour = previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2));
}
// Update the ring buffer in the stats entity
stats.ringBuffer = ringBuffer;
// Save the updated Stats entity
stats.save();
}
export function handleUbiClaimed(event: UbiClaimedEvent): void {
let stats = getOrCreateStats();
@ -114,20 +59,51 @@ export function handleUbiClaimed(event: UbiClaimedEvent): void {
stats.totalUbiClaimed = stats.totalUbiClaimed.plus(event.params.ubiAmount);
// Add the UBI amount to the current hour's total in the ring buffer
let ubiBufferIndex = (stats.ringBufferPointer * 3) + 0; // UBI is at index 0
let ubiBufferIndex = (stats.ringBufferPointer * 4) + 0; // UBI is at index 0
ringBuffer[ubiBufferIndex] = ringBuffer[ubiBufferIndex].plus(event.params.ubiAmount);
// Calculate the elapsed time in the current hour
let currentTimestamp = event.block.timestamp.toI32();
let startOfHour = (currentTimestamp / 3600) * 3600;
let elapsedSeconds = currentTimestamp - startOfHour;
// Update the ring buffer in the stats entity
stats.ringBuffer = ringBuffer;
// Project the current hour's total based on the average rate of UBI claims in the current hour
let projectedTotal = ringBuffer[ubiBufferIndex].times(BigInt.fromI32(3600)).div(BigInt.fromI32(elapsedSeconds));
// Save the updated Stats entity
stats.save();
}
// Calculate the medium between the previous hour and the projection
let previousHourTotal = ringBuffer[((stats.ringBufferPointer + 167) % 168) * 3 + 0];
stats.ubiClaimedLastHour = previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2));
export function handleTransfer(event: TransferEvent): void {
let ZERO_ADDRESS = Address.fromString("0x0000000000000000000000000000000000000000");
let TAX_POOL_ADDR = Address.fromString("0x0000000000000000000000000000000000000002");
let stats = getOrCreateStats();
// Get a copy of the ring buffer
let ringBuffer = stats.ringBuffer;
// Determine if it's a mint or burn event
if (event.params.from == ZERO_ADDRESS) {
// Mint event
stats.totalMinted = stats.totalMinted.plus(event.params.value);
// Add the minted amount to the current hour's total in the ring buffer
let mintBufferIndex = (stats.ringBufferPointer * 4) + 1; // Minted tokens are at index 1
ringBuffer[mintBufferIndex] = ringBuffer[mintBufferIndex].plus(event.params.value);
} else if (event.params.to == ZERO_ADDRESS) {
// Burn event
stats.totalBurned = stats.totalBurned.plus(event.params.value);
// Add the burned amount to the current hour's total in the ring buffer
let burnBufferIndex = (stats.ringBufferPointer * 4) + 2; // Burned tokens are at index 2
ringBuffer[burnBufferIndex] = ringBuffer[burnBufferIndex].plus(event.params.value);
} else if (event.params.to == TAX_POOL_ADDR) {
// Tax paid event
stats.totalTaxPaid = stats.totalTaxPaid.plus(event.params.value);
// Add the burned amount to the current hour's total in the ring buffer
let taxBufferIndex = (stats.ringBufferPointer * 4) + 3; // tax paid is at index 3
ringBuffer[taxBufferIndex] = ringBuffer[taxBufferIndex].plus(event.params.value);
}
// Update the ring buffer in the stats entity
stats.ringBuffer = ringBuffer;
@ -153,7 +129,7 @@ export function handleBlock(block: ethereum.Block): void {
stats.ringBufferPointer = (stats.ringBufferPointer + 1) % 168;
// Reset the new current hour in the ring buffer
let baseIndex = stats.ringBufferPointer * 3;
let baseIndex = stats.ringBufferPointer * 4;
ringBuffer[baseIndex] = BigInt.zero(); // UBI claimed
ringBuffer[baseIndex + 1] = BigInt.zero(); // Minted tokens
ringBuffer[baseIndex + 2] = BigInt.zero(); // Burned tokens
@ -162,37 +138,85 @@ export function handleBlock(block: ethereum.Block): void {
stats.lastUpdatedHour = currentHour;
// Recalculate the sum of the last 7 days and 24 hours
let totalLast7Days = BigInt.zero();
let totalMintedLast7Days = BigInt.zero();
let totalBurnedLast7Days = BigInt.zero();
let totalLast24Hours = BigInt.zero();
let totalMintedLast24Hours = BigInt.zero();
let totalBurnedLast24Hours = BigInt.zero();
let mintedLastWeek = BigInt.zero();
let burnedLastWeek = BigInt.zero();
let taxPaidLastWeek = BigInt.zero();
let ubiClaimedLastWeek = BigInt.zero();
let mintedLastDay = BigInt.zero();
let burnedLastDay = BigInt.zero();
let taxPaidLastDay = BigInt.zero();
let ubiClaimedLastDay = BigInt.zero();
for (let i = 0; i < 168; i++) {
let index = ((stats.ringBufferPointer - i + 168) % 168) * 3;
totalLast7Days = totalLast7Days.plus(ringBuffer[index]);
totalMintedLast7Days = totalMintedLast7Days.plus(ringBuffer[index + 1]);
totalBurnedLast7Days = totalBurnedLast7Days.plus(ringBuffer[index + 2]);
let index = ((stats.ringBufferPointer - i + 168) % 168) * 4;
ubiClaimedLastWeek = ubiClaimedLastWeek.plus(ringBuffer[index]);
mintedLastWeek = mintedLastWeek.plus(ringBuffer[index + 1]);
burnedLastWeek = burnedLastWeek.plus(ringBuffer[index + 2]);
taxPaidLastWeek = taxPaidLastWeek.plus(ringBuffer[index + 3]);
if (i < 24) {
totalLast24Hours = totalLast24Hours.plus(ringBuffer[index]);
totalMintedLast24Hours = totalMintedLast24Hours.plus(ringBuffer[index + 1]);
totalBurnedLast24Hours = totalBurnedLast24Hours.plus(ringBuffer[index + 2]);
ubiClaimedLastDay = ubiClaimedLastDay.plus(ringBuffer[index]);
mintedLastDay = mintedLastDay.plus(ringBuffer[index + 1]);
burnedLastDay = burnedLastDay.plus(ringBuffer[index + 2]);
taxPaidLastDay = taxPaidLastDay.plus(ringBuffer[index + 3]);
}
}
stats.ubiClaimedLastWeek = totalLast7Days;
stats.mintedLastWeek = totalMintedLast7Days;
stats.burnedLastWeek = totalBurnedLast7Days;
stats.ubiClaimedLastDay = totalLast24Hours;
stats.mintedLastDay = totalMintedLast24Hours;
stats.burnedLastDay = totalBurnedLast24Hours;
stats.mintedLastWeek = mintedLastWeek;
stats.burnedLastWeek = burnedLastWeek;
stats.taxPaidLastWeek = taxPaidLastWeek;
stats.ubiClaimedLastWeek = ubiClaimedLastWeek;
stats.mintedLastDay = mintedLastDay;
stats.burnedLastDay = burnedLastDay;
stats.taxPaidLastDay = taxPaidLastDay;
stats.ubiClaimedLastDay = ubiClaimedLastDay;
// Update the ring buffer in the stats entity
stats.ringBuffer = ringBuffer;
// Save the updated Stats entity
stats.save();
} else {
// update projected stats with every block
// Calculate the elapsed time in the current hour
let currentTimestamp = block.timestamp.toI32();
let startOfHour = (currentTimestamp / 3600) * 3600;
let elapsedSeconds = currentTimestamp - startOfHour;
for (var i = 0; i <= 3; i++) {
let bufferIndex = (stats.ringBufferPointer * 4) + i;
// Project the current hour's total based on the average rate of burned tokens in the current hour
let projectedTotal = ringBuffer[bufferIndex].times(BigInt.fromI32(3600)).div(BigInt.fromI32(elapsedSeconds));
// Calculate the medium between the previous hour and the projection
let previousHourTotal = ringBuffer[((stats.ringBufferPointer - 1 + 168) % 168) * 4 + i];
log.info("projecting stats : {} projected total: {}. previous hour Total: {}. medium: {}", [
i.toString(),
projectedTotal.toString(),
previousHourTotal.toString(),
previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2)).toString(),
]);
let medium = previousHourTotal.plus(projectedTotal).div(BigInt.fromI32(2));
if (i == 0) {
stats.ubiNextHourProjected = (medium > BigInt.zero()) ? medium : stats.ubiClaimedLastWeek.div(BigInt.fromI32(7));
} else if (i == 1) {
stats.mintNextHourProjected = (medium > BigInt.zero()) ? medium : stats.mintedLastWeek.div(BigInt.fromI32(7));
} else if (i == 2) {
stats.burnNextHourProjected = (medium > BigInt.zero()) ? medium : stats.burnedLastWeek.div(BigInt.fromI32(7));
} else if (i == 3) {
stats.taxNextHourProjected = (medium > BigInt.zero()) ? medium : stats.taxPaidLastWeek.div(BigInt.fromI32(7));
}
}
}
// Save the updated Stats entity
stats.save();
}