The Core
The Core is the PloutoRevenueRouter contract and the rule it enforces. It has one job: take claimed revenue and divide it three ways, the same way, every time.
Source
Pons creator fees
Claimed in ETH from the fee escrow
Router
Plouto Revenue Router
Immutable 60 / 30 / 10 · no admin override
- 60%
Buyback & retirement
Market purchases, permanently removed from circulation.
- 30%
Staker rewards
ETH streamed to locked Gravity positions.
- 10%
Plouto Reserve
Security, infrastructure, automation and future protocol-owned liquidity.
The rule
uint256 public constant BUYBACK_BPS = 6000;uint256 public constant STAKER_BPS = 3000;uint256 public constant RESERVE_BPS = 1000;uint256 public constant BPS = 10_000;These are constant, which in Solidity means they are compiled into the bytecode. There is no storage slot holding them and therefore no way — for an admin, an owner, a multisig or anyone else — to change them after deployment. Changing the split would require deploying a different contract and persuading Pons to redirect fees to it, which is a visible, timelocked operation on the Pons side.
The constructor asserts the arithmetic as a last line of defence against a bad edit:
assert(BUYBACK_BPS + STAKER_BPS + RESERVE_BPS == BPS);What each destination does
- 60% → BuybackExecutor
Funds open-market purchases of PLOUTO which are then permanently retired. The ETH sits as
buybackBudgetuntil a keeper executes, bounded by size, slippage and price-impact limits.- 30% → GravityStaking
Delivered by calling
notifyRewardETH(), which folds the amount into the reward-per-weight accumulator. If nothing is staked, it is held rather than lost.- 10% → PloutoReserve
Delivered by calling
depositRevenue(), which records it as protocol revenue rather than a donation and appends to the deposit history.
Ordering
The router follows checks-effects-interactions strictly. All accounting is written and the event is emitted before any ETH leaves:
unallocatedRevenue = 0;totalRevenueRouted += amount;totalSentToBuybacks += toBuybacks;totalSentToStakers += toStakers;totalSentToReserve += toReserve;_routes.push(RouteRecord(uint64(block.timestamp), amount, toBuybacks, toStakers, toReserve)); emit RevenueRouted(amount, toBuybacks, toStakers, toReserve, totalRevenueRouted, _routes.length - 1); if (toBuybacks != 0) IBuybackExecutor(buybackExecutor).fundBuyback{value: toBuybacks}();if (toStakers != 0) IGravityStaking(gravityStaking).notifyRewardETH{value: toStakers}();if (toReserve != 0) IPloutoReserve(reserve).depositRevenue{value: toReserve}();Combined with nonReentrant, a downstream contract cannot re-enter mid-split and observe or exploit a partially-updated state.
What the Core deliberately does not have
- No arbitrary-call function. There is no
execute(address,bytes)anywhere in the router. An admin cannot make it call something unexpected. - No percentage setter. Not even behind a timelock.
- No withdrawal function. ETH held by the router can only leave through the split, or — for donations specifically — through
sweepDonationsToReserve(), which sends to a fixed address. - No pause on claiming. Pausing blocks routing, not claiming, so a pause can never strand fees in the escrow.
Donations are not revenue
ETH sent directly to the router is classified as a donation, tracked in totalDonationsReceived and unallocatedDonations, and excluded from the split entirely.
The one subtlety: ETH arriving from the escrow is the payout leg of a claim, which is already accounted for by measured delta. The receive() function falls through in that case rather than double-counting:
receive() external payable { if (msg.sender == address(feeEscrow)) return; totalDonationsReceived += msg.value; unallocatedDonations += msg.value; emit DonationReceived(msg.sender, msg.value);}This was a real bug found by the test suite, and is described in build status.