Skip to main content

Permissionless Operations

Olla is designed so that anyone can keep the protocol running. Three core operations (rebalancing, accounting, and attester state refresh) are callable by any Ethereum address. There is no privileged operator role required.

This means the protocol doesn't depend on a single party being online. If the Olla team's infrastructure goes down, any user or community member can step in and call these functions to keep things moving.

What needs to happen and why

The protocol doesn't update itself automatically. Someone needs to trigger each step by sending a transaction. Here's what each operation does:

Rebalance

What it does: Moves assets between the vault buffer and the Aztec rollup.

Think of the buffer as a checking account and the rollup as a savings account. Deposits land in the checking account. Rebalance moves surplus to savings (staking), or pulls from savings when users want to withdraw.

A rebalance cycle runs five steps:

  1. Collect any earned rewards from the rollup.
  2. Pull back any funds that finished unstaking.
  3. Pay out queued withdrawal requests.
  4. Start unstaking more if needed to cover pending withdrawals.
  5. Stake any surplus buffer into the rollup.

How often: Rate-limited by a cooldown (configurable between 10 minutes and 24 hours). You can only start a new cycle after the cooldown elapses. If a cycle is already in progress, anyone can continue it without waiting.

How to call it:

cast send <OllaCore> "rebalance()" --private-key <your_key> --rpc-url <rpc>

Update Accounting

What it does: Recalculates the exchange rate between Aztec and stAztec.

After a rebalance, the protocol needs to account for any rewards earned and any slashing that occurred. This updates the exchange rate, mints protocol fee shares, and runs safety checks (circuit breakers).

When to call it: After a rebalance cycle completes (step = Done). Can also be called independently to refresh the rate.

How to call it:

cast send <OllaCore> "updateAccounting()" --private-key <your_key> --rpc-url <rpc>

Refresh Attester State

What it does: Syncs the protocol's view of each attester (validator) with the Aztec rollup's actual state.

Attesters can be slashed, exit voluntarily, or get stuck in queues. This function reads the current state from the rollup and updates the protocol's records: detecting slashing, finalizing completed exits, and promoting queued attesters to active.

When to call it: Before a rebalance, or whenever you suspect attester states are stale (e.g., after a slashing event on the rollup). Takes an array of attester addresses.

How to call it:

cast send <StakingManager> "refreshAttesterState(address[])" "[0xAttester1,0xAttester2]" --private-key <your_key> --rpc-url <rpc>

Purge Failed Queue Entry

What it does: Cleans up an attester that was queued for staking but failed to activate on the rollup (e.g., due to an invalid BLS proof or duplicate key).

This is a rare edge case. If it happens, the attester gets stuck as "Queued" in the protocol with inflated staked amounts. This function detects that the attester is no longer in the rollup's entry queue and corrects the accounting.

How to call it:

cast send <StakingManager> "purgeFailedQueueEntry(address)" <attester_address> --private-key <your_key> --rpc-url <rpc>

The Butler

Olla runs an automated service called the Butler that handles these operations continuously. It scrapes on-chain state, watches events, exposes metrics, and (when configured with a key) sends transactions.

Automated transactions

OperationCheck intervalTrigger condition
updateAccounting()5 minLast report older than 4 hours
rebalance()15 minContract cooldown gates new cycles. Mid-cycle continuation has no cooldown, and the Butler keeps calling rebalance() until the state machine reaches Done.
refreshAttesterState(...)60sStale, drifted, exitable, slashed, or zombie attesters detected. Runs in batches with a 5 minute per-attester cooldown.
flushEntryQueue() (rollup)60sOlla has queued attesters AND the rollup's per-epoch flush slot is available.
Executor ETH balance scrape5 minAlways (used for the low-balance alert).

Reverts that match expected operational conditions (OllaCore__RebalanceInProgress, OllaCore__RebalanceCooldownActive, Rollup__RewardsNotClaimable) are recognised and don't escalate to errors. Unexpected reverts are surfaced with the decoded selector.

Event monitoring

The event watcher polls every 12 s and processes events from every Olla contract plus the rollup. State is persisted to disk every 10 cycles and on shutdown, so on restart it resumes from the last processed block (no lost events). The watcher tracks:

  • Operational counters for every event class (deposits, redeem requests, withdrawal claims, rebalance / accounting cycles, staking, unstaking, rewards harvested, attester refresh, failed-queue purges).
  • Safety events: CircuitBreakerTriggered (with reason: rate drop, queue ratio, accounting stale), Paused / Unpaused, NegativeRewardsPeriod, RebalanceReset, WithdrawalAdjusted (slashing).
  • Governance log: every *Updated config-change event with old/new values, plus pause / unpause and ERC-1967 Upgraded events on every UUPS proxy.
  • Block timestamps for accurate event timing.

A separate WebSocket listener (opt-in via ETHEREUM_NODE_WS_URL) subscribes to the canonical Aztec rollup for ValidatorQueued, Deposit, FailedDeposit, WithdrawInitiated, WithdrawFinalized, and Slashed, and triggers an attester refresh on each.

HTTP endpoints

EndpointAuthWhat it returns
/metricsOptional bearer token (METRICS_BEARER_TOKEN)Prometheus gauges and counters, all prefixed olla_butler_ and labelled with network
/eventsNone (read-only)Last ~200 protocol events, optionally filtered with ?network=...
/governanceNone (read-only)Recent governance and upgrade events, optionally filtered with ?network=...&category=...
/health (and /healthz)None{"status":"ok"} for liveness/readiness probes

Prometheus alerting rules ship in monitoring/alerts.yml. Critical alerts include circuit-breaker fires, slashing, safety-module pauses, exchange-rate drops > 10 bps, and slashed/zombie attesters. Warnings cover overdue rebalances, stale accounting, low buffer utilisation, scraper failures, RPC unreachable, low key queue, and attester state drift.

Running the Butler yourself

The Butler is open source at ollafinance/olla-butler. Anyone can run their own instance:

# Install
git clone https://github.com/ollafinance/olla-butler
cd olla-butler
npm install
npm run build

# Configure (one file per network — testnet-base.env, mainnet-base.env, etc.)
mkdir -p ~/.config/olla-butler
cat > ~/.config/olla-butler/mainnet-base.env << 'EOF'
ETHEREUM_CHAIN_ID=1
ETHEREUM_NODE_URL=https://primary-rpc.com,https://fallback-rpc.com
OLLA_CORE_ADDRESS=0x...

# Optional: WebSocket subscription to rollup attester events
ETHEREUM_NODE_WS_URL=wss://your-rpc-endpoint.com

# Optional: opt-in attester monitoring (set to the block staking events begin)
ATTESTER_SCAN_START_BLOCK=1234567

# Optional: Prometheus auth
METRICS_PORT=9470
METRICS_BEARER_TOKEN=your-secret-token

# Optional: automated operations (requires a funded wallet)
TX_EXECUTOR_ENABLED=true
BUTLER_PRIVATE_KEY=0x...
EOF

# Run
npm run start:serve -- mainnet

ETHEREUM_NODE_URL accepts a comma-separated list; the Butler fails over automatically.

In monitoring-only mode (omit TX_EXECUTOR_ENABLED and BUTLER_PRIVATE_KEY), the Butler scrapes state, watches events, and exposes metrics + endpoints without sending any transactions.

Why you should participate

Even though the Butler handles operations automatically, there are good reasons for community members to run their own callers:

  • Redundancy: If Olla's Butler goes offline, the protocol keeps running as long as someone calls these functions.
  • Decentralization: The more independent callers, the less the protocol depends on any single party.
  • Transparency: Running your own monitoring lets you verify the protocol's health independently.

You don't need special permissions, roles, or approval. Just an Ethereum wallet with enough ETH for gas.