Operating on-chain staking

The consoles, the doors a person holds, the alerts, the monthly statements, and the drills to run before the first customer stakes.

7 min read

Operating on-chain staking

Everything that moves coins runs on the scheduled tasks. What a person holds is a small set of doors, each of which refuses by name what it cannot do safely. This page is the runbook for those doors, and the drills to run on a test network before the first customer stakes.

The consoles

  • Overview (Staking → Overview, in REAL mode) — per wallet: the native balance the reconciler last read, the gas reserve floor, and the book value the pools on that key say is delegated. Per pool: on-chain value, share price, unallocated value, holders by state, the observer's last read and whether it lags, the validator set's policy verdict, the commission held as treasury shares and what of it has been realised, and the unbonding ladder — what is due back, by day, with anything past the promised bound marked. Open incidents and in-flight batches sit at the bottom. Refreshes every minute while the tab is visible; reads only.
  • Pools — the form has no rate, lock, capacity, early fee, cadence or tiers for an on-chain pool. You choose an activated chain; the venue, the wallet, the validator set, the slashing policy and the commission notice come from the activation and are shown read-only. A pool's detail page gains an On-chain tab: the commission ledger with Observe now and Unstake commission, the tranches (stake accounts or Lido shares) with their validator and observed value, and the observations that moved the share price.
  • Earnings (in REAL mode) — the commission ledger for every pool. Nothing here is distributed or typed: it is what the network paid, the share the disclosure said the platform keeps, and what of that has reached the platform's own address.
  • Incidents — as before, plus Reimburse… on a slashing incident.
  • Compliance — the activation record each chain was opened under and the rules it derived, every consent recorded at the stake door, every monthly statement written, and CSV exports of consents, activations and observations. One write: Write missing statements for a period ahead of the nightly run.

The commission exit

Commission is minted as treasury shares on the pool at every observation with a positive gross reward, so it rises and falls with every holder's shares and is slashed with them. Nothing is booked as revenue until the coins reach the platform's own address:

  1. Unstake commission queues N treasury shares (default: all). At most one open request per pool; refused while the staking wallet is frozen, and refused when the chain has no ecosystem master wallet to pay to.
  2. The engine's next exit batch covers the queued value together with the users' exits, users first in FIFO order.
  3. When the coins are back, settlement burns the shares from the treasury at the lower of the request price and the current price — after every user exit ahead of it has been paid. If the users ahead of it starve the unallocated value, the commission waits for the next claim.
  4. The engine transfers the settled amount from the staking wallet to the chain's ecosystem master wallet. The firewall allows that one destination and nothing else; a frozen wallet does not sign it and the row waits.

The row walks QUEUED → UNBONDING → SETTLED → PAID and is the realised ledger; the accrual rows the observer writes are the accrued ledger. The two are shown side by side.

The slashing reimbursement

A validator penalty reduces the pool's value for every holder. What an operator may promise on top is declared at activation: PASS_THROUGH, or REIMBURSE_CAPPED with a cap per incident. Reimburse… on a SLASHING incident pays that promise and nothing beyond it:

  • at most the cap per incident, cumulatively across calls, never more than the recorded loss; in parts if you prefer, each part keyed on what was already paid so a retried request cannot pay twice;
  • pro rata to the holders by their shares (a queued exit's shares still took the loss, so they count);
  • from the Super Admin's ECO wallet for the pool's currency, which must hold the amount — a reimbursement the treasury cannot fund is refused with the shortfall named, never booked as a credit the platform does not have;
  • the incident records the amount and resolves itself when the cap is reached.

Nothing on-chain moves. Each holder is notified with the amount and the incident.

Alerts

The hourly reconciler raises what can be checked against the chain: gas reserve, share-ledger and value drift, observer lag, stuck batches, validator policy. The ten-minute alerts job raises what is a matter of time on the platform's own rows:

Kind When Severity
UNBONDING_OVERDUE an exit is past the bound the pool promised warning
DELEGATION_STALE a stake is still pending delegation past twice the activation delay plus a day warning
COMMISSION a commission exit has been settled for a day and not paid, or its chain has no master wallet warning

Every alert dedupes on its key while open, clears itself when the condition ends, and never blocks an exit. New incidents notify every admin who can see them.

Statements

Once a day the statements job writes the previous month's statement for every user who held an on-chain position in it: staked in the period, exited in the period, rewards net of commission, the commission attributed to them (each earning row's net × the observation's commission ÷ net, which is exact under the pro-rata rule the observer applies), and the value of their shares at the time of writing, labelled as such. One CSV per user and month, hashed, never regenerated. Users download theirs from Staking → Statements; the compliance console lists them all and can write a period early.

The liquid exit, and why it is off

An Ethereum pool can be set to sell its stETH on Curve's stETH/ETH pool rather than joining Lido's withdrawal queue, which releases an exit in minutes instead of days. It is off on every pool and you turn it on per pool, with a slippage cap in basis points.

What the cap does: the engine asks the venue what it would pay before it commits. If that price is worse than your cap against the protocol price, it does not sell — the exit joins the withdrawal queue as usual. The difference between the market price and the protocol price is borne by the holder who is leaving, never by the holders who stay, and the pool's disclosure states the cap as a number so they know it before they stake.

What protects it: the platform builds the swap itself against one named contract, and the firewall decodes every argument back out before the key signs — the pool address, the direction, the exact amount, and a minimum-out floor computed from your cap before any price is read. No aggregator is involved. A payload the firewall cannot read in full is refused rather than signed.

Before you enable it on a pool holding customer money: there is no test network for this path. Hoodi, where the rest of the Ethereum engine is rehearsed, has no Curve deployment. Turn it on for a pool with a small position first, watch one exit through end to end, and only then leave it on at scale.

Drills

Run these on a test network (Solana devnet or a local validator; Lido on Hoodi) with a funded staking wallet, before the first customer stakes, and again after any change to the engine.

  1. Kill switch — pause the chain under Chains. Confirm the stake door refuses with the pause named, and that a queued exit still deactivates, claims and returns on schedule. Resume.
  2. Freeze — freeze the staking wallet with a reason. Confirm a stake is refused, a delegation batch waits, and an exit, claim, return and refund still sign. Confirm a commission payout waits. Unfreeze; confirm the waiting batches land.
  3. Force-unstake — force-unstake a test position with a reason. Confirm the holder is notified with the reason, the row walks UNSTAKE_REQUESTED → UNBONDING → WITHDRAWABLE → COMPLETED, and the credit carries the return transaction hash.
  4. Commission — with treasury shares accrued, queue a commission exit. Confirm it settles after the test positions' exits, that the share invariant reconciles, and that the payout lands in the master wallet with the row PAID.
  5. Slashing — on a REIMBURSE_CAPPED activation, open a SLASHING incident by hand (or wait for a negative observation on the test network), fund the Super Admin's ECO wallet, and reimburse. Confirm the pro-rata credits, the cap, the refusal above the cap, and the refusal with an underfunded treasury.
  6. Observer lag — stop the observer job for longer than two windows. Confirm OBSERVER_LAG opens, that exits still run, and that it clears when the job resumes.
  7. Overdue exit — set a test position's unbondingBoundAt in the past. Confirm UNBONDING_OVERDUE opens and clears when the position settles.
  8. Statements — write a period from the compliance console. Confirm one row per test user, the CSV's hash matches its content, and a second run writes nothing.
  9. Restore — with the database restored from the last backup and the staking wallet's key material intact, confirm the reconciler reports the on-chain state against the book with no drift incident, and that in-flight batches settle from their recorded signatures without a rebuild.

Record the date and the outcome of each drill in the activation's validatorDueDiligence notes or your own change log.