Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quickstart — bind many citizens from one agent

audience: integrators

This quickstart stands up an agent that submits and reads across three block-building lattices, one standalone attestation organism, one confluence, and one basic service (the gov’s Atlas) — all bound through a single GovConfig. It assumes familiarity with the builder integrators quickstart when the gov includes block-building lattices; the gov layer extends that pattern rather than replacing it.

If the gov contains no lattices (for example, a composition of oracle organisms plus a cross-oracle confluence), the same pattern applies with different crates on the right-hand side of each handle.

What you need before you start

  • A GovConfig from the gov operator (struct definition, or a serialised fingerprint you copy into your crate).
  • A LatticeConfig per referenced lattice (inline in the GovConfig the operator published, or pulled from each lattice operator’s handshake page).
  • A standalone-organism reference per referenced organism (role name + Config fingerprint + pointer to the organism’s own handshake).
  • A ticket from each per-citizen operator you intend to write to. Per-citizen admission is per-citizen; the gov operator does not issue per-citizen tickets.
  • A ticket from each confluence or basic-service operator whose write-side primitive you intend to use. Usually none: most confluences and basic services are read-only for external integrators.
  • Your TDX image (if any citizen or service you write to requires attested client admission).

Step 1 — declare the GovConfig in your crate

Paste the operator’s published GovConfig definition, or import it from the operator’s handshake crate if they publish one:

use builder::LatticeConfig;
use gov::{GovConfig, ConfluenceConfig, OrganismRef};

// Exactly the bytes the operator publishes.
const ETH_MAINNET:      LatticeConfig = /* from ethereum.mainnet operator */;
const UNICHAIN_MAINNET: LatticeConfig = /* from unichain.mainnet operator */;
const BASE_MAINNET:     LatticeConfig = /* from base.mainnet operator */;

// A standalone organism the gov references — here, an
// attestation aggregator with its own Config fingerprint.
const ATTEST_AGG: OrganismRef = OrganismRef {
    role: "attest",
    fingerprint: /* published by the attest-aggregator operator */,
};

// The gov's Atlas basic service (if shipped).
const SUPERCHAIN_ATLAS:  ConfluenceConfig = /* from gov operator */;
const INTENTS_CONF:      ConfluenceConfig = /* from gov operator */;
const LEDGER_CONF:       ConfluenceConfig = /* from gov operator */;

pub const ETH_SUPERCHAIN: GovConfig = GovConfig {
    name: "ethereum.superchain",
    lattices:    &[ETH_MAINNET, UNICHAIN_MAINNET, BASE_MAINNET],
    organisms:   &[ATTEST_AGG],
    confluences: &[SUPERCHAIN_ATLAS, INTENTS_CONF, LEDGER_CONF],
};

For a gov-agnostic library, take &'static GovConfig as a constructor argument and let the binary crate pick the constant.

Step 2 — bring up the network handle

Same UNIVERSE as every other mosaik service:

use std::sync::Arc;
use mosaik::Network;
use builder::UNIVERSE;

let network = Arc::new(Network::new(UNIVERSE).await?);

One Arc<Network> per agent regardless of how many citizens, confluences, or basic services are bound.

Step 3 — bind the lattice handles you need

Indexes into lattices[] match the order in the GovConfig. A cross-chain searcher that bids on all three:

use offer::Offer;

let eth_bid  = Offer::<Bundle>::bid (&network, &ETH_SUPERCHAIN.lattices[0].offer).await?;
let uni_bid  = Offer::<Bundle>::bid (&network, &ETH_SUPERCHAIN.lattices[1].offer).await?;
let base_bid = Offer::<Bundle>::bid (&network, &ETH_SUPERCHAIN.lattices[2].offer).await?;

let eth_wins  = Offer::<Bundle>::outcomes(&network, &ETH_SUPERCHAIN.lattices[0].offer).await?;
let uni_wins  = Offer::<Bundle>::outcomes(&network, &ETH_SUPERCHAIN.lattices[1].offer).await?;
let base_wins = Offer::<Bundle>::outcomes(&network, &ETH_SUPERCHAIN.lattices[2].offer).await?;

Keep handles alive while needed; drop them when done. Each handle carries its own subscriptions and is cheap atop the shared Network.

Step 4 — bind the standalone-organism handles you need

Each OrganismRef carries the fingerprint; the concrete organism crate gives you the typed constructor:

use attest::Aggregator;

let attest = Aggregator::<Quote>::read(
    &network, ETH_SUPERCHAIN.organisms[0].config(),
).await?;

OrganismRef::config() resolves to the organism’s Config — either because the gov operator embeds the full Config struct in their published GovConfig, or because the organism crate ships a helper that rebuilds the Config from the published fingerprint and known role constants. Either way, the concrete organism crate is what gives you the typed handle.

Step 5 — bind the basic services you want

Basic services are confluences with well-known shapes. GovConfig exposes helpers:

use atlas::Atlas;
use almanac::Almanac;

// The Atlas lists every citizen in the gov with operator metadata.
if let Some(atlas_cfg) = ETH_SUPERCHAIN.atlas() {
    let atlas = Atlas::<CitizenCard>::read(&network, atlas_cfg).await?;
    // Use the atlas as your canonical "what's in this gov" reference.
}

// The Almanac is a shared tick beacon for timing correlations.
if let Some(almanac_cfg) = ETH_SUPERCHAIN.almanac() {
    let almanac = Almanac::<Tick>::read(&network, almanac_cfg).await?;
    // Use almanac ticks as the time axis for cross-citizen commits.
}

A gov may ship zero, one, two, three, or all four basic services. Always check the helper’s Option return; do not assume presence.

Step 6 — bind the confluence handles you need

The shared ledger is typical for a searcher seeking aggregated refund attribution:

use ledger::Shared;

let refunds = Shared::<Refund>::read(&network, &ETH_SUPERCHAIN.confluences[2]).await?;

tokio::spawn(async move {
    while let Some(r) = refunds.next().await {
        // Each entry carries (origin_citizen, slot, share, evidence).
        handle_refund(r);
    }
});

Step 7 — submit paired bundles

Cross-chain bundles are a searcher-level problem. The gov layer surfaces the handles; reconciliation logic is the integrator’s responsibility.

async fn submit_paired_bundle(
    eth_bid: &Offer<Bundle>::Bid,
    uni_bid: &Offer<Bundle>::Bid,
    sell_on_eth: Bundle,
    buy_on_uni:  Bundle,
) -> anyhow::Result<()> {
    let eth_ack = eth_bid.send(sell_on_eth).await?;
    let uni_ack = uni_bid.send(buy_on_uni ).await?;
    // Reconcile: if only one leg commits, post-bond collateral or
    // on-chain HTLC logic applies. The gov layer does NOT
    // provide cross-citizen atomicity. See
    // /contributors/atomicity.md.
    Ok(())
}

A paired-bundle correlator joining AuctionOutcome[L1, S] to AuctionOutcome[L2, S'] is the most common in-agent utility. Compute it in-memory as in builder’s Shape 1, or rely on the gov’s intents confluence if one is commissioned.

Step 8 — read the aggregated result

With a shared-ledger confluence, a single subscription surfaces refunds originating from any of the gov’s lattices.

while let Some(entry) = refunds.next().await {
    tracing::info!(
        origin = %entry.origin_citizen,
        slot   = entry.slot,
        share  = %entry.share,
        "refund attributed",
    );
}

Without a shared-ledger confluence, open three per- lattice tally::Tally::<Attribution>::read handles and join in memory. Both paths are valid; the confluence is a shared precomputation.

What to do when things go wrong

  • ConnectTimeout on a lattice or organism handle. The per-citizen Config does not match what the citizen’s operator is running. Recompile against that operator’s latest handshake.
  • ConnectTimeout on a confluence or basic-service handle. The ConfluenceConfig does not match, or the committee is down and has not yet published peers on the universe, or the confluence has been retired and the gov operator should have sent an updated GovConfig.
  • Admission denied on a write-side stream. Ticket missing or expired. Contact the per-citizen operator, not the gov operator.
  • Basic-service helper returns None. The gov does not ship that service. See contributor basic services for the spec if proposing addition.

Cross-references