Skip to content

feat: adventurer oracle — general objective checker for Death Mountain adventurers - #31

Merged
starknetdev merged 2 commits into
mainfrom
feat/adventurer-oracle
Jul 8, 2026
Merged

feat: adventurer oracle — general objective checker for Death Mountain adventurers#31
starknetdev merged 2 commits into
mainfrom
feat/adventurer-oracle

Conversation

@starknetdev

Copy link
Copy Markdown
Member

Layer 1 (the foundation) of the beast-mode achievement system: a reusable adventurer oracle — a general objective checker on Death Mountain adventurer NFTs. The tournament validator becomes a thin wrapper on top later; the beast achievements promotion is the first consumer.

What it does

  • create_objective(config) → u32 (owner-gated) + completed_objective(token_id, objective_id) → bool.
  • Conforms to game-components IMinigameObjectives / IMinigameObjectivesDetails (registers the same SRC5 id) — a drop-in objective provider that generalizes the score-only game_token.cairo impl to any adventurer metric.

Schema

  • Metric: xp, level, gold, health, beast_health, stat_upgrades_available, all 7 stats (strength…luck), and item checks (ItemHeldAnywhere / ItemEquipped / ItemInBag / ItemGreatness). Adding a scalar metric is a one-liner in oracle_lib::scalar_value.
  • Comparator: AtLeast, AtMost, Equal, GreaterThan, LessThan, NotEqual.
  • ObjectiveConfig { settings_id, metric, comparator, target, aux }target is the threshold (or item id for item checks); aux is the greatness threshold for ItemGreatness.

State reads + settings validation

completed_objective (1) requires the objective to exist, (2) reads the token's minted settings_id and requires it equals the objective's (mismatch → false), (3) fetches live (Adventurer, Bag), (4) evaluates via pure oracle_lib. Three constructor sources map to prod contracts: GameCore load_assets (stat-boosted adventurer + bag, so Luck/boosted stats are correct), Denshokan settings_id(token_id), GameToken settings_exist.

Tests

scarb build clean; snforge test 19 passed (scalars, stats, all comparators, equipped/bag/held-anywhere/greatness item checks, create/permissioning, settings-id mismatch → false, unknown-objective → false, batch, ownership, + 2 fuzz); scarb fmt --check clean.

Follow-up before mainnet (recommended)

types.cairo mirrors the game's Adventurer/Bag structs by Serde field order (to avoid a Scarb 2.15/2.16 + Ekubo dependency clash). The mocks share those mirrors, so they can't catch a layout drift vs the real game contract. A fork test against a live adventurer (decode load_assets output on mainnet) would lock the real Serde layout end-to-end — worth adding before this gates real rewards.

Scoped to the oracle only; the validator adapter, reward campaigns, backend worker, and client achievements panel are subsequent phases.

🤖 Generated with Claude Code

…adventurers

New package `packages/adventurer_oracle` — the reusable primitive for adventurer
achievements (and, later, a thin Budokan entry-requirement validator).

- create_objective(config) -> u32 (owner-gated) + completed_objective(token_id,
  objective_id) -> bool, conforming to game-components IMinigameObjectives /
  IMinigameObjectivesDetails (same SRC5 id) — generalizes the score-only impl to
  any adventurer metric.
- Metric: xp/level/gold/health/beast_health/stat_upgrades + all 7 stats + item
  checks (held-anywhere/equipped/in-bag/greatness). Comparator: AtLeast/AtMost/
  Equal/GreaterThan/LessThan/NotEqual. ObjectiveConfig { settings_id, metric,
  comparator, target, aux }.
- Reads live state from three prod sources (constructor args): GameCore
  load_assets (stat-boosted adventurer + bag), Denshokan settings_id(token_id),
  GameToken settings_exist — validates the token's settings_id against the
  objective before checking.
- oracle_lib holds pure evaluation (level/greatness formulas, comparators),
  unit- + fuzz-tested; mock_game drives deterministic integration tests.

scarb build clean; snforge 19 passed (incl. 2 fuzz); scarb fmt --check clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

GPT Code Review

Review execution failed (exit code 1).

The Codex CLI log was not posted because it can contain the full review prompt.
See the workflow run for job status: https://github.com/Provable-Games/metagame_extensions/actions/runs/28886872039

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Claude Code Review

Review execution failed.

Spending cap reached resets 7pm

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

Copy link
Copy Markdown

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 introduces the adventurer_oracle package, which implements a reusable objective-checker for Death Mountain adventurer NFTs. It includes the AdventurerOracle contract, pure evaluation logic in oracle_lib, local state struct mirrors, mock contracts, and comprehensive tests. The review feedback highlights two critical bugs in oracle_lib.cairo where checking for an item ID of 0 (representing an empty slot) can incorrectly return true for both is_equipped and is_in_bag. Additionally, it is recommended to add input validation for objective configurations during creation in oracle.cairo to prevent registering invalid objectives.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +40 to +49
pub fn is_equipped(equipment: Equipment, item_id: u8) -> bool {
equipment.weapon.id == item_id
|| equipment.chest.id == item_id
|| equipment.head.id == item_id
|| equipment.waist.id == item_id
|| equipment.foot.id == item_id
|| equipment.hand.id == item_id
|| equipment.neck.id == item_id
|| equipment.ring.id == item_id
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

If item_id is 0 (which represents an empty slot), is_equipped will return true if any of the equipment slots is empty. Since 0 is not a valid item ID, checking if an adventurer has item 0 equipped should return false. We should add a check to ensure item_id != 0 before checking the slots.

pub fn is_equipped(equipment: Equipment, item_id: u8) -> bool {
    item_id != 0
        && (equipment.weapon.id == item_id
            || equipment.chest.id == item_id
            || equipment.head.id == item_id
            || equipment.waist.id == item_id
            || equipment.foot.id == item_id
            || equipment.hand.id == item_id
            || equipment.neck.id == item_id
            || equipment.ring.id == item_id)
}

Comment on lines +52 to +68
pub fn is_in_bag(bag: Bag, item_id: u8) -> bool {
bag.item_1.id == item_id
|| bag.item_2.id == item_id
|| bag.item_3.id == item_id
|| bag.item_4.id == item_id
|| bag.item_5.id == item_id
|| bag.item_6.id == item_id
|| bag.item_7.id == item_id
|| bag.item_8.id == item_id
|| bag.item_9.id == item_id
|| bag.item_10.id == item_id
|| bag.item_11.id == item_id
|| bag.item_12.id == item_id
|| bag.item_13.id == item_id
|| bag.item_14.id == item_id
|| bag.item_15.id == item_id
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Similar to is_equipped, if item_id is 0, is_in_bag will return true if any of the 15 bag slots is empty. We should add a check to ensure item_id != 0 before checking the slots.

pub fn is_in_bag(bag: Bag, item_id: u8) -> bool {
    item_id != 0
        && (bag.item_1.id == item_id
            || bag.item_2.id == item_id
            || bag.item_3.id == item_id
            || bag.item_4.id == item_id
            || bag.item_5.id == item_id
            || bag.item_6.id == item_id
            || bag.item_7.id == item_id
            || bag.item_8.id == item_id
            || bag.item_9.id == item_id
            || bag.item_10.id == item_id
            || bag.item_11.id == item_id
            || bag.item_12.id == item_id
            || bag.item_13.id == item_id
            || bag.item_14.id == item_id
            || bag.item_15.id == item_id)
}

Comment on lines +201 to +208
self.assert_only_owner();

// Validate the referenced game settings exist.
let exists = IGameSettingsSourceDispatcher {
contract_address: self.settings_source.read(),
}
.settings_exist(config.settings_id);
assert(exists, Errors::SETTINGS_MISSING);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

It is highly recommended to validate the config parameters at objective creation time to prevent registering invalid objectives that can never be completed (e.g., item ID 0, item ID > 255, greatness threshold > 20, or non-zero aux for scalar metrics).

            self.assert_only_owner();

            // Validate config parameters
            match config.metric {
                Metric::ItemHeldAnywhere | Metric::ItemEquipped | Metric::ItemInBag => {
                    assert(config.target > 0 && config.target <= 255, 'Oracle: invalid item id');
                    assert(config.aux == 0, 'Oracle: aux must be 0');
                },
                Metric::ItemGreatness => {
                    assert(config.target > 0 && config.target <= 255, 'Oracle: invalid item id');
                    assert(config.aux <= 20, 'Oracle: greatness max is 20');
                },
                _ => {
                    assert(config.aux == 0, 'Oracle: aux must be 0');
                }
            }

            // Validate the referenced game settings exist.
            let exists = IGameSettingsSourceDispatcher {
                contract_address: self.settings_source.read(),
            }
                .settings_exist(config.settings_id);
            assert(exists, Errors::SETTINGS_MISSING);

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…+ optional item set

Collapse create_objective + create_item_set_objective into ONE endpoint and
generalize an objective from a single check to a CONJUNCTION:

  create_objective(name, description, settings_id,
                   conditions: Array<Condition>,      // scalars/single-item, all ANDed
                   items: Option<Array<u8>>)          // "own ALL these" (held anywhere)

An objective is complete iff settings match AND every condition passes AND (when
present) the adventurer holds every item in the set. This makes:
  - a simple goal ("reach level 10") one condition,
  - a composite goal ("score + gold + holds an item") several conditions,
  - "own this whole set of items" the objective's `items` list (as an Option),
all through a single entrypoint — no separate item-set metrics or endpoint.

- interface: `Condition { metric, comparator, target, aux }` (settings_id moves to
  the objective); new getters get_objective_settings_id / _conditions / _items.
  Drops the ItemSet* metrics.
- oracle_lib: `evaluate` -> `evaluate_condition`; add `holds_all_items` (empty set
  vacuously true, zero id never held).
- oracle: per-objective conditions + item-set storage; completed_objective ANDs
  them; objectives_details renders one row per condition + an item count.

Supersedes the item-set-metric approach (PR #33). 25 snforge tests (composite,
item-set, items-only, empty-objective + zero-id guards). scarb build + fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@starknetdev

Copy link
Copy Markdown
Member Author

Updated (commit ca080f0): objectives are now a single unified endpoint — create_objective(name, description, settings_id, conditions: Array<Condition>, items: Option<Array<u8>>). An objective is an AND of conditions (each a scalar or single-item check) plus an optional 'own all these items' set. This supersedes #33 (item-set metrics) — 'hold a set of item ids' is now the items Option, so simple, composite (score+gold+items), and item-set objectives all go through one function. 25 snforge tests green; consumers (validator #32, backend, client) are unaffected (still objective_id -> bool).

@starknetdev
starknetdev merged commit 04a0ed9 into main Jul 8, 2026
8 of 10 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