← Contract Hero · WorkLearnPlugins
acc-evm-wal
Lesson 06 · Foundry

Ship a 10 000-token ERC-721 with two storage strings, not 10 000 IPFS pins.

Build an ERC-721 whose 10 000 tokens' metadata + images live in two Walrus Quilts — one for each — instead of 10 000 IPFS pins. tokenURI(id) returns a deterministic aggregator URL with no per-token state. Includes an owner-only aggregator-migration knob for sunset resilience.

Slug: 06-quilted-collection Stack: Foundry · vendored MiniERC721 / MiniOwnable / MiniStrings Sections: 4 Final gate: forge test · 12 tests

What you'll write

One file: src/QuiltedCollection.sol. The reference-app pre-provides src/lib/ with minimal substitutes for OpenZeppelin's ERC-721, Ownable, and Strings — vendored so the workspace stays free of forge install. The lesson teaches the integration shape; production should pair the real OZ contracts with the same Walrus-quilt URL pattern.

  • s01 · storage

    Storage + constructor (and why quiltId isn't immutable)

    Solidity does not allow immutable on string — quiltId and aggregator are plain storage variables, conventionally written-once by the constructor. The CONVENTION is what guarantees immutability of quiltId — the absence of any setter is the proof. For aggregator the deliberate setAggregator setter exists as a sunset knob.

  • s02 · mint

    Self-mint with sequential ids + maxSupply cap

    tokenId = _nextTokenId++; (post-increment) — the local tokenId gets the CURRENT value (1 on first mint), then storage gets bumped to 2 for the next call. Combined with the <= cap (not <), this produces a half-open-then-closed window that mints exactly [1, maxSupply] and rejects the (maxSupply+1)-th call.

  • s03 · tokenURI

    tokenURI — building the deterministic aggregator URL

    tokenURI is a PURE STRING-CONCAT — no SLOAD other than the two storage strings, no mapping lookup, no per-token state. The URL shape <aggregator>/v1/blobs/by-quilt-id/<quiltId>/<tokenId>.json is uniform across every token; the only thing that differs per token is the decimal id. That's what collapses 10 000 token URIs into two storage strings.

  • s04 · setAggregator

    setAggregator — the sunset-migration knob

    setAggregator is owner-only and the ONLY mutable storage slot after deployment. Walrus aggregator hosts are commodity HTTPS endpoints, and any specific host might sunset. Because Walrus blobs are content-addressed, pointing at a different aggregator returns the SAME bytes — only the URL prefix changes. The migration is one transaction, costs no re-pinning, and surfaces as an AggregatorUpdated event for indexers to follow.

Verification

Twelve Foundry tests — constructor validation (4, including trailing-slash rejection), mint (2), tokenURI (2), setAggregator (4). The test_setAggregator_ownerCanMigrateAggregator test is the punchline: same token, different aggregator, new URL.

forge test

Sources

Read the reference-app on GitHub, or follow the upstream walkthrough for the two-quilt packing pipeline.