Two functions and you are automated. Prefer an agent-driven flow? The complete zero-config rulefile (interface, rules, Foundry test template, deploy scripts) is downloadable: SKILL.md - drop it into .cursor/rules/, .claude/skills/, or your repo root.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface IAutoJob {
/// @dev MUST NOT mutate state. Returns true only when performJob is ready.
function checkJob() external view returns (bool canExec, bytes memory execPayload);
/// @dev MUST be restricted to the PeekRegistry. MUST flip checkJob to
/// false for the next block.
function performJob(bytes calldata execPayload) external;
}Rules that matter: checkJob is view-only and cheap; performJob is restricted to the registry, writes its "already done" state BEFORE external calls, and is idempotent so a retry can never double-execute.
Reference pattern for ve(3,3) epoch maintenance (Pharaoh-style voter.distribute() / gauge.harvest()):
contract EpochKeeper is IAutoJob {
address public immutable REGISTRY_ADDRESS;
IVoter public immutable voter;
uint256 public nextEpoch;
error NotRegistry();
constructor(address registry, address voter_, uint256 firstEpoch) {
REGISTRY_ADDRESS = registry;
voter = IVoter(voter_);
nextEpoch = firstEpoch;
}
function checkJob() external view returns (bool, bytes memory) {
return (block.timestamp >= nextEpoch, "");
}
function performJob(bytes calldata) external {
if (msg.sender != REGISTRY_ADDRESS) revert NotRegistry();
uint256 ts = nextEpoch;
if (block.timestamp < ts) return; // idempotent within an epoch
nextEpoch = ts + 1 weeks; // effect first
bool ok;
try voter.distribute() {
ok = true; // recorded; a failed leg retries next epoch
} catch {
ok = false; // never roll back the epoch
}
}
}# register with a 0.2 AVAX escrow via the CLI peekpeak register 43113 0xYourKeeper \ --gas-limit 500000 --max-gas-price-gwei 100 \ --deposit-wei 200000000000000000 --keystore executor.json # or with Foundry DEPLOYER_KEY=0x.. REGISTRY=0x.. TARGET=0x.. GAS_LIMIT=500000 \ MAX_GAS_PRICE_GWEI=100 DEPOSIT_WEI=200000000000000000 \ forge script script/RegisterJob.s.sol:RegisterJob \ --rpc-url $FUJI_RPC --broadcast
Ship the three required Foundry tests (state flip, mock-registry execution, non-registry revert) - the templates are in SKILL.md.
| Call | Who | Effect |
|---|---|---|
registerJob(target, gasLimit, maxGasPriceGwei) payable | you | creates the job, funds escrow |
depositGas(jobId) payable | anyone | tops up escrow |
withdrawGas(jobId, amount) | job owner | drains escrow |
setJobStatus(jobId, bool) | job owner | pause / resume |
executeJob(jobId, payload) | authorized executor only | runs performJob; charges escrow exactly gasUsed x gasPrice + 0.005 AVAX flat tip |
A reverting performJob never charges your escrow; three consecutive reverts auto-deactivate the job.
worker/README.md in the repo