Module 3: Top Buyer
The whole 40% mechanic share goes to the TOP_BUYER pot, credited after 00:00 UTC to the largest single $HIBOU buy of the UTC day.
Status
Approved
Sinks
Top buyer
Code · TopBuyer.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import {IHibouModule, SwapContext} from "../IHibouModule.sol"; /// @title Top Buyer (module 3) /// @notice The whole mechanic share goes to the TOP_BUYER pot, paid after 00:00 UTC to the /// largest $HIBOU buy of the day. contract TopBuyer is IHibouModule { function split(SwapContext calldata) external pure returns (uint16[5] memory w) { w[3] = 10_000; } }
Tests
function test_badModulesFallBack() public { BadSum bad = new BadSum(); GasHog hog = new GasHog(); vm.startPrank(owner); registry.registerGenesis(address(bad), authorA, 0, "bad", true); vm.stopPrank(); (uint16[5] memory w, bool fb) = hook.weightsFor(_ctx()); assertTrue(fb); assertEq(w[1], 10_000); vm.prank(owner); registry.registerGenesis(address(hog), authorA, 0, "hog", true); (w, fb) = hook.weightsFor{gas: 1_000_000}(_ctx()); assertTrue(fb); assertEq(w[1], 10_000); // swaps still work with a bad module live _buy(1 ether, alice); assertEq(hook.feesRouted(), 0.01 ether); } function test_topBuyerPot() public { vm.prank(owner); registry.registerGenesis(address(topBuyer), authorA, 0, "Top Buyer", true); _buy(1 ether, alice); _buy(3 ether, bob); _buy(2 ether, alice); uint256 day = block.timestamp / 1 days; (address leader,, uint128 pot,) = hook.topBuys(day); assertEq(leader, bob); assertEq(pot, 0.024 ether, "40% of 6 ETH * 1%"); vm.expectRevert(HibouDispatcher.DayNotOver.selector); hook.settleTopBuyer(day); vm.warp(block.timestamp + 1 days); hook.settleTopBuyer(day); assertEq(hook.credits(bob), 0.024 ether); vm.prank(bob); hook.claim(); assertEq(bob.balance, 0.024 ether); } function test_pauseFallsBack() public { vm.prank(owner); registry.registerGenesis(address(topBuyer), authorA, 0, "Top Buyer", true); vm.expectRevert(HibouDispatcher.NotGuardian.selector); hook.setPaused(true); vm.prank(guardian); hook.setPaused(true); _buy(1 ether, alice); assertEq(hook.credits(authorA), 0, "paused: no author share"); assertEq(hook.stakersPending(), 0.006 ether); } function test_ownerWithdrawKeepsCredits() public { _genesis(); _buy(1 ether, alice); // anyone can top up the contract; funding is income through the same split (bool ok,) = address(hook).call{value: 1 ether}(""); assertTrue(ok); uint256 owed = hook.totalOwed(); assertEq(hook.available(), 0); vm.prank(owner); hook.withdrawAll(); assertEq(hook.shortfall(), owed); assertEq(hook.credits(authorA), 0.002 ether + 0.2 ether, "credit survives the withdrawal"); vm.prank(authorA); vm.expectRevert(); hook.claim(); // refill fills the shortfall first vm.deal(address(this), 10 ether); (ok,) = address(hook).call{value: owed}(""); assertTrue(ok); assertEq(hook.shortfall(), 0); vm.prank(authorA); hook.claim(); } function test_screen() public { EvilStore s = new EvilStore(); EvilCall c = new EvilCall(); vm.startPrank(agent); vm.expectRevert(); registry.propose(address(s), address(s).codehash, 0, authorA, 1, "evil"); vm.expectRevert(); registry.propose(address(c), address(c).codehash, 0, authorA, 1, "evil"); vm.stopPrank(); } function test_startersPassScreen() public view { this.screen(address(burnHour)); this.screen(address(stockWeather)); this.screen(address(topBuyer)); } function test_codeHashMustMatch() public { vm.prank(agent); vm.expectRevert(); registry.propose(address(topBuyer), keccak256("other"), 0, authorA, 1, "Top Buyer"); } function test_onlyAgentProposes() public { address t = address(topBuyer); bytes32 h = t.codehash; vm.expectRevert(ModuleRegistry.NotAgent.selector); registry.propose(t, h, 0, authorA, 1, "Top Buyer"); } function test_fullVoteFlow() public { _genesis(); _stake(alice, 100_000 ether); // above 0.5% of the ~10.15M supply _stake(bob, 10_000 ether); address t = address(topBuyer); vm.prank(agent); uint256 id = registry.propose(t, t.codehash, keccak256("fork"), authorB, 7, "Top Buyer v2"); vm.prank(alice); registry.vote(id, true); vm.prank(bob); registry.vote(id, false); vm.prank(alice); vm.expectRevert(ModuleRegistry.AlreadyVoted.selector); registry.vote(id, true); // stake locked until the vote closes vm.prank(alice); vm.expectRevert(); staking.unstake(1 ether); uint256 t0 = block.timestamp; vm.expectRevert(ModuleRegistry.VoteOpen.selector); registry.finalize(id); vm.warp(t0 + 24 hours); registry.finalize(id); ModuleRegistry.Proposal memory p = registry.getProposal(id); assertEq(uint8(p.state), uint8(ModuleRegistry.State.Queued)); vm.expectRevert(); registry.activate(id); vm.warp(t0 + 48 hours); registry.activate(id); assertEq(hook.module(), t); assertEq(hook.author(), authorB); assertEq(uint8(registry.getProposal(0).state), uint8(ModuleRegistry.State.Replaced)); assertEq(registry.shipped(), 4); vm.prank(alice); staking.unstake(1 ether); } function test_quorumNotMet() public { _stake(bob, 10_000 ether); // under 0.5% address t = address(topBuyer); vm.prank(agent); uint256 id = registry.propose(t, t.codehash, 0, authorB, 1, "x"); vm.prank(bob); registry.vote(id, true); vm.warp(block.timestamp + 24 hours); registry.finalize(id); assertEq(uint8(registry.getProposal(id).state), uint8(ModuleRegistry.State.Defeated)); } function test_noVoteAfterClose() public { _stake(alice, 1 ether); address t = address(topBuyer); vm.prank(agent); uint256 id = registry.propose(t, t.codehash, 0, authorB, 1, "x"); vm.warp(block.timestamp + 24 hours); vm.prank(alice); vm.expectRevert(ModuleRegistry.NotVoting.selector); registry.vote(id, true); } 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); } function test_genesisClosed() public { _genesis(); vm.prank(owner); vm.expectRevert(ModuleRegistry.GenesisIsClosed.selector); registry.registerGenesis(address(topBuyer), authorA, 0, "x", true); } function test_onlyRegistrySetsModule() public { vm.expectRevert(HibouDispatcher.NotRegistry.selector); hook.setModule(address(topBuyer), alice); } function test_routerBuysAndSells() public { vm.prank(owner); registry.registerGenesis(address(topBuyer), authorA, 0, "Top Buyer", true); HibouRouter router = new HibouRouter(pm, key); vm.deal(bob, 2 ether); vm.prank(bob); uint256 out = router.swapEthForHibou{value: 1 ether}(1, bob); assertEq(hibou.balanceOf(bob), 50_000 ether + out); (address leader,,,) = hook.topBuys(block.timestamp / 1 days); assertEq(leader, bob, "hookData carries the real buyer"); vm.startPrank(bob); hibou.approve(address(router), out); uint256 back = router.swapHibouForEth(out, 1, bob); vm.stopPrank(); assertGt(back, 0.9 ether); assertGt(address(hook).balance, 0.019 ether); }
Fork report
- Tests
- 3 passed, 0 failed
- Cases
- test_fullVoteFlow, test_startersPassScreen, test_topBuyerPot
- Fork
- local · local tests only: this module reads no external contract
- Source hash
- 0xf1a92428800e12335569de26d0449a8996022541178eb473cf2f86794a1b0e49
- Report hash
- 0x6cd995de770b3f9127b64303652c80b7050a6da749cd05115b9f8e0087e2af9f
- 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
- swap direction and size
- Source
- 0xf1a92428800e12335569de26d0449a8996022541178eb473cf2f86794a1b0e49
