Wire the Beasts NFT to the permissionless species registry - #20
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
GPT Code Review[CRITICAL] src/lib.cairo:715 - [MEDIUM] src/lib.cairo:743 - The fan-out emits the Genesis token and then up to |
There was a problem hiding this comment.
Pull request overview
Integrates the Beasts NFT contract with the permissionless species registry for community species (76+), while keeping the existing genesis species (1–75) behavior unchanged. This wires per-species mint authorization, routes token_uri resolution through registry-backed name/art/stats for community species, and adds safety boundaries for untrusted provider output.
Changes:
- Adds registry wiring (
set_registry_address) and updates mint authorization to resolve traits/minter via the registry for community species. - Introduces render-time art validation for untrusted community art providers and routes metadata generation through pre-fetched name/art inputs.
- Adds a community-species stats cache with permissionless refresh, plus end-to-end registry↔NFT integration tests.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/stats_cache.cairo | Adds packed cached-stats storage type for community species. |
| src/registry_integration_tests.cairo | Adds end-to-end tests wiring real registry + real NFT, including art validation + stats refresh + fan-out. |
| src/minting_coordinator.cairo | Refactors mint preparation to accept pre-resolved (tier, type) traits for registry/genesis paths. |
| src/mint_tests.cairo | Updates mint failure expectations for community species when registry is not wired. |
| src/metadata_generator.cairo | Switches metadata generation to accept resolved species name + pre-fetched image URI (no dispatcher). |
| src/lib.cairo | Wires registry into the NFT, adds stats refresh/cache, updates token-uri routing + fan-out behavior. |
| src/interfaces.cairo | Extends the public interface with registry wiring and cached-stats APIs. |
| src/beast_svg.cairo | Takes a pre-fetched image URI rather than calling a provider dispatcher from inside the renderer. |
| src/beast_manager.cairo | Introduces genesis boundary constant, trait validation, and helpers for registry-supplied traits. |
| src/art_validation.cairo | Adds strict allowlist + base64 validation for untrusted provider-returned image data URIs. |
| docs/community-beasts-design.md | Updates migration/backfill/deployment sequencing documentation to match the new wiring model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let num_deaths = death_mountain_dispatcher | ||
| .get_collectable_count( | ||
| death_mountain_dispatcher.contract_address, beast_hash, | ||
| ); |
| fn get_cached_stats(self: @ContractState, token_id: u256) -> BeastLiveStats { | ||
| let cached = self.cached_stats.entry(token_id).read(); | ||
| BeastLiveStats { | ||
| adventurers_killed: cached.adventurers_killed, | ||
| last_killed_by: cached.last_killed_by, | ||
| last_killed_timestamp: cached.last_killed_timestamp, | ||
| } | ||
| } |
7c2caeb to
f32e7b6
Compare
3d517d5 to
9078668
Compare
f32e7b6 to
0a9e422
Compare
Community species (76+) now resolve everything they need through the registry, while genesis species (1-75) keep their existing baked-in paths. - mint: per-species minter authorization. Genesis species answer to the single dungeon address; every community species names its own minter, and a zero minter reads as paused rather than matching a zero caller. - mint_provenance / emit_species_metadata_update: registry-only entrypoints. Provenance uses erc721.mint, not safe_mint, so a contract artist cannot reenter the registry mid-registration. - token_uri: routes name, art, and stats by species range. Art from a custom provider is validated at render time (allowlisted image media type + strict base64) because that provider is arbitrary artist code embedded verbatim into a single-quoted SVG attribute. - stats: community species read a cache filled by a permissionless refresh_stats, never a live call. Starknet cannot catch a failed external call, so a live read would let any artist brick rendering for their whole species. - fan-out: the Genesis Beast holds rank 0 and lives outside beast_species_lists, so a list-only walk left the artist's own token permanently stale after every art change. It is now emitted explicitly. Species resolution is split so nothing forecloses the deferred burn_and_mint migration: validation lives in reusable helpers, and the genesis tables stay reachable without any registry entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Without the gate, mock_art_provider and mock_stats_feed compile into the production artifact set — deployable contracts that exist only to serve tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses, class hashes, wiring transactions, and the verification run that exercised register -> provenance mint -> species mint -> render on a live network. deployments/ is gitignored, so the record lives in docs/ where the addresses are actually shareable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three end-to-end tests over the deployed pair rather than a mock: an ordinary ERC721 transfer of the creator token hands the species to the buyer, the seller loses admin with it, and enumeration plus a local decode recovers which species a wallet controls with no registry reads. That last one is why the two changes belong together — owner enumeration gives a client the token list, and the derived role makes the token list the answer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0a9e422 to
e7cfd75
Compare
Stacked on #19 (
feat/beast-registry), notmain— per the decision to keep the registry offmainuntil the whole stack has been exercised on Sepolia.What this does
Community species (76+) resolve minter, traits, name, art, and stats through the registry. Genesis species (1–75) keep every existing baked-in path unchanged.
mintdungeon_address; community →registry.get_minter(id). A zero minter reads as paused, not "matches a zero caller".mint_provenance+emit_species_metadata_update, both registry-only. Useserc721.mint, notsafe_mint—safe_mintcalls back into the recipient, handing a contract artist a reentry point into the registry while its definition is written butnext_idhasn't settled.token_uribeast_svg/metadata_generatornow take a pre-fetchedByteArrayinstead of a dispatcher, so one code path serves both species ranges.art_validation.cairo. A custom provider is arbitrary artist code whose output is embedded verbatim inside a single-quotedsrc='...'SVG attribute — so render-time it must clear an allowlisted image media type (png/gif/webp/svg+xml) plus a strict base64 body. No quote, angle bracket, or whitespace survives. This was the PR-4 commitment made in response to the Codex HIGH on #19.stats_cache.cairo(3×u64 packed into one felt). Community species read a cache filled by permissionlessrefresh_stats;token_urinever calls the artist-nominated source. Starknet can't catch a failed external call, so a live read would let any artist permanently brick rendering for their whole species.refresh_statsrejects no-op refreshes so it isn't a free ERC-4906 spam faucet.beast_species_lists— a list-only walk left the artist's own token permanently stale after every art change. Now emitted explicitly. Fan-out is capped atFAN_OUT_LIMITwith the overflow bookmarked.Not foreclosing
burn_and_mintMigration to V3 is intended to be a player-initiated burn-and-mint, deferred to PR 6. This PR keeps that open: mint validation lives in reusable helpers (
assert_can_mint,prepare_mint_with_traits) rather than inlined inmint;mintedstarts empty except the 75 genesis affix slots; and species 1–75 resolve without any registry entry. Registry backfill of 1–75 is explicitly dropped from scope — documented in the design doc as still possible later, since no read path asserts a lower bound on a stored key.Tests
158 passed, 0 failed(was 116 on #19). 21 are new end-to-end tests inregistry_integration_tests.cairo— the first place the real NFT and real registry are wired together and driven through register → provenance mint → species mint → render.Notable coverage: the global dungeon address cannot mint a community species; a paused (zero-minter) species rejects mints; an unregistered species reverts; a provider returning
AAAA'AAAor atext/htmlpayload is rejected at render; fan-out event counts verify the genesis token is included both before and after mints.scarb fmt --check --workspaceclean.Deploy note
The two contracts wire after the fact via write-once setters (
registry.set_nft_address,nft.set_registry_address) because each needs the other's address. A stack missing either accepts no community species — the intended fail-closed state. Deployment sequence updated in the design doc.🤖 Generated with Claude Code