Collections
Curated NFT collections with optional merkle proof enforcement
The ERC-721 Ethscriptions Collections protocol allows creators to build curated collections of ethscriptions with rich metadata and optional access control.
Overview
Collection: A named set of ethscriptions with metadata (name, symbol, description, max supply)
Items: Individual ethscriptions added to a collection
Merkle Enforcement: Optional cryptographic restriction on which items can be added
Creating a Collection
Use the create_collection_and_add_self operation to create a collection and add the first item in one transaction:
data:image/png;rule=esip6;p=erc-721-ethscriptions-collection;op=create_collection_and_add_self;d=<base64-json>;base64,<image-bytes>Where the base64-decoded d parameter contains:
{
"name": "My Collection",
"symbol": "MYC",
"maxSupply": 100,
"description": "A curated collection of digital artifacts",
"logoImageUri": "data:image/png;base64,...",
"bannerImageUri": "data:image/png;base64,...",
"website": "https://example.com",
"twitterHandle": "myhandle",
"discordUrl": "https://discord.gg/...",
"backgroundColor": "#000000",
"merkleRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"itemIndex": 0,
"itemName": "Item #1",
"itemBackgroundColor": "#FF0000",
"itemDescription": "The first item in the collection",
"itemAttributes": [["Rarity", "Legendary"], ["Color", "Red"]]
}Collection Metadata Fields
name
Collection name
Yes
symbol
Short symbol (e.g., "MYC")
Yes
maxSupply
Maximum number of items
Yes
description
Collection description
No
logoImageUri
Logo image as Data URI
No
bannerImageUri
Banner image as Data URI
No
website
Project website
No
twitterHandle
Twitter/X handle
No
discordUrl
Discord invite URL
No
backgroundColor
Default background color
No
merkleRoot
Merkle root for access control (see below)
No
Item Metadata Fields
itemIndex
Position in collection (0-indexed)
itemName
Item name
itemBackgroundColor
Item-specific background color
itemDescription
Item description
itemAttributes
Array of [traitType, value] pairs
Adding Items to a Collection
After creating a collection, add items with add_self_to_collection:
Where the d parameter contains:
The collectionId is the L1 transaction hash of the collection creation.
Merkle Proof Enforcement
When a collection has a non-zero merkleRoot, non-owners must provide a merkle proof to add items. This ensures only pre-approved items with exact metadata can be added.
How It Works
Creator generates merkle tree from approved items
Each leaf is computed from item metadata
Creator sets merkle root when creating collection
Non-owners provide proofs when adding items
Merkle Leaf Computation
Each leaf is computed as:
Merkle Tree Structure
For a 3-item collection, the tree looks like:
Where:
Proof for leaf0:
[leaf1, leaf2]Proof for leaf1:
[leaf0, leaf2]Proof for leaf2:
[H(leaf0, leaf1)]
Pair Hashing
The merkle tree uses lexicographic ordering (same as OpenZeppelin):
This ensures consistent proof verification regardless of sibling order.
Adding Items with Proofs
Non-owners include the merkle proof in the d parameter:
Owner Bypass
Collection owners can always add items without providing merkle proofs. This allows:
Adding items not in the original tree
Making corrections
Flexibility for collection management
Example: Creating a Merkle-Enforced Collection
This walkthrough creates a 3-item collection where:
Item 1 (Red)
0
Owner
No (owner bypass)
Item 2 (Green)
1
Non-owner
Yes
Item 3 (Blue)
2
Non-owner
Yes
Step 1: Compute Content Hashes
For each image, compute the keccak256 hash of the raw bytes:
Step 2: Build Merkle Leaves
Compute each leaf from the item metadata:
Step 3: Compute Merkle Root
Step 4: Create Collection (Owner)
The owner creates the collection with the merkle root and adds the first item:
Send a 0 ETH transaction to any address
Include the hex-encoded Data URI with
op=create_collection_and_add_selfThe
merkleRootis set to0x06fbc22a...Save the transaction hash as
collectionId
The owner doesn't need a merkle proof for their own item.
Step 5: Add Items (Non-Owner)
A different address adds items 2 and 3 with merkle proofs:
For Item 2 (index 1):
The proof must match exactly, and the metadata must match what was used to compute the leaf.
Operations Reference
create_collection_and_add_self
Create collection and add first item
add_self_to_collection
Add item to existing collection
edit_collection
Update collection metadata
edit_collection_item
Update item metadata
transfer_ownership
Transfer collection ownership
renounce_ownership
Surrender ownership
remove_items
Delete items from collection
lock_collection
Prevent further additions
Editing Collections
Update collection metadata with edit_collection:
Only the collection owner can edit.
Locking Collections
Once locked, no more items can be added:
Use the lock_collection operation. This is irreversible.
Error Handling
Invalid Merkle proof
Proof doesn't match root, or metadata differs from what was used to compute the leaf
Merkle proof required
Non-owner tried to add without proof to a collection with a non-zero merkle root
Item slot taken
Index already has an item
Collection locked
Cannot add to locked collection
Max supply reached
Collection is full
Not collection owner
Only owner can perform this operation
Security Considerations
Content Hash Verification - The merkle leaf includes the content hash, ensuring exact image content is verified
Metadata Binding - All metadata is bound to the merkle proof and cannot be changed after the tree is computed
Owner Bypass - Collection owners can always add items, useful for corrections
Locking - Once locked, no more items can be added even with valid proofs
Generating Merkle Trees
A TypeScript script for generating merkle trees and calldata is available in the ethscriptions-indexer repository:
The script outputs:
Content hashes for each image
Merkle leaves computed from item metadata
Merkle root for the collection
Merkle proofs for each item
Calldata payloads ready for transactions
Last updated