Skip to content

Support host-owned storage in metagame APIs - #166

Merged
starknetdev merged 6 commits into
mainfrom
feat/host-storage
Oct 11, 2026
Merged

starknetdev merged 6 commits into
mainfrom
feat/host-storage

Conversation

@starknetdev

@starknetdev starknetdev commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Applications can now use leaderboard, entry-requirement, registration, entry-fee and prize APIs with their own storage, while retaining the standard components as compatible defaults.

Adds embeddable views and shared internal operations over Store<T>, configuration providers and optional adapters to original component maps. Apps can pack fields across modules, derive configuration or retain selected deployed maps without maintaining their own copies of interface or transfer/claim logic. Standard components delegate to these APIs; their layouts, signatures, aliases, initialization, hooks and validation messages remain intact. Hosts continue to enforce authorization, lifecycle eligibility, entitlement and reentrancy protection.

Independent host examples combine registration/submission/ban state with fee refund history, and combine small prize metadata fields. Tests verify that replacing a registration preserves its refund claim, contexts stay isolated, packed writes preserve neighboring fields, and token transfers and extension dispatch behave consistently. Usage and benchmark documentation explains partial forwarding, upgrade compatibility and packing limits: the complete 165-bit entry-fee data and 88-bit distribution cannot fit together in one felt.

The matched registration → submission → refund-marking example saves 8.72% L2 gas and 25% L1 data gas (2,373,361 → 2,166,402 L2; 384 → 288 L1 data). Both paths make four write syscalls; the combined layout changes fewer distinct slots. This is a local fixture benchmark, not a tournament-wide saving or charged network fee.

Validation: all 479 metagame tests pass, including 34 host/adapter cases and existing fuzz tests; the whole workspace builds; formatting and diff checks pass. The original implementation coverage measurement (fea50c0), excluding test directories, improves from 1,247/1,389 (89.78%) on ca5d8f2 to 1,465/1,546 (94.76%), using cairo-coverage 0.6.1 and unioned source-line hits. Registration is 100%, entry fee 93.36%, prize 96.67% under that same method.

Budokan PR352 pins the implementation revision fea50c0 and now targets a fresh deployment. Its current candidate passes 360 Budokan tests with 2 existing skips, plus 165 bracket and 19 viewer release cases; its production Budokan CASM size is 78,286 felts. Budokan retains its registration and claim maps; the combined layouts are opt-in. The review follow-ups update documentation and tests without changing production contract code.

This branch builds on 5e48f22 to keep the initial Budokan dependency compatible with its deployed game-token API; unrelated newer game-status changes are not included in that pin. No declarations or upgrade transactions.

Review follow-up: the README now names all five APIs. Two new ERC721 deposit cases verify the sponsor, host destination, full-width token ID and stored record through both host storage and component adapters. Matched prize-source coverage increases from 364/390 to 366/390; the new prize API increases from 65/69 to 67/69 executable lines. The two remaining uncovered API lines are existing unreachable extension-variant fallback branches: get_token_record always constructs a token variant. The leaderboard narrow-getter finding was already fixed in ca5d8f2.

@coderabbitai

coderabbitai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e485144e-0a8f-4ad3-a3cf-3ab117b50570

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-medium Code Review: 0f9d64f

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-high Code Review: 0f9d64f

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-medium Code Review: 0f9d64f

I've finished reading the scoped diff and the store code behind it. One low-severity gas finding remains.

[LOW] packages/metagame/src/leaderboard/api.cairo:29 - get_leaderboard_entries, get_leaderboard_entry (line 43) and get_top_leaderboard_entries (line 55) call self.leaderboard_config(context_id) only to read .game_address. Through the new StoredConfiguration impl in leaderboard_component.cairo, that reads three storage slots (max_entries, ascending, game_address). Before this PR, these component views read only game_address.
Impact: Every call through the standard component, including on-chain calls such as a prize contract reading get_leaderboard_entries / get_top_leaderboard_entries, now does two extra storage reads. The PR says the standard component keeps its existing behavior, but its gas cost rises.
Fix: Add a separate game_address method to the trait, with a default implementation, and override it in the component:

pub trait Configuration<T> {
    fn leaderboard_config(self: @T, context_id: u64) -> LeaderboardStoreConfig;
    fn leaderboard_game_address(self: @T, context_id: u64) -> ContractAddress {
        Self::leaderboard_config(self, context_id).game_address
    }
}

In StoredConfiguration, implement leaderboard_game_address as self.game_address.read(context_id). In the three views, use self.leaderboard_game_address(context_id). Hosts that use packed storage keep the default, so their code does not change.

I also checked the PR's assumptions; no other actionable findings:

  • Moved logic: The refactored entry-requirement setter does the same checks and writes as before. The validation stays in api.cairo instead of reusing assert_valid_entry_requirement, because that function uses different error messages ("Token address … interface"). Keeping it there is what preserves the existing revert strings.
  • Exposure: Neither submit_score nor set_entry_requirement is reachable from outside the contract. Both are #[generate_trait] internal impls, not embeddable.
  • Existing integrations: LeaderboardHooksTrait is still re-exported at LeaderboardComponent::LeaderboardHooksTrait, so existing hook implementations keep working.

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-high Code Review: 0f9d64f

lgtm

@codecov

codecov Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.39759% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/metagame/src/prize/api.cairo 97.10% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-medium Code Review: ca5d8f2

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-high Code Review: ca5d8f2

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-medium Code Review: ca5d8f2

Quick update: I've finished reading the scoped diff. I didn't find anything that needs a change.

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-high Code Review: ca5d8f2

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-medium Code Review: fea50c0

[LOW] README.md:61 - The new paragraph says host-owned storage applies only to "Leaderboard and entry-requirement APIs". The head commit (fea50c0) also adds api/storage_adapter modules for registration, entry_fee and prize, and HOST_STORAGE.md covers them in its "Registration, entry fees and prizes" section.
Impact: People reading the top-level README will conclude that registration, entry-fee and prize hosts still have to embed their components. They won't find the shared RegistrationImpl, EntryFeeImpl or PrizeImpl APIs, and they may keep duplicating storage, which is what this PR is meant to stop.
Fix: Name all five modules, e.g.:

Leaderboard, registration, entry-requirement, entry-fee and prize APIs also
support application-owned storage without embedding their components. See
[host-owned storage](packages/metagame/HOST_STORAGE.md) for configuration
providers, optional component adapters and upgrade compatibility.

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-high Code Review: fea50c0

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-medium Code Review: fea50c0

lgtm

I found no actionable issues in the scoped packages/ diff.

  • Delegation: I compared each of the five components' delegations with the code it replaced. Every one is behavior-equivalent: same validation, same revert messages, same order of transfers and extension calls, same claim bookkeeping and same storage reads.
  • Leaderboard reads: get_top_leaderboard_entries now goes through get_range(…, 0, count, …), which caps the result at the stored entry count the same way the old loop did. start is 0, so start + count can't overflow. The component's StoredConfiguration keeps single-slot reads for the game address and max entries.
  • Entry requirements: set_entry_requirement still checks SRC5 support before writing. The writes in EntryRequirementStoreTrait::set_entry_requirement (token or extension address, then metadata) match the old inline code.
  • Hooks: LeaderboardHooksTrait was moved to hooks.cairo and is re-exported from the component, so existing import paths keep working.
  • Adapters: The ComponentStore adapters just forward to the components' existing Store impls. They don't overlap with those impls, because nothing implements HasComponent<ComponentState<_>>.
  • Registration/fee packing: The packed registration/fee fixture keeps the refund bit separate when it reads and writes registration state, as HOST_STORAGE.md describes.

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-high Code Review: fea50c0

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-high Code Review: 382a23f

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-xhigh Code Review: 382a23f

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-high Code Review: 382a23f

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-xhigh Code Review: 382a23f

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-high Code Review: a541596

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-xhigh Code Review: a541596

lgtm

@github-actions

Copy link
Copy Markdown

claude-opus-5-5-high Code Review: a541596

lgtm

@github-actions

Copy link
Copy Markdown

gpt-6.1-sol-xhigh Code Review: a541596

lgtm

@starknetdev
starknetdev merged commit 3400eff into main Oct 11, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant