Skip to content

Commit 6700d5d

Browse files
authored
Merge branch 'next' into feat/add-tokenomics-package
2 parents 47d9ceb + 0725228 commit 6700d5d

118 files changed

Lines changed: 7010 additions & 2772 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Scarb.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ members = [
33
"packages/interfaces",
44
"packages/metagame",
55
"packages/minigame",
6+
"packages/registry",
67
"packages/token",
78
"packages/tokenomics",
8-
# Test packages can be uncommented when needed
9-
"packages/test_starknet",
9+
"tests",
1010
"packages/utils",
1111
"packages/leaderboard",
1212
"packages/presets",
@@ -46,11 +46,12 @@ starknet.workspace = true
4646
game_components_interfaces = { path = "packages/interfaces" }
4747
game_components_metagame = { path = "packages/metagame" }
4848
game_components_minigame = { path = "packages/minigame" }
49+
game_components_registry = { path = "packages/registry" }
4950
game_components_token = { path = "packages/token" }
5051
game_components_tokenomics = { path = "packages/tokenomics" }
5152
# Test packages
5253
# game_components_test_dojo = { path = "packages/test_dojo" } # Excluded - must be built separately
53-
game_components_test_starknet = { path = "packages/test_starknet" }
54+
game_components_tests = { path = "tests" }
5455
game_components_utils = { path = "packages/utils" }
5556
game_components_leaderboard = { path = "packages/leaderboard" }
5657
game_components_presets = { path = "packages/presets" }

packages/metagame/Scarb.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition.workspace = true
88
[dependencies]
99
starknet.workspace = true
1010
game_components_minigame = { path = "../minigame" }
11+
game_components_registry = { path = "../registry" }
1112
game_components_token = { path = "../token" }
1213
game_components_utils = { path = "../utils" }
1314
openzeppelin_introspection.workspace = true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod callback;
12
pub mod context;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod callback;
2+
pub mod interface;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// ==============================================================================
2+
// METAGAME CALLBACK COMPONENT
3+
// ==============================================================================
4+
// A reusable component for receiving callbacks from token contracts when game
5+
// state changes (score updates, game over, objectives completed).
6+
//
7+
// Uses the hooks pattern: the component provides infrastructure and SRC5
8+
// registration, while implementations define actual callback behavior via traits.
9+
10+
#[starknet::component]
11+
pub mod MetagameCallbackComponent {
12+
use openzeppelin_introspection::src5::SRC5Component;
13+
use openzeppelin_introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait;
14+
use crate::extensions::callback::interface::{IMETAGAME_CALLBACK_ID, IMetagameCallback};
15+
16+
// ==========================================================================
17+
// STORAGE
18+
// ==========================================================================
19+
20+
#[storage]
21+
pub struct Storage {}
22+
23+
// ==========================================================================
24+
// HOOKS TRAIT
25+
// ==========================================================================
26+
// Allows the embedding contract to define custom callback behavior.
27+
// Implementers can aggregate scores, emit events, update leaderboards, etc.
28+
29+
pub trait MetagameCallbackHooksTrait<TContractState> {
30+
/// Called when a token's score is updated.
31+
/// @param token_id The token ID (packed u256)
32+
/// @param score The new score value
33+
fn on_score_update(ref self: TContractState, token_id: u256, score: u32);
34+
35+
/// Called when a game ends (game_over transitions to true).
36+
/// @param token_id The token ID (packed u256)
37+
/// @param final_score The final score when game ended
38+
fn on_game_over(ref self: TContractState, token_id: u256, final_score: u32);
39+
40+
/// Called when all objectives are completed.
41+
/// @param token_id The token ID (packed u256)
42+
fn on_objective_complete(ref self: TContractState, token_id: u256);
43+
}
44+
45+
// ==========================================================================
46+
// EMBEDDABLE IMPLEMENTATION
47+
// ==========================================================================
48+
// Implements IMetagameCallback by delegating to hooks.
49+
// Contracts must provide a MetagameCallbackHooksTrait implementation.
50+
51+
#[embeddable_as(MetagameCallbackImpl)]
52+
impl MetagameCallback<
53+
TContractState,
54+
+HasComponent<TContractState>,
55+
+Drop<TContractState>,
56+
+MetagameCallbackHooksTrait<TContractState>,
57+
> of IMetagameCallback<ComponentState<TContractState>> {
58+
fn on_score_update(ref self: ComponentState<TContractState>, token_id: u256, score: u32) {
59+
let mut contract = self.get_contract_mut();
60+
MetagameCallbackHooksTrait::on_score_update(ref contract, token_id, score);
61+
}
62+
63+
fn on_game_over(
64+
ref self: ComponentState<TContractState>, token_id: u256, final_score: u32,
65+
) {
66+
let mut contract = self.get_contract_mut();
67+
MetagameCallbackHooksTrait::on_game_over(ref contract, token_id, final_score);
68+
}
69+
70+
fn on_objective_complete(ref self: ComponentState<TContractState>, token_id: u256) {
71+
let mut contract = self.get_contract_mut();
72+
MetagameCallbackHooksTrait::on_objective_complete(ref contract, token_id);
73+
}
74+
}
75+
76+
// ==========================================================================
77+
// INTERNAL IMPLEMENTATION
78+
// ==========================================================================
79+
80+
#[generate_trait]
81+
pub impl InternalImpl<
82+
TContractState,
83+
+HasComponent<TContractState>,
84+
impl SRC5: SRC5Component::HasComponent<TContractState>,
85+
+Drop<TContractState>,
86+
> of InternalTrait<TContractState> {
87+
/// Initializes the component by registering the SRC5 interface.
88+
/// Should be called in the contract's constructor.
89+
fn initializer(ref self: ComponentState<TContractState>) {
90+
let mut src5_component = get_dep_component_mut!(ref self, SRC5);
91+
src5_component.register_interface(IMETAGAME_CALLBACK_ID);
92+
}
93+
}
94+
}
95+
96+
// ==============================================================================
97+
// EMPTY HOOKS IMPLEMENTATION
98+
// ==============================================================================
99+
// Provides a no-op implementation for contracts that don't need custom behavior.
100+
// Use this when you want to receive callbacks but don't need to process them.
101+
102+
pub impl MetagameCallbackHooksEmptyImpl<
103+
TContractState,
104+
> of MetagameCallbackComponent::MetagameCallbackHooksTrait<TContractState> {
105+
fn on_score_update(
106+
ref self: TContractState, token_id: u256, score: u32,
107+
) { // No-op: contracts can override for custom score handling
108+
}
109+
110+
fn on_game_over(
111+
ref self: TContractState, token_id: u256, final_score: u32,
112+
) { // No-op: contracts can override for custom game over handling
113+
}
114+
115+
fn on_objective_complete(
116+
ref self: TContractState, token_id: u256,
117+
) { // No-op: contracts can override for custom objectives handling
118+
}
119+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// ==============================================================================
2+
// METAGAME CALLBACK INTERFACE
3+
// ==============================================================================
4+
// Interface for metagame contracts to receive automatic callbacks from token
5+
// contracts when game state changes (score updates, game over, objectives
6+
// completed). This enables tournament score aggregation without manual sync.
7+
8+
/// Interface ID for IMetagameCallback (SRC5 compliant)
9+
/// Computed as: H(on_score_update) XOR H(on_game_over) XOR H(on_objective_complete)
10+
pub const IMETAGAME_CALLBACK_ID: felt252 =
11+
0x04d4f4758b99dcb4f1e2dc37c3a6e8c7a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6;
12+
13+
/// Interface for metagame contracts to receive callbacks from token contracts
14+
#[starknet::interface]
15+
pub trait IMetagameCallback<TState> {
16+
/// Called when a token's score is updated
17+
/// @param token_id The token ID (packed u256)
18+
/// @param score The new score value
19+
fn on_score_update(ref self: TState, token_id: u256, score: u32);
20+
21+
/// Called when a game ends (game_over transitions to true)
22+
/// @param token_id The token ID (packed u256)
23+
/// @param final_score The final score when game ended
24+
fn on_game_over(ref self: TState, token_id: u256, final_score: u32);
25+
26+
/// Called when the objective is completed
27+
/// @param token_id The token ID (packed u256)
28+
fn on_objective_complete(ref self: TState, token_id: u256);
29+
}

packages/metagame/src/interface.cairo

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,3 @@ pub trait IMetagame<TContractState> {
88
fn context_address(self: @TContractState) -> ContractAddress;
99
fn default_token_address(self: @TContractState) -> ContractAddress;
1010
}
11-

packages/metagame/src/lib.cairo

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ pub mod extensions;
22
pub mod interface;
33
pub mod libs;
44
pub mod metagame;
5+
pub mod structs;
56
pub mod ticket_booth;

packages/metagame/src/libs.cairo

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use game_components_metagame::extensions::context::structs::GameContextDetails;
22
use game_components_minigame::interface::{IMinigameDispatcher, IMinigameDispatcherTrait};
3+
use game_components_registry::interface::{
4+
IMinigameRegistryDispatcher, IMinigameRegistryDispatcherTrait,
5+
};
36
use game_components_token::core::interface::{
47
IMinigameTokenDispatcher, IMinigameTokenDispatcherTrait,
58
};
6-
use game_components_token::examples::minigame_registry_contract::{
7-
IMinigameRegistryDispatcher, IMinigameRegistryDispatcherTrait,
8-
};
9+
use game_components_token::structs::MintParams;
910
use starknet::ContractAddress;
11+
use crate::structs::MintMetagameParams;
1012

1113
/// Asserts that a game is registered in the minigame token contract
1214
///
@@ -36,7 +38,7 @@ pub fn assert_game_registered(game_address: ContractAddress) {
3638
/// * `settings_id` - Optional settings ID
3739
/// * `start` - Optional start time
3840
/// * `end` - Optional end time
39-
/// * `objective_ids` - Optional objective IDs
41+
/// * `objective_id` - Optional objective ID
4042
/// * `context` - Optional context data
4143
/// * `client_url` - Optional client URL
4244
/// * `renderer_address` - Optional renderer contract address
@@ -52,7 +54,7 @@ pub fn mint(
5254
settings_id: Option<u32>,
5355
start: Option<u64>,
5456
end: Option<u64>,
55-
objective_ids: Option<Span<u32>>,
57+
objective_id: Option<u32>,
5658
context: Option<GameContextDetails>,
5759
client_url: Option<ByteArray>,
5860
renderer_address: Option<ContractAddress>,
@@ -75,7 +77,7 @@ pub fn mint(
7577
settings_id,
7678
start,
7779
end,
78-
objective_ids,
80+
objective_id,
7981
context,
8082
client_url,
8183
renderer_address,
@@ -96,7 +98,7 @@ pub fn mint(
9698
settings_id,
9799
start,
98100
end,
99-
objective_ids,
101+
objective_id,
100102
context,
101103
client_url,
102104
renderer_address,
@@ -106,3 +108,57 @@ pub fn mint(
106108
},
107109
}
108110
}
111+
112+
/// Mints multiple game tokens in batch through minigame token contracts
113+
///
114+
/// # Arguments
115+
/// * `default_token_address` - The default token address for minting when no game_address is
116+
/// provided * `mints` - Array of mint parameters for each token
117+
///
118+
/// # Returns
119+
/// * `Array<u64>` - Array of minted token IDs
120+
pub fn mint_batch(
121+
default_token_address: ContractAddress, mints: Array<MintMetagameParams>,
122+
) -> Array<u64> {
123+
let mut token_ids = array![];
124+
let mut index = 0;
125+
126+
loop {
127+
if index >= mints.len() {
128+
break;
129+
}
130+
131+
let mint_param = mints.at(index);
132+
133+
// Clone non-copyable Option types
134+
let context_clone = match mint_param.context {
135+
Option::Some(ctx) => Option::Some(ctx.clone()),
136+
Option::None => Option::None,
137+
};
138+
139+
let client_url_clone = match mint_param.client_url {
140+
Option::Some(url) => Option::Some(url.clone()),
141+
Option::None => Option::None,
142+
};
143+
144+
let token_id = mint(
145+
default_token_address,
146+
*mint_param.game_address,
147+
*mint_param.player_name,
148+
*mint_param.settings_id,
149+
*mint_param.start,
150+
*mint_param.end,
151+
*mint_param.objective_id,
152+
context_clone,
153+
client_url_clone,
154+
*mint_param.renderer_address,
155+
*mint_param.to,
156+
*mint_param.soulbound,
157+
);
158+
159+
token_ids.append(token_id);
160+
index += 1;
161+
}
162+
163+
token_ids
164+
}

packages/metagame/src/metagame.cairo

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod MetagameComponent {
1616
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
1717
use crate::interface::{IMETAGAME_ID, IMetagame};
1818
use crate::libs;
19+
use crate::structs::MintMetagameParams;
1920

2021
#[storage]
2122
pub struct Storage {
@@ -93,7 +94,7 @@ pub mod MetagameComponent {
9394
settings_id: Option<u32>,
9495
start: Option<u64>,
9596
end: Option<u64>,
96-
objective_ids: Option<Span<u32>>,
97+
objective_id: Option<u32>,
9798
context: Option<GameContextDetails>,
9899
client_url: Option<ByteArray>,
99100
renderer_address: Option<ContractAddress>,
@@ -107,13 +108,19 @@ pub mod MetagameComponent {
107108
settings_id,
108109
start,
109110
end,
110-
objective_ids,
111+
objective_id,
111112
context,
112113
client_url,
113114
renderer_address,
114115
to,
115116
soulbound,
116117
)
117118
}
119+
120+
fn mint_batch(
121+
ref self: ComponentState<TContractState>, mints: Array<MintMetagameParams>,
122+
) -> Array<u64> {
123+
libs::mint_batch(self.default_token_address.read(), mints)
124+
}
118125
}
119126
}

0 commit comments

Comments
 (0)