Revenue epochs
A revenue epoch is one complete cycle of pulling ETH out of the Pons Fee Escrow and splitting it. It is the atomic unit of everything Plouto does.
An epoch is not a fixed period of time. It is not hourly, daily or weekly. It happens whenever someone calls the router, and it processes whatever has accumulated since the last time.
The two halves
An epoch has two distinct steps, and they can happen in the same transaction or in different ones.
- claimPonsFees()
Pulls this contract's entire native-ETH credit out of the Pons Fee Escrow. Credits only the measured ETH balance delta. Permissionless — anyone can call it, because claiming can only ever move money toward the protocol.
- routeUnallocatedRevenue()
Takes everything currently unallocated, splits it 60/30/10, and forwards each part. Also permissionless. Blocked until the registry is initialized, because the buyback route does not exist before PLOUTO does.
claimAndRoute() does both in one transaction, which is what the keeper normally calls.
Why claiming is permissionless
There is no privileged "epoch operator". Anyone can trigger a claim or a route, and this is intentional: both operations can only move ETH from a place the protocol does not control into places it does, along a path that is fixed in the contract.
A malicious caller can, at worst, cause the split to happen at a moment of their choosing. They cannot redirect any of it.
What gets credited
This is the part where a naive implementation would go wrong. The escrow's claim() returns a number. Plouto does not use it.
function claimPonsFees() public nonReentrant returns (uint256 received) { uint256 balanceBefore = address(this).balance; uint256 reported = feeEscrow.claim(); received = address(this).balance - balanceBefore; if (received == 0) revert NothingClaimed(); totalRevenueClaimed += received; unallocatedRevenue += received; emit PonsFeesClaimed(reported, received, unallocatedRevenue);}reported is emitted for observability. received — the measured delta — is what the accounting uses. A test in the suite drives an escrow that deliberately over-reports and confirms the protocol credits the real amount.
Dust and exactness
The split rounds down for the two larger shares and gives the remainder to the reserve:
toBuybacks = (amount * BUYBACK_BPS) / BPS;toStakers = (amount * STAKER_BPS) / BPS;toReserve = amount - toBuybacks - toStakers;This guarantees toBuybacks + toStakers + toReserve == amount for every possible input, with no wei created or destroyed. An invariant test asserts it across randomised amounts.
An epoch can be empty
If the escrow holds nothing, claimPonsFees() reverts with NothingClaimed() rather than emitting a zero-value event. If nothing is unallocated, routeUnallocatedRevenue() reverts with NothingToRoute().
Reverting rather than no-oping keeps the event log meaningful: every RevenueRouted event in history corresponds to real money moving.
What an epoch does not include
Fees still sitting on the Pons curve or hook are not part of any epoch. They have not been swept, so they are not in escrow, so they cannot be claimed. Plouto has no way to read them as a balance, and the interface shows that stage as explicitly unknown rather than guessing.