feat(token): add refresh_metadata for state-free ERC-4906 refreshes - #121
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
📝 WalkthroughWalkthroughAdds single-token and batch metadata refresh interfaces and implementations. Refresh calls emit ChangesMetadata refresh
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant CoreTokenComponent
participant EventLog
Caller->>CoreTokenComponent: refresh_metadata(token_id)
CoreTokenComponent->>EventLog: emit MetadataUpdate(token_id)
Caller->>CoreTokenComponent: refresh_metadata_batch(token_ids)
CoreTokenComponent->>CoreTokenComponent: refresh each token_id
CoreTokenComponent->>EventLog: emit one MetadataUpdate per token_id
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Codex Review - General Engineering ReviewReview process failed to complete. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Codex Review - Cairo/Starknet Contract ReviewReview process failed to complete. |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/embeddable_game_standard/src/token/token_component.cairo (1)
810-875: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winComplete the public entrypoint documentation.
Document
token_idexistence constraints, batch empty-input behavior, emitted events, and an invocation example;refresh_metadata_batchcurrently has no doc block.As per coding guidelines, “Every function must include clear explanation of what it does and why, parameter descriptions with types and constraints, return value documentation, and example usage when appropriate.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/embeddable_game_standard/src/token/token_component.cairo` around lines 810 - 875, Complete the documentation for the public entrypoints refresh_metadata, update_player_name, update_game_batch, and refresh_metadata_batch, including each parameter’s type and constraints, token existence and ownership requirements where applicable, emitted events, and that these functions return no value. Add an invocation example where appropriate, and document refresh_metadata_batch’s non-empty input requirement and per-token metadata refresh behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/embeddable_game_standard/src/token/tests/test_events.cairo`:
- Around line 452-462: Update the assert_emitted call in the metadata refresh
test to assert three MetadataUpdate events, one for each requested token ID at
token_ids.at(0), token_ids.at(1), and token_ids.at(2), while preserving the
existing event structure and ordering.
In `@packages/interfaces/src/token/core.cairo`:
- Around line 99-104: Regenerate IMINIGAME_TOKEN_ID for the expanded
IMinigameToken trait using the documented src5_rs parse procedure, then update
the exported constant and its matching documentation. Keep CoreTokenComponent
registration aligned with the regenerated selector value so SRC5 advertises the
current trait ABI.
---
Nitpick comments:
In `@packages/embeddable_game_standard/src/token/token_component.cairo`:
- Around line 810-875: Complete the documentation for the public entrypoints
refresh_metadata, update_player_name, update_game_batch, and
refresh_metadata_batch, including each parameter’s type and constraints, token
existence and ownership requirements where applicable, emitted events, and that
these functions return no value. Add an invocation example where appropriate,
and document refresh_metadata_batch’s non-empty input requirement and per-token
metadata refresh behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f72f7f9c-02be-4750-b4a4-33b27b4191d9
📒 Files selected for processing (9)
packages/embeddable_game_standard/src/metagame/tests/test_fuzz_mint_parameters.cairopackages/embeddable_game_standard/src/metagame/tests/test_libs.cairopackages/embeddable_game_standard/src/metagame/tests/test_metagame_component.cairopackages/embeddable_game_standard/src/metagame/tests/test_tournament_flow.cairopackages/embeddable_game_standard/src/token/interface.cairopackages/embeddable_game_standard/src/token/tests/test_events.cairopackages/embeddable_game_standard/src/token/token_component.cairopackages/interfaces/src/token/core.cairopackages/metagame/src/ticket_booth/tests/test_ticket_booth.cairo
| spy | ||
| .assert_emitted( | ||
| @array![ | ||
| ( | ||
| token_address, | ||
| CoreTokenComponent::Event::MetadataUpdate( | ||
| CoreTokenComponent::MetadataUpdate { token_id: (*token_ids.at(2)).into() }, | ||
| ), | ||
| ), | ||
| ], | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert every refreshed token ID.
The count check plus the single assertion for token_ids.at(2) does not prove the first two metadata events belong to their requested tokens. Assert all three expected events.
Proposed test strengthening
spy
.assert_emitted(
`@array`![
+ (
+ token_address,
+ CoreTokenComponent::Event::MetadataUpdate(
+ CoreTokenComponent::MetadataUpdate {
+ token_id: (*token_ids.at(0)).into(),
+ },
+ ),
+ ),
+ (
+ token_address,
+ CoreTokenComponent::Event::MetadataUpdate(
+ CoreTokenComponent::MetadataUpdate {
+ token_id: (*token_ids.at(1)).into(),
+ },
+ ),
+ ),
(
token_address,
CoreTokenComponent::Event::MetadataUpdate(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| spy | |
| .assert_emitted( | |
| @array![ | |
| ( | |
| token_address, | |
| CoreTokenComponent::Event::MetadataUpdate( | |
| CoreTokenComponent::MetadataUpdate { token_id: (*token_ids.at(2)).into() }, | |
| ), | |
| ), | |
| ], | |
| ); | |
| spy | |
| .assert_emitted( | |
| `@array`![ | |
| ( | |
| token_address, | |
| CoreTokenComponent::Event::MetadataUpdate( | |
| CoreTokenComponent::MetadataUpdate { | |
| token_id: (*token_ids.at(0)).into(), | |
| }, | |
| ), | |
| ), | |
| ( | |
| token_address, | |
| CoreTokenComponent::Event::MetadataUpdate( | |
| CoreTokenComponent::MetadataUpdate { | |
| token_id: (*token_ids.at(1)).into(), | |
| }, | |
| ), | |
| ), | |
| ( | |
| token_address, | |
| CoreTokenComponent::Event::MetadataUpdate( | |
| CoreTokenComponent::MetadataUpdate { token_id: (*token_ids.at(2)).into() }, | |
| ), | |
| ), | |
| ], | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/embeddable_game_standard/src/token/tests/test_events.cairo` around
lines 452 - 462, Update the assert_emitted call in the metadata refresh test to
assert three MetadataUpdate events, one for each requested token ID at
token_ids.at(0), token_ids.at(1), and token_ids.at(2), while preserving the
existing event structure and ordering.
| fn refresh_metadata(ref self: TState, token_id: felt252); | ||
| fn update_player_name(ref self: TState, token_id: felt252, name: felt252); | ||
|
|
||
| // Batch write functions | ||
| fn update_game_batch(ref self: TState, token_ids: Span<felt252>); | ||
| fn refresh_metadata_batch(ref self: TState, token_ids: Span<felt252>); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file excerpt =="
sed -n '1,130p' packages/interfaces/src/token/core.cairo
echo
echo "== src5_rs references =="
rg -n "src5_rs parse|IMINIGAME_TOKEN_ID|supports_interface|register_interface|core::src5" packages/interfaces packages -g '*.cairo' -g '*.toml' || true
echo
echo "== locate src5 parse tool/config =="
fd -a 'src5|SRC5|src5' . | head -100Repository: Provable-Games/game-components
Length of output: 44338
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== diff for files tracked (short) =="
git diff --stat HEAD~1..HEAD 2>/dev/null || git diff --stat 2>/dev/null || true
echo
echo "== changed token interfaces context =="
git diff --name-only HEAD~1..HEAD 2>/dev/null | rg 'packages/interfaces/src/.*\.cairo$' || true
echo
echo "== selectors in target trait by external mut/immutable refs =="
python3 - <<'PY'
from pathlib import Path
p = Path('packages/interfaces/src/token/core.cairo')
text = p.read_text()
for i,line in enumerate(text.splitlines(), 1):
s=line.strip()
if s.startswith('fn '):
print(f"{i}: {s}")
PYRepository: Provable-Games/game-components
Length of output: 3550
🏁 Script executed:
#!/bin/bash
set -euo eou pipefail
echo "== generator/check files mentioning src5/id generation =="
rg -n "src5_rs|interface.*id|IMINIGAME_TOKEN_ID|IMINIGAME_REGISTRY_ID|src5::|supports_interface" packages/interfaces/src -g '*.cairo' -g '*.md' -g '*.toml' || true
echo
echo "== AGENTS interface-id procedure =="
sed -n '1,220p' packages/interfaces/src/AGENTS.md 2>/dev/null || true
echo
echo "== token component support registration excerpt =="
sed -n '900,930p' packages/embeddable_game_standard/src/token/token_component.cairo 2>/dev/null || trueRepository: Provable-Games/game-components
Length of output: 212
🌐 Web query:
src5_rs parse Starknet SRC5 interface ID generator documentation
💡 Result:
src5-rs is a utility tool designed to automatically compute Starknet SRC-5 interface IDs directly from Cairo source code, helping developers avoid the manual effort and potential errors associated with calculating interface selectors [1][2][3]. The SRC-5 standard defines a mechanism for Starknet contracts to introspect and query whether a contract supports specific interfaces [1][4]. The interface ID is defined as the XOR of all the extended function selectors of that interface [4][5]. Key points regarding SRC-5 and interface ID generation: - Standard Definition: Interface ID computation is specified in the SNIP-5 (Starknet Improvement Proposal 5) documentation [1][4]. - Utility: Developers are strongly advised to use automated tools like src5-rs rather than manual computation [4][3]. - Integration: OpenZeppelin's Cairo contracts library provides a standard SRC5Component that can be used to implement interface support and register these IDs in a contract [4][5][6]. - Finding the Tool: You can find the src5-rs project on GitHub (typically at github.com/ericnordelo/src5-rs), which is referenced by the official Starknet community and OpenZeppelin documentation as a primary tool for this purpose [1][2][5]. To compute an ID, the tool processes Cairo traits and translates them into the required selectors compliant with the SNIP-5 specification [1][2]. For those manually verifying, the SNIP-5 document provides specific examples, such as calculating the ISRC5 interface ID (0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055) by running a starknet_keccak hash on its function signature [1].
Citations:
- 1: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md
- 2: https://community.starknet.io/t/starknet-standard-interface-detection/92664/30
- 3: Replace ERC-165 with SRC-5 (Update Interface Ids) OpenZeppelin/cairo-contracts#637
- 4: https://docs.openzeppelin.com/contracts-cairo/3.x/introspection
- 5: https://raw.githubusercontent.com/OpenZeppelin/docs/refs/heads/main/content/contracts-cairo/3.x/introspection.mdx
- 6: https://docs.provable.games/embeddable-game-standard/advanced/src5
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== generator/check files mentioning src5/id generation =="
rg -n "src5_rs|interface.*id|IMINIGAME_TOKEN_ID|IMINIGAME_REGISTRY_ID|src5::|supports_interface" packages/interfaces/src -g '*.cairo' -g '*.md' -g '*.toml' || true
echo
echo "== AGENTS interface-id procedure =="
if [ -f packages/interfaces/src/AGENTS.md ]; then
sed -n '1,220p' packages/interfaces/src/AGENTS.md
else
echo "missing packages/interfaces/src/AGENTS.md"
fi
echo
echo "== token component support registration excerpt =="
sed -n '900,930p' packages/embeddable_game_standard/src/token/token_component.cairo 2>/dev/null || true
echo
echo "== all IMINIGAME_TOKEN_ID declaration usages =="
rg -n "const IMINIGAME_TOKEN_ID|pub const IMINIGAME_TOKEN_ID|IMINIGAME_TOKEN_ID" packages/interfaces packages/embeddable_game_standard packages -g '*.cairo' | sed -n '1,40p'Repository: Provable-Games/game-components
Length of output: 17666
Regenerate IMINIGAME_TOKEN_ID for the expanded IMinigameToken trait.
IMinigameToken now exposes additional selectors, but the constant at packages/interfaces/src/token/core.cairo:13 was not updated. That value is exported as IMINIGAME_TOKEN_ID and registered in CoreTokenComponent, so SRC5 support will advertise an ABI surface that no longer matches the trait. Generate it with src5_rs parse per the interfaces README/AGENTS procedure and update the constant and matching docs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/interfaces/src/token/core.cairo` around lines 99 - 104, Regenerate
IMINIGAME_TOKEN_ID for the expanded IMinigameToken trait using the documented
src5_rs parse procedure, then update the exported constant and its matching
documentation. Keep CoreTokenComponent registration aligned with the regenerated
selector value so SRC5 advertises the current trait ABI.
Source: Coding guidelines
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Codex Review - General Engineering ReviewReview process failed to complete. |
Codex Review - Cairo/Starknet Contract ReviewReview process failed to complete. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/embeddable_game_standard/src/token/token_component.cairo (2)
860-863: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winMove the batch panic message into a descriptive constant.
refresh_metadata_batchadds another inline copy of the empty-token-IDs error. Define a module-level error constant and reuse it here and in the adjacent batch entrypoints.As per coding guidelines, all error messages must be implemented as descriptive constants.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/embeddable_game_standard/src/token/token_component.cairo` around lines 860 - 863, Define a descriptive module-level constant for the empty token IDs error, then update refresh_metadata_batch and the adjacent batch entrypoints to reuse it in their assertions instead of inline message strings. Preserve the existing validation behavior and message content.Source: Coding guidelines
810-826: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winComplete the API documentation for both refresh entrypoints.
refresh_metadatalacks explicit parameter/return documentation, andrefresh_metadata_batchhas no doc comment. Document thefelt252inputs, empty-span constraint, emitted-event behavior, and()return contract.As per coding guidelines, every function must include a clear explanation, parameter descriptions with types and constraints, return-value documentation, and examples when appropriate.
Also applies to: 860-873
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/embeddable_game_standard/src/token/token_component.cairo` around lines 810 - 826, Complete the API documentation for refresh_metadata and refresh_metadata_batch. Add clear descriptions of each felt252 token_id input, including the batch function’s requirement that the span is non-empty, document that each call emits MetadataUpdate without changing state, and specify the () return value; include usage examples where appropriate while preserving the existing permissionless and advisory behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/embeddable_game_standard/src/token/token_component.cairo`:
- Around line 860-863: Define a descriptive module-level constant for the empty
token IDs error, then update refresh_metadata_batch and the adjacent batch
entrypoints to reuse it in their assertions instead of inline message strings.
Preserve the existing validation behavior and message content.
- Around line 810-826: Complete the API documentation for refresh_metadata and
refresh_metadata_batch. Add clear descriptions of each felt252 token_id input,
including the batch function’s requirement that the span is non-empty, document
that each call emits MetadataUpdate without changing state, and specify the ()
return value; include usage examples where appropriate while preserving the
existing permissionless and advisory behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ba6a1dc9-94b1-46f7-9275-33355e8e82c8
📒 Files selected for processing (2)
packages/embeddable_game_standard/src/token/tests/test_events.cairopackages/embeddable_game_standard/src/token/token_component.cairo
update_game re-reads game_over and score from the game contract and notifies the minter, which costs several cross-contract calls. Indexers only need the MetadataUpdate event to learn that a token's rendered metadata is stale — token_uri reads score live from the game — so games can now call refresh_metadata after ordinary mid-game actions and reserve update_game for transitions that must be persisted (game over, objective completion). refresh_metadata asserts the token exists and emits MetadataUpdate. It writes no state, so it can never substitute for the game-over sync; a test pins that invariant. Permissionless, matching update_game. The worst case is a caller paying to trigger a re-render. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The check cost ~52k l2 gas on what is meant to be the cheap path and bought nothing defensively: a caller who wants to spam MetadataUpdate can do it with any real token id, so the check only blocked the harmless variant. The event is advisory — consumers must resolve the token id against their own record of minted tokens before acting on it, and token_uri already reverts for a token that does not exist. Replaces the should_panic test with one asserting the event is emitted for an unminted id, so the contract is pinned rather than incidental. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
b09637e to
6e7833a
Compare
Claude Code ReviewThe interface ID is a hardcoded constant, not auto-derived, so adding trait methods won't change it at compile time. The AGENTS.md guidance to leave it untouched is intentional and documented. Whether CI's The diff is internally consistent. All findings I could raise are already deliberately addressed and pinned by tests (existence check omission, no-state-persistence, interface ID exclusion). The batch loop uses the same lgtm |
GPT Code Review[MEDIUM] packages/interfaces/src/token/core.cairo:99 - |
…KEN_ID Review flagged the constant as stale after the trait gained refresh_metadata / refresh_metadata_batch. Keeping the ID: it is registered on-chain by every deployed token contract, so rederiving it for two additive optional methods would make supports_interface return false on all of them and break interface discovery ecosystem-wide. Recorded in the AGENTS.md that already owns the derivation procedure — the constant's doc comment points there — so anyone rebuilding the stripped input knows to omit both methods, and future additive methods get the same treatment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/embeddable_game_standard/src/token/tests/test_events.cairo`:
- Around line 369-381: Update the test around token_dispatcher.refresh_metadata
to assert the token-side score remains unchanged after refresh_metadata, in
addition to the existing flag assertions. Then extend the update_game
verification to assert that the token score synchronizes to the mock game’s
score of 100, using the state access pattern already present.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 568cea6c-2d76-4ba5-9d87-19dc039306f4
📒 Files selected for processing (10)
packages/embeddable_game_standard/src/metagame/tests/test_fuzz_mint_parameters.cairopackages/embeddable_game_standard/src/metagame/tests/test_libs.cairopackages/embeddable_game_standard/src/metagame/tests/test_metagame_component.cairopackages/embeddable_game_standard/src/metagame/tests/test_tournament_flow.cairopackages/embeddable_game_standard/src/token/interface.cairopackages/embeddable_game_standard/src/token/tests/test_events.cairopackages/embeddable_game_standard/src/token/token_component.cairopackages/interfaces/src/AGENTS.mdpackages/interfaces/src/token/core.cairopackages/metagame/src/ticket_booth/tests/test_ticket_booth.cairo
🚧 Files skipped from review as they are similar to previous changes (8)
- packages/embeddable_game_standard/src/metagame/tests/test_fuzz_mint_parameters.cairo
- packages/metagame/src/ticket_booth/tests/test_ticket_booth.cairo
- packages/embeddable_game_standard/src/token/interface.cairo
- packages/embeddable_game_standard/src/metagame/tests/test_libs.cairo
- packages/embeddable_game_standard/src/metagame/tests/test_metagame_component.cairo
- packages/embeddable_game_standard/src/metagame/tests/test_tournament_flow.cairo
- packages/embeddable_game_standard/src/token/token_component.cairo
- packages/interfaces/src/token/core.cairo
| mock_game.set_score(token_id, 100); | ||
| mock_game.set_game_over(token_id, true); | ||
|
|
||
| token_dispatcher.refresh_metadata(token_id); | ||
|
|
||
| let state = token_dispatcher.token_mutable_state(token_id); | ||
| assert!(!state.game_over, "refresh_metadata must not persist game_over"); | ||
| assert!(!state.completed_objective, "refresh_metadata must not persist completed_objective"); | ||
| assert!(token_dispatcher.is_playable(token_id), "token should still be playable"); | ||
|
|
||
| // update_game is still the thing that syncs it. | ||
| token_dispatcher.update_game(token_id); | ||
| assert!(token_dispatcher.token_mutable_state(token_id).game_over, "update_game should sync"); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert that refresh leaves the token-side score unchanged.
The test sets the game score to 100, but only checks flags. A regression that persists score during refresh_metadata would still pass; assert the pre-refresh token score remains intact, then verify update_game synchronizes it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/embeddable_game_standard/src/token/tests/test_events.cairo` around
lines 369 - 381, Update the test around token_dispatcher.refresh_metadata to
assert the token-side score remains unchanged after refresh_metadata, in
addition to the existing flag assertions. Then extend the update_game
verification to assert that the token score synchronizes to the mock game’s
score of 100, using the state access pattern already present.
Summary
Adds
refresh_metadata(token_id)andrefresh_metadata_batch(token_ids)toCoreTokenComponent— an entrypoint that emits the ERC-4906MetadataUpdateevent and does nothing else.update_gamecouples two separable concerns: syncinggame_over/completed_objectiveintotoken_mutable_state, and telling indexers that a token's rendered metadata is stale. Only the first needs the cross-contract reads back into the game and the metagame callbacks. Sincetoken_urireads score live from the game contract, a bareMetadataUpdateis enough to keep an indexer's view fresh.That lets a game call
refresh_metadataafter ordinary mid-game actions and reserveupdate_gamefor transitions that must be persisted (game over, objective completion), instead of choosing between paying for the full callback on every action or dropping the event entirely and letting indexed state go stale.Changes
CoreTokenComponent:refresh_metadataandrefresh_metadata_batch. The batch form mirrorsupdate_game_batch— same empty-array assert, same loop.IMinigameToken(packages/interfaces/src/token/core.cairo) and theIMinigameTokenMixindispatcher interface gain both methods.IMinigameTokenimplementations across the metagame and ticket-booth tests get no-op stubs, since adding trait methods breaks implementors.No storage layout, event, or existing-entrypoint changes. Downstream token contracts that embed
CoreTokenImplpick the entrypoints up with no code change.Permissionless, and no existence check
refresh_metadatais callable by anyone and deliberately does not verify thattoken_idexists.An existence check measured ~52k l2 gas on what is meant to be the cheap path, and it bought nothing defensively: a caller who wants to spam
MetadataUpdatecan do it with any real token id, so the check only blocked the harmless variant. The event is advisory.The contract for consumers: resolve
token_idagainst your own record of minted tokens before acting on it.token_urireverts for a token that does not exist, so an indexer that blindly fetches on every event will burn RPC calls on ids that were never minted (or have since been burned).test_refresh_metadata_unminted_token_emits_without_revertingpins this so a future "hardening" change has to be deliberate.What it deliberately does not do
refresh_metadatanever writestoken_mutable_state. A game that is over on the game contract stays "not over" on the token untilupdate_gameruns, so this cannot be substituted for the game-over sync —is_playable,assert_playable, and theon_game_overcallback keep working exactly as before.test_refresh_metadata_does_not_persist_game_statepins that: it sets the mock game togame_over = true, callsrefresh_metadata, asserts the token is still playable, then callsupdate_gameand asserts it syncs.Gas
Like-for-like against
update_game— identical deploys, mint, andset_score, differing only in the final call:update_gamerefresh_metadataDropping the existence check saves a further ~52,530. Treat the total as a floor rather than the expected saving: the mock game's
game_over()andscore()are trivial storage reads, whereas in a real game they re-run the game's own state loading, which is where the cost actually lives.Test plan
scarb buildcleansnforge test -p game_components_embeddable_game_standard— 1133/1133 passsnforge test -p game_components_metagame— 437/437 passscarb fmt --check --workspacecleangit diff --checkcleanSix new tests in
token/tests/test_events.cairo: exact-event assertion, no-state-persistence, single-event emission, unminted-token emits without reverting, per-token batch emission, empty-batch panic.🤖 Generated with Claude Code
Summary by CodeRabbit
MetadataUpdateevents and do not modify gameplay-related token state.