Docs menu

Reading state

Every protocol fact is readable from the Shapes address. Nothing requires a periphery contract. Reads that take a token id require a live token and revert for one that is not, except exists, composeDepth and positionOf, which never revert.

One call for everything

function shapeState(uint256 tokenId) external view returns (ShapeState memory);

struct ShapeState {
    bytes32 seed;
    uint8 denominationIndex;
    uint32 originCount;
    uint8 inkGene;
    bool isBlack;
    ShapeFormation formation;     // 0 Fragment, 1 Direct, 2 Composed, 3 Complete, 4 Black
    uint256 faceValueWei;         // the denomination; unchanged by burnBacking
    uint256 redeemableValueWei;   // 0 for a Black Shape, else faceValueWei
    bytes modules;                // stored ModuleCodec bytes; empty when seed-derived
}

previewCompose returns the same struct for a hypothetical survivor.

Per-token reads

FunctionReturns
exists(tokenId)Whether the id is live now. Never reverts. True for a Black Shape
ownerOf(tokenId)ERC-721 owner
valueOf(tokenId), backingOf(tokenId)Redeemable wei; 0 for Black
denomIndexOf(tokenId)Ladder index 0 to 8; Black keeps 8
seedOf(tokenId)Immutable seed
originCountOf(tokenId)Direct-mint origins credited
inkGeneOf(tokenId)0 to 6
formationOf(tokenId), isComplete(tokenId)Formation class; see Core concepts
isBlackShape(tokenId)Black flag
modulesOf(tokenId)Stored module bytes only; empty for an original mint
effectiveModulesOf(tokenId)Stored bytes, or the seed's grammar v1 expression
composeDepth(tokenId)Reversible composes on the stack; 0 for a non-survivor
composeRecordAt(tokenId, depth)One record; see Decomposing
splitOriginOf(tokenId)Split provenance; reverts NotASplitChild otherwise
positionOf(tokenId)Address from the positions pointer, or zero; never reverts
tokenURI(tokenId), metadataJSON(tokenId), svg(tokenId), unicodeCard(tokenId)Presentation; see Geometry and rendering

Collection reads

FunctionReturns
totalSupply()Live Shapes, Black included
totalMinted()Next id to issue; one past the highest id ever issued
redeemableBacking()ETH owed to live non-Black Shapes
burnedBacking()ETH sent to 0x…dEaD by burnBacking, cumulative
blackShapeCount()Live Black Shapes
pendingFees(), feesOwedTo(recipient)Accrued mint fees, outside the reserve
mintFee(), mintStart(), feeRecipient()Mint parameters
owner(), ownerToken()Owner token holder and id; ownerToken reverts NoOwnerToken once it is gone
admin(), artist(), artistReleaseHash(), artistSignature()Roles and attestation
renderer(), collection(), presentationLocked()Presentation pointers
positions(), market()(target, locked) pointer pairs
unit(), denominationCount(), denominationAt(i), isSupportedDenomination(wei)Ladder
contractURI()Collection metadata, from the collection contract

totalMinted is not a mint count: split children advance it, decompose restores do not, and burns never lower it. Use totalSupply for the live count and the events for history.

Simulating a write

previewCompose and previewSplit return outcomes without checking ownership. To learn whether an account may perform an operation, eth_call the mutating function from that account and decode the revert. Every error is a custom error listed on Errors.

Reading many tokens

Reads are plain view calls, so Multicall3 batches them. For a gallery or a feed, prefer the Indexer, which serves token rows, lineage edges and an activity feed without an RPC per token.

Interface ids

supportsInterface answers true for ERC-721, ERC-721 Metadata, ERC-2981 (zero royalty), ERC-4906, IERC721Value, IAdminControl, IShapes, IShapeValue, IShapeRecomposition and IShapeProvenance. The ids are on Interfaces.