Event spine & ERC standards
The event spine is the set of events an ABX contract emits. With the read interfaces, it forms the ABX protocol. Concrete contracts emit these events, and services such as indexers, resolvers, and render nodes read them to track a project's state.
The spine has two registers: the standard ERC events, and a native ABX layer.
Structure
- One project per contract. A project is identified by its contract address. There is no
projectId. - A required core with opt-in extensions. The core is an ERC-165-discoverable ERC-721 or
ERC-1155 contract that emits one required event,
AbxDeployed. All other events belong to opt-in extensions. A Series is many unique tokens (ERC-721); an Edition is many copies of a token (ERC-1155). - An open set. New extensions can be defined. The ones documented here are the current canonical set.
Two registers
Every state change is announced in both registers.
| Register | Read by | Purpose |
|---|---|---|
| Standard ERC and OpenSea events | existing indexers and marketplaces | compatibility with tools that have no ABX awareness |
| Native ABX events | ABX-aware resolvers and render nodes | re-indexing a project's state from the logs |
Where a standard already defines a signal, ABX emits it verbatim alongside the native event.
Register 1: the standards ABX speaks
These are emitted with their canonical signatures, so existing indexers read ABX contracts without special-casing.
- ERC-721: ownership (
Transfer,Approval,ApprovalForAll). A mint is a transfer from0x0, so no ABX-specific mint event is needed. - ERC-1155: ownership and metadata-refresh for the edition contracts (
OneOfOneEdition,EditionImage,EditionCode) —TransferSingle,TransferBatch,URI, and the sameApprovalForAll. A mint is a transfer from0x0, same as ERC-721. - ERC-2309 (optional): a batch mint as a single event.
- ERC-4906:
MetadataUpdateandBatchMetadataUpdate, the metadata-update signal marketplaces read — emitted and ERC-165-advertised on both lanes. The standard is nominally a 721 extension, but the range form is what the ecosystem actually consumes for ERC-1155 too, and this repo'sIERC4906is event-only, so0x49064906is a refresh marker rather than a claim of ERC-721 support. An edition emits ERC-1155's nativeURIin addition, but not everywhere a 4906 ping fires:URIhas no range variant, so a contract-wide re-point signals only via 4906 plus its own config event, and a per-id override write skipsURIwhen an on-chain renderer is configured (the event would have to carry the whole rendered document).pingURI(ids)is the owner-only, caller-chunked helper for re-emitting per-id refresh events afterward: it emitsMetadataUpdate(id)for every id named, plus nativeURI(uri(id), id)when no on-chain renderer is configured. It is owner-only because that matches what it is for — an owner's follow-up to their own re-point — and because with an on-chain renderer a permissionless version is asymmetric: the caller pays event gas while an event-driven indexer may perform an expensiveuri(id)per id, for arbitrary, duplicate, or nonexistent ids. - ERC-7572:
ContractURIUpdated, for contract-level metadata. - ERC-2981: royalty resolution, via the
royaltyInfoview. - ERC-721-C (or its ERC-1155 twin, ERC-1155C, on an edition — opt-in per deploy): royalty enforcement, for creators who want it. A collection that enrolls at deploy emits
TransferValidatorUpdatedat enrollment and on every validator change, and advertises the creator-token interfaces via ERC-165 — only when enrolled; an unenrolled collection carries no trace of the standard. See Royalty enforcement. - ERC-7496, ERC-5192, OpenSea
PermanentURI(optional): on-chain traits, soulbound tokens, and frozen metadata.
The current ABX contracts implement a subset of these standards. The others are listed because the protocol is designed to speak them, and future implementations may add them as needed.
Register 2: the native ABX layer
These are opt-in extensions, advertised through ERC-165. Each defines its events and, where it has a read surface, its interface.
AbxDeployed: the one required event, emitted once at deploy. It is an open discovery signal: a resolver finds ABX contracts by watching this event, and any implementation may emit it, including custom ones. It signals discovery, not authenticity. See Authenticity.- Royalty, Primary Payee, External Minter, Max Invocations, Paused: sale and ownership configuration.
- Edition Supply: the ERC-1155 analogue of Max Invocations, scoped per id instead of per project — each id's max supply, only ever decreasing. The one extension new to editions; every other extension id is shared, unchanged, between the two standards.
- Creator Token: the opt-in ERC-721C surface as an ABX extension — ERC-1155C on an edition, same extension id, same ERC-165 ids, same enrollment rule. Its beacon version is reported only when the token enrolled at deploy; the standard's own
TransferValidatorUpdatedevent carries the state, so the extension adds no native events. - On-Chain Script, Dependencies, Seed Source: code as content. A chunked on-chain program, its ordered libraries, and a mint-time pseudorandom seed stored as configuration.
- On-Chain Metadata, Params, Configurable Params: the field-and-representation store and typed parameters. See Metadata and Parameters. Authorship and rights (
creator,display_notes,creator_links,license) are reserved keys in this store, not a separate extension.
Re-indexing from the logs
A contract's AbxDeployed event, its per-extension version events, and its ERC-165 responses tell a
resolver which contracts are ABX, which events to follow, at which version, and which addresses to
watch. A project's configuration is expressed by which extensions it enables. A resolver, such as the
self-hosted reference implementation, re-indexes a project's state from these events and reads its
content bytes from content-addressed data.
Resolving an edition id's cap
An ERC-1155 edition's copy limit comes from two events, and reading only the per-id one is the mistake worth naming, because it is wrong in the common case:
DefaultMaxSupplySet(cap)— emitted once at deploy. The collection-wide default for every id (--copies N;0means open). Most projects stop here:setMaxSupplyis never called, so no id has a per-id event of its own.MaxSupplyUpdated(id, cap)— that id overriding the default, only ever downward. Its presence also means the id is explicitly overridden.
An id's effective cap is its own override if it has one, otherwise the collection default. That
matches the maxSupply(id) read exactly. What the read cannot tell you is what a 0 means: an id never
capped is open, an id deliberately overridden to 0 is closed forever, and both return 0. The
log distinguishes them — an override event exists only for the second — which is why anything rendering
a buy button should fold the log rather than trust the getter alone.
The SDK does this for you: ProjectState.defaultMaxSupply, each token's effective maxSupply and
maxSupplyOverridden, and editionCapOf(token) returning open / capped / closed.
Authenticity
AbxDeployed finds contracts; it does not vouch for them. Because any contract can emit it, discovery
and trust are separate steps. The source of truth is the canonical factory for each chain: a contract
is a canonical ABX contract when that factory deployed it, which a consumer verifies against the
factory. The SDK reports this as isCanonical, and the canonical addresses per chain are in the
SDK deployments.
The protocol is the spine and the interfaces, not one implementation. The canonical factory marks the reference implementation ABX publishes, but anyone can build a conforming implementation and emit AbxDeployed.
Versioning
Versioning is per-extension, through AbxExtensionVersionSet and extensionVersion(id). No event
carries a version field, and older projects remain readable when new versions are added. Standard
events are pinned to their canonical signatures.