Floor keeper
The unprivileged service that submits eligible floor raises. It cannot choose a floor, take backing, or pause a market.
The keeper is liveness, not authority. It watches markets whose on-chain floor policy already permits a raise and submits the same permissionless call anyone else may submit:
FloorPolicyController.raiseIfEligible(market, witness);
The FloorPolicyController recomputes the plan on-chain. The witness only proves freshness. If the plan, market nonce, engine hash, or deadline no longer match, the call reverts. The floor cannot go down.
There is no keeper bounty in v1. The operator funds unprivileged gas accounts. Any third party can call the same function at its own cost.
Shutting the keeper off does not make a market insolvent or a position liquidatable. It only delays a floor increase that is already permitted by on-chain state. Existing spot, floor, reserve accounting, redemptions, and positions continue under the last completed raise.
What it does
- Discover markets from the factory sequence, with optional indexer hints.
- Read eligibility and the deterministic plan through the SDK: quoteNextRaise.
- Drop stale or ineligible candidates. This is not an operational failure.
- Simulate, then submit
raiseIfEligiblewith a conservative gas cap: raiseIfEligible. - Confirm the raise on-chain. Another actor winning the same raise is a successful race loss, not an incident.
The keeper does not:
- pick a target floor or invent a plan from spot, TWAP, or an oracle
- open, close, or liquidate positions
- claim creator or protocol revenue
- pause, upgrade, or hold admin
- use the deployer or upgrader key
Compromise of a keeper key can waste that key's gas and compete to submit permissionless raises. It cannot choose an invalid floor or take backing if the controller and market are correct.
Who may submit
raiseIfEligible is public. The product keeper is one operator-run client. Users and competing bots may submit the same call through the SDK.
The operator client uses two funded gas wallets. Markets are sharded across them so the two accounts never share a nonce or bid against each other. If the preferred wallet is underfunded or nonce-halted, work moves to the standby wallet and the operator is paged — fallback is an incident, not silent rotation.
Gas and batching
Floor raises pay no bounty, so the keeper does not join priority-fee auctions. Fees are capped. If a replacement would exceed the cap, the keeper waits rather than overpay. A competing submitter raising the floor is a good outcome for the protocol.
When many markets are eligible at once, the SDK packs raiseIfEligible calls into a Multicall3 batch (aggregate3 with allowFailure). A single raise still goes straight to the controller.
Default is observe-only
The Worker starts with OBSERVE_ONLY=true. It still discovers, quotes, and simulates. It does not broadcast. One real local submit is a later proving step, not this default.
Quotes, calldata, and broadcasts go through @repo/contract-client. Indexed HTTP is discovery only and is never required for liveness.
Related
- Architecture — where the keeper sits relative to Directory, Factory, and FloorMarket
- Curve & Floor Math — why a raise cannot lower the floor or break spot
- Risks & Controls — keeper outage and admin pause as separate trust assumptions
- raiseIfEligible — SDK write path