Skip to content
Protocol contracts not yet configured for this build
Enter app

Security

Keeper constraints

The keeper is the most-used role and the one most likely to live on a hot key. It is therefore designed to be untrusted: the protocol is safe if it is stolen.

The entire surface

solidity
BuybackExecutor.executeBuyback(uint256 ethIn, uint256 minTokensOut, uint256 deadline);PloutoRevenueRouter.sweepDonationsToReserve();

That is all of it. Two functions.

What a stolen keeper key cannot do

  • Withdraw anything. No withdrawal function is reachable.
  • Change any parameter. setSafetyParams is DEFAULT_ADMIN_ROLE.
  • Redirect funds. Purchase output goes to burn or the dead address; donations go to the reserve. Both destinations are fixed in code.
  • Grant itself more. Role administration is admin-only.
  • Trade outside the bounds. Every bound is enforced on chain.
  • Execute at any price. The price-impact floor is computed by the contract from pre-trade spot, not supplied by the caller.

What it can do

Grief. Specifically, repeatedly execute at the worst tolerated price impact until the budget is exhausted.

The damage per execution is capped by maxPriceImpactBps (default 500 = 5%), and the rate is capped by maxEthPerExecution and maxBudgetShareBps. Every execution still buys and retires real PLOUTO; the loss is the difference between a good execution and a maximally bad one.

Response

  1. pause() on BuybackExecutorPAUSER_ROLE. Budget is retained.
  2. revokeRole(KEEPER_ROLE, compromised)DEFAULT_ADMIN_ROLE.
  3. grantRole(KEEPER_ROLE, replacement).
  4. unpause().

Nothing is lost in the meantime. The budget accumulates.

Operational recommendations

  • A dedicated wallet holding only gas. Never a wallet holding tokens or admin rights.
  • Keep maxPriceImpactBps tight; widening it widens the sandwich window directly.
  • Vary execution timing and size. A predictable keeper is easier to trade against.
  • Monitor BuybackExecuted for realised slippage; a run of maximum-impact executions is a signal.

Why claiming needs no keeper

claimPonsFees() and routeUnallocatedRevenue() are permissionless. Anyone can call them, and they can only move money toward the protocol along a fixed path.

The keeper:route script exists to run them on a schedule, not because a permission is required. If the operator disappears, anyone can keep the protocol running.