diff --git a/indexer.db b/indexer.db index 9fed803..b10e48b 100644 Binary files a/indexer.db and b/indexer.db differ diff --git a/manifests/dev/base/abis/contracts/flippyflop-actions-4407e5f5.json b/manifests/dev/base/abis/contracts/flippyflop-actions-4407e5f5.json index 3ab98a8..1b4d6bc 100644 --- a/manifests/dev/base/abis/contracts/flippyflop-actions-4407e5f5.json +++ b/manifests/dev/base/abis/contracts/flippyflop-actions-4407e5f5.json @@ -161,7 +161,12 @@ { "type": "function", "name": "claim", - "inputs": [], + "inputs": [ + { + "name": "flipped_tiles", + "type": "core::array::Array::<(core::integer::u32, core::integer::u32)>" + } + ], "outputs": [], "state_mutability": "external" } diff --git a/manifests/dev/base/abis/models/flippyflop-Claim-c098f39e.json b/manifests/dev/base/abis/models/flippyflop-Claim-c098f39e.json new file mode 100644 index 0000000..12943ae --- /dev/null +++ b/manifests/dev/base/abis/models/flippyflop-Claim-c098f39e.json @@ -0,0 +1,421 @@ +[ + { + "type": "impl", + "name": "DojoModelImpl", + "interface_name": "dojo::model::model::IModel" + }, + { + "type": "struct", + "name": "core::byte_array::ByteArray", + "members": [ + { + "name": "data", + "type": "core::array::Array::" + }, + { + "name": "pending_word", + "type": "core::felt252" + }, + { + "name": "pending_word_len", + "type": "core::integer::u32" + } + ] + }, + { + "type": "enum", + "name": "core::option::Option::", + "variants": [ + { + "name": "Some", + "type": "core::integer::u32" + }, + { + "name": "None", + "type": "()" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::layout::FieldLayout", + "members": [ + { + "name": "selector", + "type": "core::felt252" + }, + { + "name": "layout", + "type": "dojo::model::layout::Layout" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::model::layout::Layout", + "variants": [ + { + "name": "Fixed", + "type": "core::array::Span::" + }, + { + "name": "Struct", + "type": "core::array::Span::" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + }, + { + "name": "Enum", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Member", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "ty", + "type": "dojo::model::introspect::Ty" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Struct", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::<(core::felt252, dojo::model::introspect::Ty)>", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::<(core::felt252, dojo::model::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "dojo::model::introspect::Enum", + "members": [ + { + "name": "name", + "type": "core::felt252" + }, + { + "name": "attrs", + "type": "core::array::Span::" + }, + { + "name": "children", + "type": "core::array::Span::<(core::felt252, dojo::model::introspect::Ty)>" + } + ] + }, + { + "type": "struct", + "name": "core::array::Span::", + "members": [ + { + "name": "snapshot", + "type": "@core::array::Array::" + } + ] + }, + { + "type": "enum", + "name": "dojo::model::introspect::Ty", + "variants": [ + { + "name": "Primitive", + "type": "core::felt252" + }, + { + "name": "Struct", + "type": "dojo::model::introspect::Struct" + }, + { + "name": "Enum", + "type": "dojo::model::introspect::Enum" + }, + { + "name": "Tuple", + "type": "core::array::Span::" + }, + { + "name": "Array", + "type": "core::array::Span::" + }, + { + "name": "ByteArray", + "type": "()" + } + ] + }, + { + "type": "interface", + "name": "dojo::model::model::IModel", + "items": [ + { + "type": "function", + "name": "name", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "tag", + "inputs": [], + "outputs": [ + { + "type": "core::byte_array::ByteArray" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "type": "core::integer::u8" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "selector", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "name_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "namespace_hash", + "inputs": [], + "outputs": [ + { + "type": "core::felt252" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "unpacked_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "packed_size", + "inputs": [], + "outputs": [ + { + "type": "core::option::Option::" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "layout", + "inputs": [], + "outputs": [ + { + "type": "dojo::model::layout::Layout" + } + ], + "state_mutability": "view" + }, + { + "type": "function", + "name": "schema", + "inputs": [], + "outputs": [ + { + "type": "dojo::model::introspect::Ty" + } + ], + "state_mutability": "view" + } + ] + }, + { + "type": "impl", + "name": "claimImpl", + "interface_name": "flippyflop::models::Iclaim" + }, + { + "type": "struct", + "name": "core::integer::u256", + "members": [ + { + "name": "low", + "type": "core::integer::u128" + }, + { + "name": "high", + "type": "core::integer::u128" + } + ] + }, + { + "type": "struct", + "name": "flippyflop::models::Claim", + "members": [ + { + "name": "player", + "type": "core::felt252" + }, + { + "name": "amount", + "type": "core::integer::u256" + } + ] + }, + { + "type": "interface", + "name": "flippyflop::models::Iclaim", + "items": [ + { + "type": "function", + "name": "ensure_abi", + "inputs": [ + { + "name": "model", + "type": "flippyflop::models::Claim" + } + ], + "outputs": [], + "state_mutability": "view" + } + ] + }, + { + "type": "event", + "name": "flippyflop::models::claim::Event", + "kind": "enum", + "variants": [] + } +] \ No newline at end of file diff --git a/manifests/dev/base/contracts/flippyflop-actions-4407e5f5.toml b/manifests/dev/base/contracts/flippyflop-actions-4407e5f5.toml index 50719dd..828b991 100644 --- a/manifests/dev/base/contracts/flippyflop-actions-4407e5f5.toml +++ b/manifests/dev/base/contracts/flippyflop-actions-4407e5f5.toml @@ -1,6 +1,6 @@ kind = "DojoContract" -class_hash = "0x5c1ec4f6bda7a3ad05182759b4c23fb2d5b40c97ae8da92dbff7317e51c7331" -original_class_hash = "0x5c1ec4f6bda7a3ad05182759b4c23fb2d5b40c97ae8da92dbff7317e51c7331" +class_hash = "0x4b4721d434ce198b907c12463c98a0c437abbf13fc239ffe7393d0adede908" +original_class_hash = "0x4b4721d434ce198b907c12463c98a0c437abbf13fc239ffe7393d0adede908" base_class_hash = "0x0" abi = "manifests/dev/base/abis/contracts/flippyflop-actions-4407e5f5.json" reads = [] diff --git a/manifests/dev/base/models/flippyflop-Claim-c098f39e.toml b/manifests/dev/base/models/flippyflop-Claim-c098f39e.toml new file mode 100644 index 0000000..4a5ba89 --- /dev/null +++ b/manifests/dev/base/models/flippyflop-Claim-c098f39e.toml @@ -0,0 +1,17 @@ +kind = "DojoModel" +class_hash = "0x7416d8eddcdb8106e3574223f03624a8290f2f012c5c4d628383d3ccb8a56a0" +original_class_hash = "0x7416d8eddcdb8106e3574223f03624a8290f2f012c5c4d628383d3ccb8a56a0" +abi = "manifests/dev/base/abis/models/flippyflop-Claim-c098f39e.json" +tag = "flippyflop-Claim" +qualified_path = "flippyflop::models::claim" +manifest_name = "flippyflop-Claim-c098f39e" + +[[members]] +name = "player" +type = "felt252" +key = true + +[[members]] +name = "amount" +type = "u256" +key = false diff --git a/src/models.cairo b/src/models.cairo index b6459f5..fa7ead5 100644 --- a/src/models.cairo +++ b/src/models.cairo @@ -54,6 +54,14 @@ impl PowerUpImpl of PowerUpTrait { } } +#[derive(Serde, Drop)] +#[dojo::model] +pub struct Claim { + #[key] + pub player: felt252, + pub amount: u256, +} + #[derive(Serde, Drop)] #[dojo::model] pub struct User { diff --git a/src/packing.cairo b/src/packing.cairo index d762efc..2034078 100644 --- a/src/packing.cairo +++ b/src/packing.cairo @@ -18,7 +18,7 @@ fn pack_flipped_data(address: felt252, powerup: PowerUp) -> felt252 { packed.try_into().unwrap() } -fn unpack_flipped_data(flipped: felt252) -> (ContractAddress, PowerUp) { +fn unpack_flipped_data(flipped: felt252) -> (felt252, PowerUp) { let flipped_u256: u256 = flipped.into(); let address: felt252 = (flipped_u256 & ADDRESS_MASK).try_into().unwrap(); let powerup_type: felt252 = ((flipped_u256 & POWERUP_MASK) / 256_u256).try_into().unwrap(); @@ -31,5 +31,5 @@ fn unpack_flipped_data(flipped: felt252) -> (ContractAddress, PowerUp) { _ => PowerUp::None, }; - (address.try_into().unwrap(), powerup) + (address, powerup) } \ No newline at end of file diff --git a/src/systems/actions.cairo b/src/systems/actions.cairo index ab732f7..ad19ce8 100644 --- a/src/systems/actions.cairo +++ b/src/systems/actions.cairo @@ -3,7 +3,7 @@ trait IActions { fn flip(ref world: IWorldDispatcher, x: u32, y: u32); fn flop(ref world: IWorldDispatcher); - fn claim(ref world: IWorldDispatcher); + fn claim(ref world: IWorldDispatcher, flipped_tiles: Array<(u32, u32)>); } // dojo decorator @@ -11,7 +11,7 @@ trait IActions { mod actions { use super::{IActions}; use starknet::{ContractAddress, get_caller_address, info::get_tx_info}; - use flippyflop::models::{PowerUp, PowerUpTrait, Game}; + use flippyflop::models::{PowerUp, PowerUpTrait, Game, Claim}; use core::poseidon::poseidon_hash_span; use dojo::model::{FieldLayout, Layout}; use flippyflop::tokens::flip::{IFlip, IFlipDispatcher, IFlipDispatcherTrait}; @@ -102,54 +102,51 @@ mod actions { } } - fn claim(ref world: IWorldDispatcher) { + fn claim(ref world: IWorldDispatcher, flipped_tiles: Array<(u32, u32)>) { // Game must be locked let game = get!(world, GAME_ID, Game); assert!(game.is_locked, "Game is not locked"); - let player = get_caller_address(); + let player = get_caller_address().into(); + let masked_player: felt252 = (player.into() & ADDRESS_MASK).try_into().unwrap(); + + // Check if a Claim already exists for this player + let existing_claim = get!(world, (player), Claim); + assert!(existing_claim.amount == 0, "Claim already processed"); + let flip_token = flip_token(world); let mut total_tokens: u256 = 0; - // Iterate through all tiles - let mut x: u32 = 0; + // Iterate through the provided flipped tiles + let mut i = 0; loop { - if x >= X_BOUND { + if i >= flipped_tiles.len() { break; } - - let mut y: u32 = 0; - loop { - if y >= Y_BOUND { - break; - } - - let entity_hash = poseidon_hash_span(array![x.into(), y.into()].span()); - let tile = world.entity_lobotomized(TILE_MODEL_SELECTOR, entity_hash); - - // Check if the tile is flipped and belongs to the player - let (tile_owner, powerup) = unpack_flipped_data(tile); - if tile_owner == player { - // Calculate base token amount (1 ETH in wei) - let mut tokens: u256 = 1000000000000000000; - - // Apply powerup multiplier if any - if let PowerUp::Multiplier(multiplier) = powerup { - tokens *= multiplier.into(); - } - - total_tokens += tokens; - } - - y += 1; - }; - - x += 1; + let (x, y) = *flipped_tiles[i]; + + let entity_hash = poseidon_hash_span(array![x.into(), y.into()].span()); + let tile = world.entity_lobotomized(TILE_MODEL_SELECTOR, entity_hash); + + // Verify the tile belongs to the player + let (tile_owner, powerup) = unpack_flipped_data(tile); + assert!(tile_owner == masked_player, "Tile does not belong to the player"); + + // Calculate tokens for this tile + let mut tokens: u256 = 1000000000000000000; // 1 ETH in wei + + // Apply powerup multiplier if any + if let PowerUp::Multiplier(multiplier) = powerup { + tokens *= multiplier.into(); + } + + total_tokens += tokens; + i += 1; }; - // Mint FLIP tokens to the player + set!(world, (Claim { player, amount: total_tokens })); if total_tokens > 0 { - flip_token.mint_from(player, total_tokens); + flip_token.mint_from(player.try_into().unwrap(), total_tokens); } } }