Skip to content

fix: Settings Address Zero Check Fix#5

Open
FemiOje wants to merge 2 commits intoProvable-Games:mainfrom
FemiOje:fix/token-uri
Open

fix: Settings Address Zero Check Fix#5
FemiOje wants to merge 2 commits intoProvable-Games:mainfrom
FemiOje:fix/token-uri

Conversation

@FemiOje
Copy link

@FemiOje FemiOje commented Nov 2, 2025

Fix: Prevent panic when settings_address is zero in token_uri

Summary

This PR fixes a bug in FullTokenContract.token_uri() where the function would panic when attempting to call a contract at a zero settings_address. The fix adds a zero-check before making the contract call, preventing panics and returning default settings details instead.

Problem

When token_uri() is called on a token associated with a game that has a zero settings_address, the function would attempt to call a contract at address 0x0, causing a panic with the error:

Requested contract address 0x0000000000000000000000000000000000000000000000000000000000000000 is not deployed.

This occurred because the code would call call_contract_syscall() with a zero address without checking if the address was valid first.

Solution

Added a zero-check using settings_address.is_zero() before attempting to call the settings contract. When settings_address is zero, the function now returns a default GameSettingDetails struct instead of attempting the contract call.

Changes

  1. packages/token/src/examples/full_token_contract.cairo:

    • Added zero-check before call_contract_syscall for settings_address
    • Returns default GameSettingDetails when settings_address is zero
    • Reorganized imports (no functional changes)
  2. packages/test_starknet/src/token/test_token_uri_settings_address.cairo:

    • New test file with focused test test_token_uri_with_zero_settings_address
    • Verifies that token_uri() does not panic when settings_address is zero
  3. packages/test_starknet/src/token.cairo:

    • Added new test module to test suite

Code Changes

Before (Buggy Code)

let mut settings_calldata = array![];
settings_calldata.append(token_metadata.settings_id.into());

let settings_details =
    match call_contract_syscall(
        settings_address, settings_details_selector, settings_calldata.span(),
    ) {
        // ... error handling
    };

After (Fixed Code)

// Check if settings_address is zero before attempting contract call
let settings_details = if settings_address.is_zero() {
    GameSettingDetails { name: "", description: "", settings: array![].span() }
} else {
    let mut settings_calldata = array![];
    settings_calldata.append(token_metadata.settings_id.into());

    match call_contract_syscall(
        settings_address, settings_details_selector, settings_calldata.span(),
    ) {
        // ... error handling
    }
};

Testing

  • ✅ New test test_token_uri_with_zero_settings_address passes
  • ✅ Test verifies that token_uri() returns a valid token URI (non-empty) when settings_address is zero
  • ✅ No panics occur when calling token_uri() with zero settings_address

Checklist

  • Code changes follow the project's style guidelines
  • Tests added/updated
  • Tests pass locally
  • Changes are minimal and focused on the bug fix
  • No breaking changes introduced

Summary by CodeRabbit

  • Bug Fixes

    • Improved token URI handling to gracefully manage cases where settings address is not configured, using sensible defaults instead of failing.
  • Tests

    • Added new test coverage for token URI functionality with zero settings address configuration.

@FemiOje FemiOje requested a review from starknetdev as a code owner November 2, 2025 22:06
@coderabbitai
Copy link

coderabbitai bot commented Nov 2, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This pull request adds a new test module for token URI behavior when settings_address is zero, and updates the full token contract to handle this case gracefully by using default settings and avoiding unnecessary syscalls when the address is zero.

Changes

Cohort / File(s) Summary
Test Module Addition
packages/test_starknet/src/token.cairo
Added new test module dependency test_token_uri_settings_address guarded by #[cfg(test)] to include additional test coverage.
New Test Implementation
packages/test_starknet/src/token/test_token_uri_settings_address.cairo
New test file with test_token_uri_with_zero_settings_address that verifies token URI generation succeeds and returns a non-empty value when settings_address is zero, confirming graceful fallback behavior.
Contract Logic Update
packages/token/src/examples/full_token_contract.cairo
Modified settings_details retrieval logic to guard against zero addresses (avoiding unnecessary syscalls), use default GameSettingDetails when settings_address is zero, added default_settings constant for consistent fallback behavior, and reorganized imports to align with new usage patterns.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant FullTokenContract
    participant SettingsContract

    Note over FullTokenContract: New Logic (with zero-address guard)
    Caller->>FullTokenContract: Call token_uri()
    FullTokenContract->>FullTokenContract: Check if settings_address == ZERO_ADDRESS
    alt settings_address is zero
        FullTokenContract->>FullTokenContract: Use default_settings
        Note over FullTokenContract: Return token_uri with default settings
    else settings_address is non-zero
        FullTokenContract->>SettingsContract: Call settings_details()
        alt Call succeeds
            SettingsContract-->>FullTokenContract: Return GameSettingDetails
        else Call fails
            FullTokenContract->>FullTokenContract: Use default_settings as fallback
            Note over FullTokenContract: Consistent error handling
        end
        FullTokenContract->>FullTokenContract: Return token_uri with retrieved/fallback settings
    end
    FullTokenContract-->>Caller: token_uri
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Logic modifications in full_token_contract.cairo: The guard against zero addresses and default settings handling requires careful verification of control flow and error handling consistency.
  • Import reorganization: Verify that all reorganized and new imports are correct and necessary for the new logic.
  • Test setup and assertions: Review the new test module to ensure proper initialization, mock setup, and assertion logic for the zero settings_address scenario.

Poem

🐰 A rabbit hops through token URIs bright,
When settings vanish into zero night,
We guard the gates and default with grace,
No syscalls wasted on an empty place! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Settings Address Zero Check Fix" is directly related to the main change in the changeset. The PR's primary objective is to fix a panic in FullTokenContract.token_uri() by adding a zero-address check on the settings_address parameter; if the address is zero, the function returns a default GameSettingDetails instead of attempting a contract call. The title clearly and specifically summarizes this fix, allowing a teammate scanning history to understand the primary change. While there is minor stylistic redundancy with the "fix:" prefix and the word "Fix" in the title, the title remains clear and directly relevant to the changeset.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @FemiOje, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness of the FullTokenContract by addressing a vulnerability in its token_uri function. Previously, calling this function with a zero-address for game settings would lead to a contract panic. The changes introduce a protective measure to detect such invalid addresses, ensuring the contract can continue to operate stably by providing default settings in these edge cases, thus improving overall system reliability.

Highlights

  • Bug Fix: Resolved a critical bug in FullTokenContract.token_uri() that caused a panic when attempting to call a contract at a zero settings_address.
  • Zero-Address Check: Implemented a settings_address.is_zero() check before making the call_contract_syscall, preventing contract panics.
  • Default Settings: When settings_address is zero, the function now gracefully returns default GameSettingDetails instead of attempting an invalid contract call.
  • New Test Case: Added a dedicated test test_token_uri_with_zero_settings_address to verify the fix and ensure token_uri() functions correctly with a zero settings_address.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses a panic caused by a zero settings_address in token_uri. The fix, which involves adding a pre-check for the zero address, is correct and well-implemented. The inclusion of a new, focused test case is excellent as it ensures the bug is resolved and prevents regressions. I have one suggestion to refactor a small portion of the code to reduce duplication and improve maintainability.

Comment on lines +291 to 315
// Check if settings_address is zero before attempting contract call
let settings_details = if settings_address.is_zero() {
GameSettingDetails { name: "", description: "", settings: array![].span() }
} else {
let mut settings_calldata = array![];
settings_calldata.append(token_metadata.settings_id.into());

let settings_details =
match call_contract_syscall(
settings_address, settings_details_selector, settings_calldata.span(),
) {
Result::Ok(result) => {
// Try to deserialize the result as GameSettingDetails
let mut result_span = result;
match Serde::<GameSettingDetails>::deserialize(ref result_span) {
Option::Some(settings_details) => settings_details,
Option::None => GameSettingDetails {
name: "", description: "", settings: array![].span(),
},
}
},
Result::Err(_) => GameSettingDetails {
name: "", description: "", settings: array![].span(),
},
Result::Ok(result) => {
// Try to deserialize the result as GameSettingDetails
let mut result_span = result;
match Serde::<GameSettingDetails>::deserialize(ref result_span) {
Option::Some(settings_details) => settings_details,
Option::None => GameSettingDetails {
name: "", description: "", settings: array![].span(),
},
}
},
Result::Err(_) => GameSettingDetails {
name: "", description: "", settings: array![].span(),
},
}
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve maintainability and reduce code duplication, you can define a closure to create the default GameSettingDetails. This avoids repeating the struct instantiation in multiple places.

                // Check if settings_address is zero before attempting contract call
                let get_default_settings = || GameSettingDetails { name: "", description: "", settings: array![].span() };
                let settings_details = if settings_address.is_zero() {
                    get_default_settings()
                } else {
                    let mut settings_calldata = array![];
                    settings_calldata.append(token_metadata.settings_id.into());

                    match call_contract_syscall(
                        settings_address, settings_details_selector, settings_calldata.span(),
                    ) {
                        Result::Ok(result) => {
                            // Try to deserialize the result as GameSettingDetails
                            let mut result_span = result;
                            match Serde::<GameSettingDetails>::deserialize(ref result_span) {
                                Option::Some(settings_details) => settings_details,
                                Option::None => get_default_settings(),
                            }
                        },
                        Result::Err(_) => get_default_settings(),
                    }
                };

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/test_starknet/src/token/test_token_uri_settings_address.cairo (1)

22-90: Well-structured test for the zero-address fix.

The test correctly verifies that token_uri() no longer panics when settings_address is zero and returns a non-empty URI. The test setup properly initializes the game with ZERO_ADDRESS() for settings (line 55) and exercises the fixed code path.

Consider adding an assertion to verify that the returned URI contains expected default content (e.g., checking for presence of game name or specific metadata structure), rather than just checking non-empty. This would provide stronger validation that the default settings are being used correctly.

For example:

     // Verify that token_uri was generated successfully (non-empty)
-    // The exact content depends on the implementation, but it should not be empty
     assert!(token_uri.len() > 0, "Token URI should not be empty");
+    // Optionally verify it contains expected game metadata
+    assert!(token_uri.contains("TestGame"), "Token URI should contain game name");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3a58f0 and 3794406.

📒 Files selected for processing (3)
  • packages/test_starknet/src/token.cairo (1 hunks)
  • packages/test_starknet/src/token/test_token_uri_settings_address.cairo (1 hunks)
  • packages/token/src/examples/full_token_contract.cairo (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/*/src/**/*.cairo

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/*/src/**/*.cairo: Verify correct usage of component architecture with #[starknet::component] in Cairo code
Check SRC5 interface discovery implementation for capability detection in Cairo contracts
Ensure proper storage isolation using #[substorage(v0)] in Cairo contracts
Validate dispatcher pattern usage for cross-contract calls in Cairo code
Confirm extensive use of Option types for optional parameters in Cairo contracts
Verify interface IDs are defined as constants (e.g., IMINIGAME_ID) in Cairo contracts
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
Use descriptive variable names (e.g., liquidity_unlock_timestamp not t)
Contracts must follow this organization: 1. Interfaces 2. Events 3. Storage 4. Constructor 5. External functions 6. Internal functions
All error messages must be implemented as descriptive constants
Events must be emitted for all state changes in Cairo contracts
Review for proper implementation of optional extensions: settings, objectives, context, minter, renderer
Ensure game lifecycle is implemented: Setup → Mint → Play → Sync → Complete
Token metadata structure and state tracking must be properly implemented
Review for proper usage of account abstraction features in Starknet contracts
Review for proper usage of L1-L2 messaging patterns in Starknet contracts
Review for proper usage of STARK-friendly cryptography in contracts

Files:

  • packages/token/src/examples/full_token_contract.cairo
  • packages/test_starknet/src/token/test_token_uri_settings_address.cairo
  • packages/test_starknet/src/token.cairo
🧠 Learnings (12)
📚 Learning: 2025-08-01T09:49:40.817Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-01T09:49:40.817Z
Learning: Applies to packages/*/src/**/minigame_token*.cairo : Implement IMinigameTokenData trait with score() and game_over() functions in relevant contracts

Applied to files:

  • packages/token/src/examples/full_token_contract.cairo
  • packages/test_starknet/src/token/test_token_uri_settings_address.cairo
  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:50:10.672Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-01T09:50:10.672Z
Learning: Applies to src/**/*.cairo : Use OpenZeppelin contracts for token and introspection functionality.

Applied to files:

  • packages/token/src/examples/full_token_contract.cairo
📚 Learning: 2025-08-01T09:50:10.672Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-01T09:50:10.672Z
Learning: Applies to src/**/*.cairo : Use Option types for optional parameters in contracts.

Applied to files:

  • packages/token/src/examples/full_token_contract.cairo
📚 Learning: 2025-08-01T09:50:10.672Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-01T09:50:10.672Z
Learning: Applies to src/**/*.cairo : Use component architecture with #[starknet::component] in Cairo contracts.

Applied to files:

  • packages/token/src/examples/full_token_contract.cairo
  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:50:10.672Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-01T09:50:10.672Z
Learning: Applies to tests/integration/**/*.cairo : Add integration tests for cross-contract interactions.

Applied to files:

  • packages/test_starknet/src/token/test_token_uri_settings_address.cairo
  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:49:40.817Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-01T09:49:40.817Z
Learning: Applies to packages/*/src/**/*.cairo : Verify correct usage of component architecture with #[starknet::component] in Cairo code

Applied to files:

  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:49:40.817Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-01T09:49:40.817Z
Learning: Applies to packages/*/src/**/*.cairo : Review for proper usage of account abstraction features in Starknet contracts

Applied to files:

  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:49:40.817Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-01T09:49:40.817Z
Learning: Applies to packages/*/src/**/*.cairo : Ensure proper storage isolation using #[substorage(v0)] in Cairo contracts

Applied to files:

  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:49:40.817Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-01T09:49:40.817Z
Learning: Applies to packages/*/src/**/*.cairo : Token metadata structure and state tracking must be properly implemented

Applied to files:

  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:49:40.817Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-01T09:49:40.817Z
Learning: Applies to packages/*/src/**/*.cairo : Review for proper usage of STARK-friendly cryptography in contracts

Applied to files:

  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:50:10.672Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-01T09:50:10.672Z
Learning: Applies to src/**/*.cairo : Use StarkNet 2.10.1 for all StarkNet-specific code.

Applied to files:

  • packages/test_starknet/src/token.cairo
📚 Learning: 2025-08-01T09:50:10.672Z
Learnt from: CR
Repo: Provable-Games/game-components PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-01T09:50:10.672Z
Learning: Applies to src/**/*.cairo : Use storage isolation via #[substorage(v0)] in contracts.

Applied to files:

  • packages/test_starknet/src/token.cairo
🔇 Additional comments (2)
packages/test_starknet/src/token.cairo (1)

24-25: LGTM!

The new test module is properly integrated following the existing pattern with appropriate #[cfg(test)] guards.

packages/token/src/examples/full_token_contract.cairo (1)

5-9: LGTM!

Import reorganization properly supports the zero-address check implementation by adding necessary types (GameSettingDetails, GameContextDetails, etc.) and syscall support.

Also applies to: 15-17, 21-24, 30-30

Comment on lines +291 to 315
// Check if settings_address is zero before attempting contract call
let mut default_settings = GameSettingDetails {
name: "", description: "", settings: array![].span(),
};

let settings_details = if settings_address.is_zero() {
default_settings
} else {
let mut settings_calldata = array![];
settings_calldata.append(token_metadata.settings_id.into());

let settings_details =
match call_contract_syscall(
settings_address, settings_details_selector, settings_calldata.span(),
) {
Result::Ok(result) => {
// Try to deserialize the result as GameSettingDetails
let mut result_span = result;
match Serde::<GameSettingDetails>::deserialize(ref result_span) {
Option::Some(settings_details) => settings_details,
Option::None => GameSettingDetails {
name: "", description: "", settings: array![].span(),
},
}
},
Result::Err(_) => GameSettingDetails {
name: "", description: "", settings: array![].span(),
},
Result::Ok(result) => {
// Try to deserialize the result as GameSettingDetails
let mut result_span = result;
match Serde::<GameSettingDetails>::deserialize(ref result_span) {
Option::Some(settings_details) => settings_details,
Option::None => default_settings,
}
},
Result::Err(_) => default_settings,
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Good fix with minor refinement needed.

The zero-address check correctly prevents panics by avoiding unnecessary syscalls when settings_address is zero. The code properly reuses default_settings for all fallback scenarios, which addresses the previous review comment about duplication.

Issue: Remove unnecessary mut keyword on line 292:

-                let mut default_settings = GameSettingDetails {
+                let default_settings = GameSettingDetails {
                     name: "", description: "", settings: array![].span(),
                 };

The variable is never mutated, so mut is not needed.

Optional refinement: Consider adding a brief inline comment explaining the zero-address check for future maintainers:

                 // Check if settings_address is zero before attempting contract call
+                // to avoid panics on uninitialized/optional settings contracts
                 let default_settings = GameSettingDetails {
🤖 Prompt for AI Agents
In packages/token/src/examples/full_token_contract.cairo around lines 291 to
315, the variable declaration "let mut default_settings = ..." unnecessarily
uses the mut keyword even though the variable is never mutated; remove the mut
to make it "let default_settings = ...". Also optionally add a brief inline
comment above the zero-address check explaining that it avoids a syscall when
settings_address is zero for future maintainers.

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