ERC-8180: Blob Authenticated Messaging (BAM)

ERC-XXXX: Blob Authenticated Messaging (BAM)

Category: ERCs | Type: Standards Track | Status: Draft

Authors: Vitalik Buterin (@vbuterin), Skeletor Spaceman
(@skeletor-spaceman)

Abstract

This ERC defines interfaces for decentralized authenticated messaging over EIP-4844 blobs. The core
registration interface extends ERC-BSS (Blob Space Segments) with a decoder pointer and a signature
registry pointer: the decoder is an untrusted on-chain contract that extracts messages from
payloads; the signature registry is a trusted contract that verifies signatures. Separating decoding
from verification ensures that a buggy or malicious decoder cannot cause impersonation — it can only
produce wrong messages that fail signature verification against the trusted registry. Supporting
interfaces standardize multi-scheme key management (ECDSA, BLS, STARK, etc.) and on-chain message
proving.

The Problem

Ethereum can function as a general-purpose authenticated messaging medium. EIP-4844 blobs provide
128 KiB of data availability per blob at a fraction of calldata cost. With compression, a single
blob holds thousands of messages — at 9x compression, 498 million messages per day are achievable at
current blob throughput.

No standard exists for the on-chain interfaces. Each implementation defines its own:

  • Batch registration events (how indexers discover new message batches)
  • Message encoding (how clients decode batch payloads)
  • Key management contracts (how users register signing keys)
  • Proving mechanisms (how individual messages are proven on-chain)

Existing standards do not address this:

  • ERC-3722 (Poster): event-based minimal social contract,
    no blob awareness, stagnant
  • ERC-7847: NFT-based social media, no blobs
  • Farcaster/Lens: L2-specific, not standardized as ERCs

Without shared interfaces, indexers cannot track message submissions across implementations, clients
cannot decode messages from other protocols, wallets cannot manage signing keys across applications,
and smart contracts cannot react to authenticated messages from different protocols.

Design Principles

Anyone can read. A client with an Ethereum node and access to blob data should decode messages
from any compliant implementation. The batch registration event contains a decoder address pointing
to an on-chain contract that extracts messages from the payload. Proprietary encodings create
centralization pressure; discoverable on-chain decoders prevent it.

Capture-minimizing. No privileged decoders, registries, or gatekeepers. Decoder contracts are
permissionless to deploy. The decoder address is a per-submission parameter, not a global constant.
Different implementations use different decoders. Nothing prevents forking a decoder contract and
deploying a modified version.

Design Summary

Decoder Contracts (IERC_BAM_Decoder)

A decoder contract extracts messages and signature data from payloads. Given raw bytes from a blob
segment or calldata batch, it returns the decoded messages and the raw signature bytes. Decoders are
untrusted: a lying decoder produces wrong messages whose hashes fail verification against the
trusted registry. Decoders do not verify signatures.

interface IERC_BAM_Decoder {
    struct Message {
        address sender;
        uint64 nonce;
        bytes contents;
    }

    function decode(bytes calldata payload)
        external view returns (Message[] memory messages, bytes memory signatureData);
}

decode returns all messages (sender + nonce + contents) and opaque signature bytes (e.g.,
aggregated BLS signature or concatenated ECDSA signatures). The client then computes standardized
message hashes and verifies them against the trusted registry. This separation means a buggy decoder
can cause false negatives (valid messages rejected) but never false positives (forged messages
accepted).

v1 decoders should use simple encodings (ABI-encoded arrays, RLP, or SSZ). Complex compression
(dictionary-based, delta encoding) can be introduced in later decoder versions. The interface is
encoding-agnostic; a v2 decoder with on-chain decompression implements the same function.

Core Registration (IERC_BAM_Core extends IERC_BSS)

The core interface extends ERC-BSS (Blob Space Segments). registerBlobBatch declares the blob
segment and registers the batch with
its decoder and signature registry in one call:

interface IERC_BAM_Core is IERC_BSS {
    event BlobBatchRegistered(
        bytes32 indexed versionedHash,
        address indexed submitter,
        address indexed decoder,
        address signatureRegistry
    );
    event CalldataBatchRegistered(
        bytes32 indexed contentHash,
        address indexed submitter,
        address indexed decoder,
        address signatureRegistry
    );

    function registerBlobBatch(
        uint256 blobIndex,
        uint16 startFE,
        uint16 endFE,
        bytes32 contentTag,
        address decoder,
        address signatureRegistry
    ) external returns (bytes32 versionedHash);

    function registerCalldataBatch(
        bytes calldata batchData,
        address decoder,
        address signatureRegistry
    ) external returns (bytes32 contentHash);
}

registerBlobBatch internally calls declareBlobSegment (inherited from IERC_BSS), emitting
BlobSegmentDeclared for BSS indexers, then emits BlobBatchRegistered with the decoder and
signature registry addresses for BAM indexers. Two events, one call, unambiguous correlation between
segment and batch.

registerCalldataBatch allows self-publication without aggregators. Same decoder and signature
registry pointers; no segment declaration (calldata has no blob).

Signature Registry (IERC_BAM_SignatureRegistry)

Generic interface that works across ECDSA, BLS, STARK, and future schemes:

interface IERC_BAM_SignatureRegistry {
    event KeyRegistered(address indexed owner, bytes pubKey, uint256 index);

    function schemeId() external pure returns (uint8 id);
    function schemeName() external pure returns (string memory name);
    function register(bytes calldata pubKey, bytes calldata popProof)
        external returns (uint256 index);
    function getKey(address owner)
        external view returns (bytes memory pubKey);
    function isRegistered(address owner)
        external view returns (bool registered);
    function verify(bytes calldata pubKey, bytes32 messageHash, bytes calldata signature)
        external view returns (bool valid);
    function supportsAggregation() external pure returns (bool supported);
    function verifyAggregated(
        bytes[] calldata pubKeys,
        bytes32[] calldata messageHashes,
        bytes calldata aggregatedSignature
    ) external view returns (bool valid);
}

Abbreviated; the full interface also includes pubKeySize(), signatureSize(), and
verifyWithRegisteredKey(). Each signature scheme deploys its own registry. Scheme IDs:
0x01=ECDSA, 0x02=BLS12-381, etc. Proof of possession prevents rogue key attacks. Aggregation
support is opt-in per scheme.

Message Exposure (IERC_BAM_Exposer)

Standardized event for proving individual messages on-chain:

interface IERC_BAM_Exposer {
    event MessageExposed(
        bytes32 indexed contentHash,
        bytes32 indexed messageId,
        address indexed author,
        address exposer
    );

    function isExposed(bytes32 messageId) external view returns (bool exposed);
}

The expose() function itself is NOT standardized; it varies by proof type (KZG, ZK, merkle) and
signature scheme. The event is the interoperability surface: any exposer, regardless of mechanism,
emits MessageExposed.

Key Design Decisions

BAM extends BSS. The original design defined registerBlobBatch(blobIndex) with no segment
coordinates. When sharing blobs with other protocols, callers needed a separate BSS call. Two calls
to two contracts created a correlation problem: a shared blob with N segments produces N
BlobSegmentDeclared events and one BlobBatchRegistered event, all referencing the same versioned
hash. Inheriting IERC_BSS eliminates the ambiguity. One call, two events, unambiguous correlation.

Trust separation: decoder vs registry. The original design bundled decoding and verification in
a single “schema” contract. If a client trusts a bad schema, anyone can impersonate any address. The
fix: decoders (untrusted, many) extract messages; registries (trusted, ~4) verify signatures. A
lying decoder produces wrong hashes that fail verification — false negatives, never false positives.

On-chain decoder discovery. Traditional approaches embed decoding logic in off-chain clients. If
a protocol changes its encoding, every client needs an update. On-chain decoders invert this: the
decoder is on-chain, auditable, and callable by any contract or client. The decoder address is
emitted in the registration event, discoverable from the event log alone.

No singleton requirement. BAM contracts do not require a shared singleton deployment. Each
deployment emits BlobSegmentDeclared from its own address. Ethereum event topics are globally
indexed; indexers filter by topic hash, not by contract address.

Zero-storage core. Events only, no SSTORE. Registration costs ~5,900 gas (segment validation +
two LOG4 event emissions) vs ~25,000+ with storage. Under 29% overhead on the cheapest possible blob
transaction.

Generic signature registry. One interface for all schemes. BLS12-381 is the primary use case
today (79-94% authentication overhead savings from signature aggregation depending on batch size),
but the interface supports post-quantum schemes without modification.

Unstandardized expose function. BLS+KZG requires different parameters than ECDSA+Merkle or
STARK+ZK. The event provides the interoperability surface.

Message ID convention. keccak256(author || nonce || contentHash): deterministic, requires no
on-chain state, collision-resistant. The nonce prevents same-batch collisions.

Signing domain. keccak256("ERC-BAM.v1" || chainId): prevents cross-chain replay. EIP-712 was
considered but rejected; authenticated messages lack a standardized struct type, and EIP-712’s typed
data model adds overhead without benefit when the message format is intentionally unspecified.

Use Cases

Decentralized Twitter on Blobs

An aggregator collects signed messages from users, encodes them via a v1 decoder format, compresses
into a blob, and submits to L1. registerBlobBatch emits segment + batch events with decoder and
signature registry addresses. Any client reads these from the event, fetches blob data, calls
decoder.decode(payload) to extract messages and signature data, computes standardized message
hashes, and calls registry.verifyAggregated(pubKeys, signedHashes, signatureData) to validate.
Users register BLS keys via the signature registry for efficient aggregation (one signature for 500+
messages).

On-Chain Governance Trigger

A governance contract needs to verify that a specific message was posted before executing a
proposal. An exposer proves the message via KZG point evaluation, emitting MessageExposed. The
governance contract calls isExposed(messageId) to verify.

Cross-Implementation Interoperability

Two messaging protocols both implement IERC_BAM_Core. A unified indexer tracks
BlobBatchRegistered events across both, reads their respective decoder addresses, and decodes
messages from both protocols using the on-chain decoders. Both protocols can share the same trusted
registry. Users register keys once and use them across protocols.

Shared Blob with L2 Rollup

An Optimism batcher fills field elements 0-1999 with rollup data. A messaging aggregator fills
2000-4095 with messages. The rollup calls bss.declareBlobSegment(0, 0, 2000, ...) on a standalone
BSS contract. The messaging aggregator calls core.registerBlobBatch(0, 2000, 4096, ...) on the BAM
contract. Both emit BlobSegmentDeclared (from different contracts). The BAM contract additionally
emits BlobBatchRegistered with the decoder and signature registry. Indexers correlate by versioned
hash and event topics.

Self-Publication Fallback

If aggregators censor a user, they self-publish via registerCalldataBatch. Higher gas cost but
censorship-resistant. Same decoder and signature registry pointers; indexers decode identically.

What This ERC Does NOT Cover

  1. Message byte layout, batch format, compression: decoder-specific. v1 decoders use simple
    encodings; complex compression is a later concern.
  2. Aggregator protocol: how aggregators collect, order, and submit messages is off-chain.
  3. Fee splitting: blob gas payment is an application concern, not a protocol one.
  4. Social features: follows, likes, profiles, threads. Application-layer; excluded from protocol
    scope.
  5. Archival: blob data preservation beyond the ~18-day pruning window is an infrastructure
    concern.
  6. The expose() function: varies by proof type and scheme. Only the event is standardized.

Relationship to Existing Work

ERC-BSS (Blob Space Segments). ERC-BAM extends ERC-BSS via Solidity interface inheritance. BSS declares
where in a blob (field element ranges). BAM adds what (authenticated message batches) and how
to read
(decoder contract). BAM contracts are BSS contracts. Standalone BSS remains available for
non-messaging use cases.

ERC-3722 (Poster). Philosophically aligned: minimal
social media on Ethereum. Poster is event-based with no blob awareness, key management, or message
proving. ERC-BAM extends the concept to blobs with signature support and decoder discovery.

ERC-7588 (Blob Transaction Metadata). Orthogonal.
ERC-7588 describes blob metadata. ERC-BAM uses blobs for authenticated message data. Can coexist.

ERC-7847 (Social Media NFTs). Different paradigm.
ERC-7847 is NFT-based; ERC-BAM is event-based with no tokens.

Farcaster / Lens. L2-specific protocols, not standardized as ERCs. ERC-BAM targets L1 blob data,
though the calldata and key management interfaces work on any EVM chain.

Reference Implementation

A reference implementation is deployed on Sepolia. The existing contracts predate the
decoder/signature-registry separation and BSS-extension features; they are functionally equivalent
for signature registry and exposure:

  • Signature Registry: BLSRegistry.sol (BLS12-381 with key rotation, revocation, and
    aggregated verification)
  • Exposer: BLSExposer.sol (KZG + BLS message exposure with on-chain verification)
Contract Sepolia Address
SocialBlobsCore 0xAdd498490f0Ffc1ba15af01D6Bf6374518fE0969
BLSRegistry 0x2146758C8f24e9A0aFf98dF3Da54eef9f53BCFbf
BLSExposer 0x0136454b435fE6cCa5F7b8A6a8cFB5B549afB717

The ERC interfaces (IERC_BAM_Core.sol, IERC_BAM_Decoder.sol, IERC_BAM_SignatureRegistry.sol,
IERC_BAM_Exposer.sol) are included in the ERC PR under assets/.

Call for Feedback

Looking for community input on:

  1. Trust separation. We split the old “schema” into an untrusted decoder (extracts messages) and
    a trusted registry (verifies signatures). A lying decoder can only cause false negatives (valid
    messages rejected), never false positives (impersonation). Does this trust model make sense? Are
    there edge cases we are missing?

  2. Decoder interface. Is a single decode(payload) → (messages, signatureData) the right
    surface? Should the decoder also return metadata (e.g., compression algorithm, version)?

  3. BAM extends BSS. Does the inheritance model (BAM contracts are BSS contracts) make sense for
    your use case? Any friction with the two-event pattern (BlobSegmentDeclared +
    BlobBatchRegistered)?

  4. Standardized message hash. We standardize
    messageHash = keccak256(sender || nonce || contents) so clients can verify independently of the
    decoder. Is this the right formula? Should it include additional fields?

  5. v1 encoding. For v1 decoders, which encoding is the right default: ABI-encoded arrays, RLP,
    or SSZ? What tradeoffs matter most (gas cost, tooling support, extensibility)?

  6. Message ID convention. Is keccak256(author || nonce || contentHash) the right formula?
    Should nonce be mandatory, or optional for protocols that do not need per-batch ordering?

  7. Exposure standardization. Is standardizing only the event (not the function) the right
    tradeoff? Would a base expose(bytes calldata proof) with opaque proof data be useful?

  8. Aggregation model. The signature registry makes aggregation opt-in per scheme. Should
    aggregation be a separate interface rather than part of the base registry?

Links