Skip to content

How It Works

What this document covers

The actual pipeline implemented in this repo today — from both the operator/foundation perspective and the granted seat user perspective. Everything here reflects current code paths in src/ and the current live operating model.


0. Prerequisites — What Must Be True First

Seat Manager works correctly only when all of these conditions are met.

Repository & Network

Condition Requirement
Repo layout centurion-infra and centurion-networks exist side-by-side under /home/user/centurion/
Canonical files deploy-node.sh, network.yaml, and deposit_contract_fingerprint.json must be readable
Network connectivity At least 2 endpoint labels must be reachable for cross-check preflight
RPC access Direct or SSH tunnel, depending on SEAT_MANAGER_RPC_MODE

Chain & Contract State

Condition Requirement
Chain ID Must match canonical chain_id
Contract code Must exist at canonical address
Runtime hash Must match canonical fingerprint
On-chain owner() Must match canonical deposit_contract_owner

Key Material & Funding

Operation Key Required Funding Required
Propose-only commands None None
Owner admin send (approve, switch, freeze) SEAT_MANAGER_OWNER_PRIVATE_KEY Gas
Deposit send (seat deposit --send) SEAT_MANAGER_DEPOSITOR_PRIVATE_KEY (or owner fallback) Gas + deposit value

Contract State for Deposits

Condition Requirement
Seat status Must not be REVOKED
If allowlist is ON (pubkey, wc) intent must already be allowlisted
If owner-only depositor is ON Deposit sender must be owner
Amount Must map to integer gwei and satisfy contract bounds
Deposit data root Must match (pubkey, wc, signature, amount) used in tx

Data Requirements

Condition Requirement
Pubkey 48 bytes
Withdrawal credentials 32 bytes
Watcher promotion to ACTIVE Validator must be visible on at least 2 CL labels

Deposit Contract Feature Additions

These are the 4 key additions versus the baseline deposit contract, and how they must be set for Seat Manager operations:

# Feature Current Stage (Permissioned) Later Stage (Permissionless)
1 Intent allowlist gate (pubkey, wc) ENABLED (pubkeyAllowlistEnabled = true) DISABLED forever (pubkeyAllowlistDisabledForever = true)
2 Reversible allowlist switch AVAILABLE (must not be disabled forever) NOT AVAILABLE after permanent disable
3 Timelocked disable-forever path NOT EXECUTED in normal runs EXECUTED once, after timelock maturity
4 Owner-only depositor gate ENABLED by policy (ownerOnlyDepositorEnabled = true) DISABLED (ownerOnlyDepositorEnabled = false)

Current model requirements

For Seat Manager to work in the current permissioned model:

  • pubkeyAllowlistEnabled must be true
  • pubkeyAllowlistDisabledForever must be false
  • disableAllowlistNotBefore should be 0 (unless intentionally running a timelock exercise)
  • If ownerOnlyDepositorEnabled is true, the deposit signer must be the owner
  • If ownerOnlyDepositorEnabled is false, a non-owner depositor key can be used

1. Roles and Responsibilities

Role Responsibilities
Foundation / Operator (Seat Manager owner) Maintains Seat Manager DB, runs CLI/watchers, controls allowlist admin txs, sends deposit txs
Granted Seat User (candidate operator) Provides pubkey + wc intent to foundation, runs validator infrastructure, does not submit deposits in current flow
DepositContractCTN Enforces on-chain rules: allowlist gate, owner-only sender gate, deposit root/value checks
EL/CL Clients EL includes tx + event in chain; CL ingests deposit and exposes validator state

2. Canonical Inputs

Seat Manager reads canonical chain data from three sources:

Source What It Provides
centurion-infra/.../deploy-node.sh Endpoint labels + client mapping
centurion-networks/.../network.yaml Chain/contract/ports configuration
centurion-networks/.../deposit_contract_fingerprint.json Runtime hash + root expectations
Networks root resolution order
  1. centurionNetworksRoot in seat-manager.config.json (optional)
  2. CENTURION_NETWORKS_ROOT env var (optional)
  3. Sibling default: /home/user/centurion/centurion-networks
  4. Legacy fallback: <infraRoot>/networks (compatibility)

3. End-to-End Operator Pipeline

graph TD
    A["<b>1. seat create</b><br/>Validate pubkey (48B) + wc (32B)<br/>Compute intentHash<br/>Store as CREATED"] --> B
    B["<b>2. seat approve</b><br/>Cross-check preflight on 2+ labels<br/>Produce evidence bundle<br/>With --send: addAllowedDeposit<br/>Status → ALLOWLISTED"] --> C
    C["<b>3. seat deposit</b><br/>Parse amount, resolve signer<br/>Generate deposit data<br/>Simulate via eth_call<br/>With --send: send + verify event"] --> D
    D["<b>4. Watchers</b><br/>EL: DepositEvent → DEPOSITED<br/>CL: 1st label → SEEN_BY_CL<br/>CL: 2+ labels → ACTIVE"]

4. State Machine

stateDiagram-v2
    [*] --> CREATED : seat create

    CREATED --> ALLOWLISTED : seat approve --send
    CREATED --> REVOKED : operational revoke

    ALLOWLISTED --> DEPOSITED : seat deposit --send\n(successful tx + event)
    ALLOWLISTED --> REVOKED : removeAllowedDeposit

    DEPOSITED --> SEEN_BY_CL : CL visible on 1 label
    DEPOSITED --> REVOKED : operational only

    SEEN_BY_CL --> ACTIVE : CL visible on 2+ labels
    SEEN_BY_CL --> REVOKED : operational only

Post-deposit revoke

DEPOSITED, SEEN_BY_CL, and ACTIVE seats can only be revoked operationally — there is no on-chain mechanism to eject a validator via the deposit contract.


5. Deposit Send Path

What is actually checked when you run seat deposit:

Step Check
1 Seat exists and status is not REVOKED
2 Parse amount → wei → gwei (must be integer gwei)
3 Cross-check preflight on 2 labels
4 Resolve deposit signer (DEPOSITOR_KEY or OWNER_KEY fallback)
5 Resolve deposit data for seat + amount (deposit-cli generation + local cache)
6 Simulate with eth_call
7 If --simulate-only or no --send: stop here
8 Send tx and assert: receipt success, exactly 1 DepositEvent, field match, depositCount +1

Simulation flags

  • --simulate-only: Always stop after simulation
  • --expect-revert: Pass only if simulation reverts (useful for verifying blocked paths)

6. Operator Controls

Allowlist Switch

Command What It Does
switch on --send setPubkeyAllowlistEnabled(true)
switch off --send setPubkeyAllowlistEnabled(false)

Both require cross-check preflight on 2+ labels and refuse if disabledForever = true.

Freeze Pipeline

Command What It Does
freeze schedule --delay-seconds N Sets notBefore = block.timestamp + N
freeze cancel Clears pending schedule (notBefore = 0)
freeze execute --dangerous Irreversible — calls disablePubkeyAllowlistForever()

Freeze execute gates

Requires: --dangerous flag + CONFIRM_FREEZE=I_UNDERSTAND + matured timelock + cross-check preflight.


7. Owner-Only Depositor Mode

Current state

Seat Manager does not expose a CLI command to toggle ownerOnlyDepositorEnabled. This is enforced by the contract itself during deposit().

Effect on Seat Manager deposits:

  • If owner-only is ON and signer is not owner → revert (UnauthorizedDepositor)
  • If owner-only is ON and signer is owner → proceeds to normal checks

Truth Table (Contract Gate Logic)

Allowlist (A) Owner-Only (O) Intent Listed (I) Sender = Owner (S) Result
ON ON Yes Yes PASS
ON ON Yes No REVERT UnauthorizedDepositor
ON ON No Yes REVERT IntentNotAllowlisted
ON OFF Yes Any PASS
ON OFF No Any REVERT IntentNotAllowlisted
OFF ON Any Yes PASS
OFF ON Any No REVERT UnauthorizedDepositor
OFF OFF Any Any PASS

Don't-care fields

When a gate is OFF, its corresponding input doesn't matter — but other checks (value, root, length) can still revert.


8. Granted Seat User View

From the user's perspective, the flow is:

Step User Action What Happens
1 Submit pubkey + wc intent to foundation Foundation creates the seat
2 Wait for approval Foundation sends addAllowedDeposit on-chain
3 Wait for deposit Foundation sends the deposit tx
4 Run validator and monitor CL visibility → SEEN_BY_CL → ACTIVE

Current permissioned flow

The granted user is not the transaction sender for deposit. The user cannot bypass contract gates with local actions.


9. Evidence and Audit Artifacts

Artifact Location Contents
Evidence bundles ./evidence/*.json Canonical snapshot, preflight summaries, calldata, tx hash/status/block
SQLite database ./data/seat-manager.sqlite Seat lifecycle, deposit events, CL observations, audit log

10. Watchers Pipeline

graph LR
    subgraph EL["EL Watcher (12s poll)"]
        EL1[Fetch DepositEvent logs] --> EL2[Decode event fields]
        EL2 --> EL3[Record deposit row]
        EL3 --> EL4["Status → DEPOSITED"]
    end

    subgraph CL["CL Watcher (24s poll)"]
        CL1[Query /validators/pubkey] --> CL2[Record CL observation]
        CL2 --> CL3["1st label → SEEN_BY_CL"]
        CL3 --> CL4["2+ labels → ACTIVE"]
    end

Both watchers use 2+ labels with distinct clients for diversity, bounded exponential backoff on errors, and graceful SIGINT/SIGTERM handling.


11. Failure Modes

Failure What Happens
Cross-check preflight fails Chain/fingerprint/owner mismatch or endpoint unavailable — operation aborts
Simulation reverts Allowlist missing, owner-only sender mismatch, bad deposit data, or bad amount
CL visibility delay Validator may not appear immediately after EL success due to CL ingestion timing
Operational revoke after deposit Seat marked REVOKED in DB, but on-chain deposit is irreversible

12. Practical Run Sequence

# 1. Health check
seat-manager status

# 2. Create a seat
seat-manager seat create --operator <id> --pubkey <48B> --wc <32B>

# 3. Approve (allowlist on-chain)
seat-manager seat approve <id> --send

# 4. Deposit
seat-manager seat deposit <id> --amount-ctn 32 --send

# 5. Watch for activation
seat-manager watch

# 6. Review
seat-manager seat list

Dry-run options

  • Add --simulate-only to test without sending
  • Add --expect-revert to verify a blocked path

13. Permissionless Transition

To reach truly permissionless behavior at the contract level:

Setting Current (Permissioned) Target (Permissionless)
pubkeyAllowlistDisabledForever false true
ownerOnlyDepositorEnabled true false

Seat Manager already handles the allowlist switch/freeze pipeline. Owner-only toggling is currently external to this CLI.


14. Staking Launchpad (Current Plan)

Current position

The launchpad is not the production deposit path in this permissioned stage. Seat Manager remains the operational system of record for onboarding and deposits.

What we are doing now:

  • Keep launchpad tested against mainnet-fork and contract behavior
  • Do not use launchpad for live onboarding while owner-only/permissioned controls are active

Cutover Plan

graph TD
    A["<b>Managed Phase</b><br/>Seat Manager controls approvals + deposits<br/>ownerOnlyDepositorEnabled = true<br/>pubkeyAllowlistEnabled = true"] --> B
    B["<b>Permissionless Cutover</b><br/>ownerOnlyDepositorEnabled → false<br/>Schedule disable forever → wait timelock → execute<br/>pubkeyAllowlistDisabledForever = true"] --> C
    C["<b>Launchpad Primary Path</b><br/>Public deposits allowed by contract state"]