Module 1: Burn Hour
One UTC hour a day, drawn from a seed fixed by the block hash at deployment, sends the 40% mechanic share to BURN. The other 23 hours send it to STAKERS.
Status
Live
Sinks
Burn / Stakers
Code · BurnHour.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IHibouModule, SwapContext} from "../IHibouModule.sol"; /// @title Burn Hour (module 1) /// @notice One UTC hour a day sends the whole mechanic share to BURN; the rest of the day it goes /// to STAKERS. The hour is drawn per day from a seed fixed at deployment (the block hash). contract BurnHour is IHibouModule { bytes32 public immutable seed; constructor() { seed = blockhash(block.number - 1); } function burnHourOf(uint256 day) public view returns (uint256) { return uint256(keccak256(abi.encode(seed, day))) % 24; } function split(SwapContext calldata ctx) external view returns (uint16[5] memory w) { uint256 day = ctx.timestamp / 1 days; uint256 hour = (ctx.timestamp % 1 days) / 1 hours; if (hour == burnHourOf(day)) w[0] = 10_000; else w[1] = 10_000; } }
Tests
function test_feeOnExactHibouOut() public { _genesis(); swapRouter.swap{value: 1_000 ether}( key, SwapParams({zeroForOne: true, amountSpecified: 500 ether, sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}), "" ); assertGt(address(hook).balance, 0); } function test_feeOnExactEthOut() public { _genesis(); swapRouter.swap( key, SwapParams({zeroForOne: false, amountSpecified: 1 ether, sqrtPriceLimitX96: TickMath.MAX_SQRT_PRICE - 1}), PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}), "" ); assertEq(address(hook).balance, 0.01 ether); } function test_defaultSplitWithoutModule() public { _buy(1 ether, alice); // no author: the 20% joins stakers; module 40% defaults to stakers assertEq(hook.stakersPending(), 0.006 ether); assertEq(hook.burnPending(), 0.004 ether); } function test_burnHourRoutes() public { _genesis(); uint256 day = block.timestamp / 1 days; uint256 h = burnHour.burnHourOf(day); // move to a non-burn hour uint256 other = (h + 1) % 24; vm.warp(day * 1 days + other * 1 hours + 60); _buy(1 ether, alice); assertEq(hook.burnPending(), 0.004 ether); assertEq(hook.stakersPending(), 0.004 ether); // the burn hour vm.warp(day * 1 days + 1 days + burnHour.burnHourOf(day + 1) * 1 hours + 60); _buy(1 ether, alice); assertEq(hook.burnPending(), 0.004 ether + 0.008 ether); } function test_stockWeather() public { _genesis(); feed.set(100e8, 110e8); SwapContext memory c = _ctx(); uint16[5] memory w = stockWeather.split(c); assertEq(w[2], 10_000, "up -> LP donate"); feed.set(110e8, 100e8); w = stockWeather.split(c); assertEq(w[0], 10_000, "down -> burn"); feed.breakIt(); w = stockWeather.split(c); assertEq(w[0], 10_000, "broken feed -> burn"); } function test_executeBurn() public { _buy(10 ether, alice); uint256 pending = hook.burnPending(); assertEq(pending, 0.04 ether); uint256 deadBefore = hibou.balanceOf(hook.DEAD()); vm.expectRevert(HibouDispatcher.NotKeeper.selector); hook.executeBurn(0); vm.prank(agent); uint256 out = hook.executeBurn(1); assertGt(out, 0); assertEq(hibou.balanceOf(hook.DEAD()) - deadBefore, out); assertEq(hook.burnPending(), 0); assertEq(hook.ethBurned(), pending); } function test_burnSlippage() public { _buy(10 ether, alice); vm.prank(agent); vm.expectRevert(); hook.executeBurn(type(uint256).max); assertEq(hook.burnPending(), 0.04 ether, "reverted burn keeps the pot"); } function test_startersPassScreen() public view { this.screen(address(burnHour)); this.screen(address(stockWeather)); this.screen(address(topBuyer)); } function test_submitIdeaBurns() public { vm.startPrank(alice); hibou.approve(address(registry), 1_000 ether); uint256 id = registry.submitIdea("Burn twice on Fridays"); vm.stopPrank(); assertEq(id, 1); assertEq(hibou.balanceOf(registry.DEAD()), 1_000 ether); vm.prank(agent); registry.recordIdea(bob, "Rebate the first buy of each hour"); assertEq(registry.ideaCount(), 2); }
Fork report
- Tests
- 3 passed, 0 failed
- Cases
- test_burnHourRoutes, test_fullVoteFlow, test_startersPassScreen
- Fork
- local · local tests only: this module reads no external contract
- Source hash
- 0x860db76b9546412161109826a1b75467cf868c1d0007477219c362c41cb388c9
- Report hash
- 0x586d54ae9b04b3e08cb32a9236b5fb4f770b66e4f25c574bad1ed6ce443721df
- Generated
- 2026-10-27 05:02 UTC
Vote
Pre-approved at genesis with the other starter modules. Every module after these goes through a 24 h vote and a 24 h timelock.
Onchain
- Contract
- at deployment
- Author
- Genesis
- Code hash
- set by the registry at deployment
- Reads
- block time
- Source
- 0x860db76b9546412161109826a1b75467cf868c1d0007477219c362c41cb388c9
