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

Receiving refunds and attributions

audience: integrators

Refund accounting is the tally organism’s job. Tally commits one Refunds[S] entry per slot once the chain has included the lattice’s winning block, and publishes an Attestations[S] entry that is presentable to an on-chain settlement contract for independent verification.

Open the handles

use tally::Tally;

let mut refunds      = Tally::<Attribution>::read        (&network, &ETH_MAINNET.tally).await?;
let mut attestations = Tally::<Attestation>::attestations(&network, &ETH_MAINNET.tally).await?;

Both are read-only Streams and world-readable by any lattice ticket holder. Attestations are specifically designed to be safe to publish — they carry no cleartext sender identity, only on-chain-presentable signatures over commitment-hash inputs.

What an Attribution carries

pub struct Attribution {
    pub slot:       u64,
    pub block_hash: [u8; 32],            // included on chain
    pub recipients: Vec<Recipient>,       // who gets paid what
    pub evidence:   Evidence,            // refs into upstream commits
}

pub struct Recipient {
    pub addr:   [u8; 20],
    pub amount: u128,       // in chain native units
    pub kind:   RecipientKind,
}

pub enum RecipientKind {
    /// Wallet whose zipnet submission made it into the block.
    OrderflowProvider { submission: SubmissionRef },
    /// Searcher whose offer bid won the slot's auction.
    BidWinner         { bid:        BidRef        },
    /// Co-builder operator whose atelier hint made it into the block.
    CoBuilder         { member:     BlsPub        },
}

SubmissionRef, BidRef, and the BlsPub committee member are all opaque handles into the lattice’s upstream commit logs. Recipients prove their claim by matching one of these references against their local record (your wallet’s SubmissionId from zipnet; your searcher’s BidId from offer; your co-builder’s public key).

Filtering attributions concerning you

A wallet or searcher typically only cares about attributions referencing their own prior activity:

use futures::StreamExt;

while let Some(attr) = refunds.next().await {
    for recipient in &attr.recipients {
        if recipient.addr == self_addr {
            println!("slot {} block {:x?}: {} wei for {:?}",
                     attr.slot, attr.block_hash, recipient.amount, recipient.kind);
        }
    }
}

There is no server-side filter in v0; agents scan every attribution and match locally. For a high-volume analytics use case, index attributions into your own store keyed by recipient address.

Claiming on-chain

An Attestation is the cryptographic proof you present to the lattice’s settlement contract to claim the refund:

pub struct Attestation {
    pub slot:        u64,
    pub block_hash:  [u8; 32],
    pub recipient:   [u8; 20],
    pub amount:      u128,
    pub kind_digest: [u8; 32],  // blake3 of the RecipientKind payload
    pub signatures:  Vec<(TallyMemberId, Secp256k1Sig)>,
}

Submit the Attestation to the settlement contract’s claim function. The contract verifies:

  • The signature set is at least t-of-n of the lattice’s tally committee (where t is pinned in the contract at deployment).
  • The signatures cover blake3(slot ‖ block_hash ‖ recipient ‖ amount ‖ kind_digest).
  • The block_hash matches an on-chain block at the given slot.

A tally committee that attempts to mis-attest — signs a claim for a block that was never included, or to a recipient that has no upstream reference — is rejected by the contract. The contract is the ground truth for claim validity; tally’s Refunds collection is the authoritative history.

Verifying the upstream evidence yourself

For wallets or searchers that do not want to trust tally’s attestation at face value, the evidence field in Attribution names the upstream commits. You read them out of the upstream organisms and reconstruct the attribution yourself:

use zipnet::Zipnet;
use offer::Offer;

let mut zipnet_reader = Zipnet::<Tx2718>::read(&network, &ETH_MAINNET.zipnet).await?;
let mut offer_outcomes = Offer::<Bundle>::outcomes(&network, &ETH_MAINNET.offer).await?;

// For each Attribution's evidence, go read the referenced slot in
// the upstream organism and check it matches.

This is work you only need to do if tally’s trust assumption (majority-honest) is inadequate for your use case. Most integrators treat Attestations as authoritative.

If your submission did not produce a refund

Not every submission produces a refund. Reasons:

  • Your zipnet envelope was a cover packet or a collision — nothing was in the winning block.
  • The winning block did not include your transaction (fee market, gas limit, etc.).
  • The lattice had a liveness failure for the slot (tally did not commit); the chain used a different builder.
  • The tally committee is majority-malicious and dropped your attribution. You escalate to the lattice operator; the on-chain record is your ground truth.

tally::Refunds[S] is an append-only collection. The absence of a recipient record for your address in a given slot is a negative fact — the lattice is saying “no refund for you on this slot”. Lattice operators SLA around liveness (percentage of slots where Refunds commits within a deadline); missed slots beyond the SLA are an operator concern, not a protocol one.

Next reading