Keeper
ImplementedA permissionless caller that can start a sync and cannot choose what it does.
#What a keeper controls
Two things: when a sync happens, and which asset is evaluated.
That is the whole surface. sync(token) takes one token address. The order the contract builds from it has no calldata field and no recipient field — the recipient is forced to the calling vault — so there is nowhere to express "send the proceeds elsewhere".
Security. A keeper key controls no user funds. It pays gas and may earn a capped bounty. If a keeper key were stolen, the attacker's entire capability would be: calling a public function, at a time of their choosing, that does what the contract decided anyway.
#Candidate discovery
A keeper sweeps candidate assets each round. The candidate set is the union of the vault's tracked assets and the assets the source currently holds, minus the base asset.
Tracked assets alone are not enough — a vault with no positions tracks nothing, so a keeper sweeping only tracked assets could never open a first position. That was a real bug: vaults sat empty because the keeper had nothing to iterate over.
#Failure reporting
The reference keeper logs every outcome individually: successes, failures, gas-too-high, and unprofitable. An earlier version logged only successes, which meant a keeper doing nothing at all looked exactly like a keeper with nothing to do.
#Chain assertion
The keeper asserts the configured chain ID at startup and refuses to run against a mismatch. This immediately caught a local Anvil node reporting 31337 while the configuration said 46630 — a class of mistake that otherwise produces confusing failures much later.
#Configuration
| Setting | Purpose |
|---|---|
KEEPER_PRIVATE_KEY | Signs sync transactions. Controls no user funds. |
KEEPER_DRY_RUN | Simulate and log without sending |
KEEPER_MAX_GAS_PRICE_GWEI | Skip rounds above this gas price |
KEEPER_POLL_INTERVAL_MS | Sweep cadence |
#Liveness is a real dependency
Mirroring is autonomous in the sense that nobody can direct it. It is not autonomous in the sense of happening without anyone. If no keeper calls sync, a vault stops mirroring and drifts from its source — visibly, since divergence is shown with its reason, but it drifts.
Keeper liveness is named as a residual risk in Known limitations.