For the publisher service that keeps forward prices fresh onchain, see Publisher Operations.
Architecture
A keeper is any service that:- Reads open position state via
PositionManager.getPositionStats(pairId) - Settles matured positions via
SettlementEngine.batchSettlePositions() - Liquidates undercollateralized positions via
SettlementEngine.batchLiquidatePositions() - Optionally clears matured forward rounds to reclaim gas via
OracleModule.clearMaturedForwards()
Keeper Cycle
The keeper polls for matured and liquidatable positions, then executes batch operations. Settlement and liquidation are independent — one failing does not block the other.Query Position Stats
Call
getPositionStats(pairId) (read-only) to get counts of matured and liquidatable positions for the EUR/USD pair.Settle Matured Positions
If matured count is greater than 0, call
batchSettlePositions(pairId, maxCount) to settle matured positions in a single transaction. Matured positions that are also liquidatable are skipped by batch settlement and require a separate liquidation call.Permissionless Entry Points
Anyone can call these functions — no special role is required. This ensures the protocol can always make progress even if the official keeper goes offline.| Function | Description | Gas Refund |
|---|---|---|
batchSettlePositions(pairId, maxCount) | Settle matured positions in batch | No |
settlePosition(positionId) | Settle a single matured position | No |
batchLiquidatePositions(pairId, maxCount) | Liquidate eligible positions in batch | No |
liquidatePosition(positionId) | Liquidate a single eligible position | No |
recordFixingPriceFromPyth(pairId, fixingTimestamp, pythUpdateData[]) | Record fixing price from Pyth | No (requires msg.value for Pyth fee) |
clearForwardRound(pairId, fixingTimestamp) | Clear matured forward storage | Yes (SSTORE deletion refund) |
clearMaturedForwards(pairId) | Clear all matured forward rounds | Yes |
checkOracleAndPause() | Check oracle validity, auto-pause if invalid | No |
recordFixingPriceFromPyth requires sending ETH as msg.value to cover the Pyth oracle update fee. The exact fee can be queried from the Pyth contract’s getUpdateFee() function.Gas Cost Responsibility
The current M2 design does not include direct economic incentives for keeper operators. This is intentional for the testnet phase.- Settlement and liquidation callers pay gas but receive no direct reward
- Liquidation penalty goes to fee destinations (treasury + pool), not to the liquidator
- Forward clearing provides SSTORE refunds — deleting storage slots returns a gas rebate per EIP-3529
- Oracle fixing requires the Pyth update fee as
msg.valuein addition to gas costs
Position-Aware Oracle Cleanup
Forward price data for matured timestamps is no longer needed once all positions at that fixing time are settled. The cleanup functions include safety checks:-
clearForwardRound(pairId, fixingTimestamp)checkspositionManager.openPositionCountAtFixingTs(pairId, fixingTimestamp). If any open positions still reference that fixing timestamp, the clear is silently skipped (not reverted). This prevents clearing forward data that is still needed for open position PnL calculations. -
clearMaturedForwards(pairId)resets safeguard baselines (spacing, move limit tracking) after clearing, ensuring fresh publish cycles start clean. This is useful for batch cleanup after all positions at multiple fixing timestamps have been settled.
In M2, the keeper is operated by the protocol team and there are no direct keeper incentives. M3 plans include economic incentives (keeper rewards, MEV-aware settlement) to attract external keeper operators.
Related Pages
Publisher Operations
Forward price publication and fixing
Oracle & Forward Pricing
Product-level pricing overview
Oracle Safeguards
Defense-in-depth oracle protection