diff --git a/contracts/dojo_mainnet.toml b/contracts/dojo_mainnet.toml index 6242611..e476188 100644 --- a/contracts/dojo_mainnet.toml +++ b/contracts/dojo_mainnet.toml @@ -33,10 +33,6 @@ world_address = "0x02b127646258e21186e6c7e6234f42583d0d19bf88a57776a404c2cefeb42 [init_call_args] "tournament-LSTournament" = [ - "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", - "0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49", - "0x018108b32cea514a78ef1b0e4a0753e855cdf620bc0565202c02456f618c4dc4", - "0x2a85bd616f912537c50a49a4076db02c00b29b2cdc8a197ce92ed1837fa875b", "1", # safe_mode "1" # test_mode ] diff --git a/contracts/src/lib.cairo b/contracts/src/lib.cairo index c4cdbb6..eb85185 100644 --- a/contracts/src/lib.cairo +++ b/contracts/src/lib.cairo @@ -1,29 +1,32 @@ mod ls15_components { pub mod constants; mod interfaces; - // mod loot_survivor; + mod loot_survivor; mod libs { pub mod store; pub mod utils; } pub mod models { - // pub mod loot_survivor; + pub mod loot_survivor; pub mod tournament; } pub mod tournament; mod tests { - // pub mod eth_mock; - // pub mod lords_mock; - // pub mod erc20_mock; - // pub mod erc721_mock; - // #[cfg(test)] - // mod helpers; - // pub mod loot_survivor_mock; - // pub mod pragma_mock; - // pub mod tournament_mock; + pub mod libs { + pub mod store; + } + pub mod eth_mock; + pub mod lords_mock; + pub mod erc20_mock; + pub mod erc721_mock; + #[cfg(test)] + mod helpers; + pub mod loot_survivor_mock; + pub mod pragma_mock; + pub mod tournament_mock; #[cfg(test)] mod test_tournament; - // pub mod interfaces; + pub mod interfaces; // #[cfg(test)] // mod test_tournament_stress_tests; } diff --git a/contracts/src/ls15_components/constants.cairo b/contracts/src/ls15_components/constants.cairo index 410d785..447d941 100644 --- a/contracts/src/ls15_components/constants.cairo +++ b/contracts/src/ls15_components/constants.cairo @@ -11,7 +11,7 @@ pub const MIN_REGISTRATION_PERIOD: u32 = 300; // 5 minutes pub const MAX_REGISTRATION_PERIOD: u32 = 2592000; // 1 month pub const MIN_TOURNAMENT_LENGTH: u32 = 3600; // 1 hour pub const MAX_TOURNAMENT_LENGTH: u32 = 15552000; // 6 months -pub const MIN_SUBMISSION_PERIOD: u32 = 1800; // 30 mins +pub const MIN_SUBMISSION_PERIOD: u32 = 86400; // 1 day pub const MAX_SUBMISSION_PERIOD: u32 = 1209600; // 2 weeks pub const GAME_EXPIRATION_PERIOD: u32 = 864000; // 10 days diff --git a/contracts/src/ls15_components/interfaces.cairo b/contracts/src/ls15_components/interfaces.cairo index f54ee15..96e3afe 100644 --- a/contracts/src/ls15_components/interfaces.cairo +++ b/contracts/src/ls15_components/interfaces.cairo @@ -2,6 +2,7 @@ use starknet::ContractAddress; use adventurer::{adventurer::Adventurer, adventurer_meta::AdventurerMetadata, bag::Bag}; use dojo::world::{WorldStorage, WorldStorageTrait, IWorldDispatcher}; use tournament::presets::ls_tournament::{ILSTournamentDispatcher}; +use tournament::ls15_components::models::tournament::FreeGameTokenType; use tournament::ls15_components::libs::utils::ZERO; @@ -34,6 +35,7 @@ pub trait ILootSurvivor { fn get_adventurer_meta(self: @TState, adventurer_id: felt252) -> AdventurerMetadata; fn get_bag(self: @TState, adventurer_id: felt252) -> Bag; fn get_cost_to_play(self: @TState) -> u128; + fn free_game_available(self: @TState, token_type: FreeGameTokenType, token_id: u128) -> bool; fn new_game( ref self: TState, client_reward_address: ContractAddress, diff --git a/contracts/src/ls15_components/libs/store.cairo b/contracts/src/ls15_components/libs/store.cairo index 41e88b1..674592e 100644 --- a/contracts/src/ls15_components/libs/store.cairo +++ b/contracts/src/ls15_components/libs/store.cairo @@ -96,7 +96,7 @@ pub impl StoreImpl of StoreTrait { } // Setters // - + // Tournament #[inline(always)] diff --git a/contracts/src/ls15_components/loot_survivor.cairo b/contracts/src/ls15_components/loot_survivor.cairo index bd382eb..27d5580 100644 --- a/contracts/src/ls15_components/loot_survivor.cairo +++ b/contracts/src/ls15_components/loot_survivor.cairo @@ -1,5 +1,9 @@ use starknet::ContractAddress; -use tournament::ls15_components::models::loot_survivor::{Adventurer, AdventurerMetadata, Bag}; +use tournament::ls15_components::models::loot_survivor::{ + Adventurer, AdventurerMetadataStorage, Bag +}; +use tournament::ls15_components::models::tournament::FreeGameTokenType; +use adventurer::{adventurer_meta::AdventurerMetadata}; #[starknet::interface] trait ILootSurvivor { @@ -7,6 +11,9 @@ trait ILootSurvivor { fn get_adventurer_meta(self: @TState, adventurer_id: felt252) -> AdventurerMetadata; fn get_bag(self: @TState, adventurer_id: felt252) -> Bag; fn get_cost_to_play(self: @TState) -> u128; + fn free_game_available( + self: @TState, free_game_type: FreeGameTokenType, token_id: u128 + ) -> bool; fn new_game( ref self: TState, client_reward_address: ContractAddress, @@ -20,9 +27,10 @@ trait ILootSurvivor { ) -> felt252; fn set_adventurer(ref self: TState, adventurer_id: felt252, adventurer: Adventurer); fn set_adventurer_meta( - ref self: TState, adventurer_id: felt252, adventurer_meta: AdventurerMetadata + ref self: TState, adventurer_id: felt252, adventurer_meta: AdventurerMetadataStorage ); fn set_bag(ref self: TState, adventurer_id: felt252, bag: Bag); + fn set_free_game_available(self: @TState, free_game_type: FreeGameTokenType, token_id: u128); } /// @@ -37,12 +45,15 @@ pub mod loot_survivor_component { use starknet::{ContractAddress, get_block_timestamp, get_caller_address, get_contract_address}; use dojo::contract::components::world_provider::{IWorldProvider}; + use adventurer::{adventurer_meta::AdventurerMetadata}; + use tournament::ls15_components::models::loot_survivor::{ - Adventurer, AdventurerMetadata, Bag, Stats, Equipment, Item, AdventurerModel, - AdventurerMetaModel, BagModel, GameCountModel, Contracts + Adventurer, AdventurerMetadataStorage, Bag, Stats, Equipment, Item, AdventurerModel, + AdventurerMetaModel, BagModel, GameCountModel, FreeGameAvailableModel, Contracts }; + use tournament::ls15_components::models::tournament::FreeGameTokenType; use tournament::ls15_components::interfaces::{WorldTrait, WorldImpl}; - use tournament::ls15_components::libs::store::{Store, StoreTrait}; + use tournament::ls15_components::tests::libs::store::{Store, StoreTrait}; use tournament::ls15_components::libs::utils::{pow}; use openzeppelin_introspection::src5::SRC5Component; @@ -94,7 +105,18 @@ pub mod loot_survivor_component { self.get_contract().world_dispatcher(), @"tournament" ); let mut store: Store = StoreTrait::new(world); - store.get_adventurer_meta_model(adventurer_id).adventurer_meta + let adventurer_meta = store.get_adventurer_meta_model(adventurer_id).adventurer_meta; + let formatted_adventurer_meta = AdventurerMetadata { + birth_date: adventurer_meta.birth_date, + death_date: adventurer_meta.death_date, + level_seed: adventurer_meta.level_seed, + item_specials_seed: adventurer_meta.item_specials_seed, + rank_at_death: adventurer_meta.rank_at_death, + delay_stat_reveal: adventurer_meta.delay_stat_reveal, + golden_token_id: adventurer_meta.golden_token_id, + launch_tournament_winner_token_id: 0, + }; + formatted_adventurer_meta } fn get_bag(self: @ComponentState, adventurer_id: felt252) -> Bag { @@ -109,6 +131,16 @@ pub mod loot_survivor_component { 50000000000000000000 } + fn free_game_available( + self: @ComponentState, free_game_type: FreeGameTokenType, token_id: u128 + ) -> bool { + let mut world = WorldTrait::storage( + self.get_contract().world_dispatcher(), @"tournament" + ); + let mut store: Store = StoreTrait::new(world); + store.get_free_game_available_model(free_game_type, token_id).available + } + fn new_game( ref self: ComponentState, client_reward_address: ContractAddress, @@ -127,11 +159,15 @@ pub mod loot_survivor_component { let contracts = store.get_contracts_model(get_contract_address()); let cost_to_play = self.get_cost_to_play(); // transfer base game cost - let lords_dispatcher: IERC20Dispatcher = IERC20Dispatcher { - contract_address: contracts.lords - }; - lords_dispatcher - .transfer_from(get_caller_address(), get_contract_address(), cost_to_play.into()); + if (golden_token_id.is_zero() && launch_tournament_winner_token_id.is_zero()) { + let lords_dispatcher: IERC20Dispatcher = IERC20Dispatcher { + contract_address: contracts.lords + }; + lords_dispatcher + .transfer_from( + get_caller_address(), get_contract_address(), cost_to_play.into() + ); + } // transfer VRF cost let eth_dispatcher: IERC20Dispatcher = IERC20Dispatcher { @@ -187,7 +223,7 @@ pub mod loot_survivor_component { @AdventurerModel { adventurer_id: adventurer_id.into(), adventurer } ); - let adventurer_meta = AdventurerMetadata { + let adventurer_meta = AdventurerMetadataStorage { birth_date: get_block_timestamp().into(), death_date: 0, level_seed: 0, @@ -222,7 +258,7 @@ pub mod loot_survivor_component { fn set_adventurer_meta( ref self: ComponentState, adventurer_id: felt252, - adventurer_meta: AdventurerMetadata + adventurer_meta: AdventurerMetadataStorage ) { let mut world = WorldTrait::storage( self.get_contract().world_dispatcher(), @"tournament" @@ -239,6 +275,19 @@ pub mod loot_survivor_component { let mut store: Store = StoreTrait::new(world); store.set_bag_model(@BagModel { adventurer_id, bag }); } + + fn set_free_game_available( + self: @ComponentState, free_game_type: FreeGameTokenType, token_id: u128 + ) { + let mut world = WorldTrait::storage( + self.get_contract().world_dispatcher(), @"tournament" + ); + let mut store: Store = StoreTrait::new(world); + store + .set_free_game_available_model( + @FreeGameAvailableModel { free_game_type, token_id, available: true } + ); + } } #[generate_trait] diff --git a/contracts/src/ls15_components/models/loot_survivor.cairo b/contracts/src/ls15_components/models/loot_survivor.cairo index 48570dc..c5508b8 100644 --- a/contracts/src/ls15_components/models/loot_survivor.cairo +++ b/contracts/src/ls15_components/models/loot_survivor.cairo @@ -1,4 +1,5 @@ use starknet::ContractAddress; +use tournament::ls15_components::models::tournament::FreeGameTokenType; // dojo compatible structs @@ -41,7 +42,7 @@ pub struct Adventurer { } #[derive(Drop, Copy, Serde, Introspect)] -pub struct AdventurerMetadata { +pub struct AdventurerMetadataStorage { pub birth_date: u64, // 64 bits in storage pub death_date: u64, // 64 bits in storage pub level_seed: u64, // 64 bits in storage @@ -49,7 +50,7 @@ pub struct AdventurerMetadata { pub rank_at_death: u8, // 2 bits in storage pub delay_stat_reveal: bool, // 1 bit in storage pub golden_token_id: u8, // 8 bits in storage - // launch_tournament_winner_token_id: u128, // 32 bits in storage + // pub launch_tournament_winner_token_id: u128, // 32 bits in storage } @@ -97,7 +98,7 @@ pub struct AdventurerModel { pub struct AdventurerMetaModel { #[key] pub adventurer_id: felt252, - pub adventurer_meta: AdventurerMetadata, + pub adventurer_meta: AdventurerMetadataStorage, } #[dojo::model] @@ -116,6 +117,16 @@ pub struct GameCountModel { pub game_count: u128, } +#[dojo::model] +#[derive(Copy, Drop, Serde)] +pub struct FreeGameAvailableModel { + #[key] + pub free_game_type: FreeGameTokenType, + #[key] + pub token_id: u128, + pub available: bool, +} + #[dojo::model] #[derive(Copy, Drop, Serde)] pub struct Contracts { diff --git a/contracts/src/ls15_components/models/tournament.cairo b/contracts/src/ls15_components/models/tournament.cairo index 43953a7..ff545cb 100644 --- a/contracts/src/ls15_components/models/tournament.cairo +++ b/contracts/src/ls15_components/models/tournament.cairo @@ -67,6 +67,11 @@ pub enum EntryStatus { Submitted, } +#[derive(Copy, Drop, PartialEq, Introspect, Serde)] +pub enum FreeGameTokenType { + GoldenToken, + LaunchTournamentChampion, +} /// /// Model @@ -203,6 +208,8 @@ pub struct TournamentConfig { pub lords: ContractAddress, pub loot_survivor: ContractAddress, pub oracle: ContractAddress, + pub golden_token: ContractAddress, + pub blobert: ContractAddress, pub safe_mode: bool, pub test_mode: bool } diff --git a/contracts/src/ls15_components/tests/helpers.cairo b/contracts/src/ls15_components/tests/helpers.cairo index 4d2e48d..eaa3b90 100644 --- a/contracts/src/ls15_components/tests/helpers.cairo +++ b/contracts/src/ls15_components/tests/helpers.cairo @@ -13,7 +13,7 @@ use tournament::ls15_components::tests::interfaces::{ ITournamentMockDispatcher, ITournamentMockDispatcherTrait }; use adventurer::{adventurer::Adventurer, equipment::Equipment, item::Item, stats::Stats}; -use tournament::ls15_components::models::loot_survivor::AdventurerMetadata; +use tournament::ls15_components::models::loot_survivor::AdventurerMetadataStorage; use tournament::ls15_components::models::tournament::{ERC20Data, ERC721Data, Token, TokenDataType}; // @@ -44,6 +44,16 @@ pub fn approve_game_costs( eth.approve(tournament.contract_address, entries * 200000000000000); } +pub fn approve_free_game_cost( + eth: IERC20MockDispatcher, + golden_token: IERC721MockDispatcher, + token_id: u256, + tournament: ITournamentMockDispatcher +) { + eth.approve(tournament.contract_address, 200000000000000); + golden_token.approve(tournament.contract_address, token_id); +} + pub fn create_dead_adventurer_with_xp(xp: u16) -> Adventurer { Adventurer { health: 0, @@ -70,8 +80,8 @@ pub fn create_dead_adventurer_with_xp(xp: u16) -> Adventurer { } } -pub fn create_adventurer_metadata_with_death_date(death_date: u64) -> AdventurerMetadata { - AdventurerMetadata { +pub fn create_adventurer_metadata_with_death_date(death_date: u64) -> AdventurerMetadataStorage { + AdventurerMetadataStorage { birth_date: get_block_timestamp().into(), death_date: death_date, level_seed: 0, diff --git a/contracts/src/ls15_components/tests/interfaces.cairo b/contracts/src/ls15_components/tests/interfaces.cairo index 5881e9c..0c9b80d 100644 --- a/contracts/src/ls15_components/tests/interfaces.cairo +++ b/contracts/src/ls15_components/tests/interfaces.cairo @@ -1,14 +1,18 @@ use adventurer::{ - adventurer::{Adventurer, ImplAdventurer}, adventurer_meta::{ImplAdventurerMetadata}, bag::Bag + adventurer::{Adventurer, ImplAdventurer}, + adventurer_meta::{AdventurerMetadata, ImplAdventurerMetadata}, bag::Bag }; -use tournament::ls15_components::models::loot_survivor::AdventurerMetadata; +use tournament::ls15_components::models::loot_survivor::AdventurerMetadataStorage; use tournament::ls15_components::models::tournament::{ - TournamentModel, Token, Premium, TokenDataType, GatedType, GatedSubmissionType + TournamentModel, Token, Premium, TokenDataType, GatedType, GatedSubmissionType, + FreeGameTokenType }; use tournament::ls15_components::interfaces::{DataType, PragmaPricesResponse}; use starknet::ContractAddress; -use dojo::world::IWorldDispatcher; +use dojo::world::{WorldStorage, WorldStorageTrait, IWorldDispatcher}; + +use tournament::ls15_components::libs::utils::ZERO; #[starknet::interface] pub trait IERC20Mock { @@ -115,7 +119,13 @@ pub trait ITournamentMock { ref self: TState, tournament_id: u64, gated_submission_type: Option ); fn start_tournament( - ref self: TState, tournament_id: u64, start_all: bool, start_count: Option + ref self: TState, + tournament_id: u64, + start_all: bool, + start_count: Option, + client_reward_address: ContractAddress, + golden_token_free_game_ids: Span, + blobert_free_game_ids: Span, ); fn submit_scores(ref self: TState, tournament_id: u64, game_ids: Array); fn add_prize( @@ -133,8 +143,12 @@ pub trait ITournamentMock { lords_address: ContractAddress, loot_survivor_address: ContractAddress, oracle_address: ContractAddress, + golden_token: ContractAddress, + blobert: ContractAddress, safe_mode: bool, - test_mode: bool + test_mode: bool, + test_erc20: ContractAddress, + test_erc721: ContractAddress, ); } @@ -188,6 +202,7 @@ pub trait ILootSurvivorMock { fn get_adventurer_meta(self: @TState, adventurer_id: felt252) -> AdventurerMetadata; fn get_bag(self: @TState, adventurer_id: felt252) -> Bag; fn get_cost_to_play(self: @TState) -> u128; + fn free_game_available(self: @TState, token_type: FreeGameTokenType, token_id: u128) -> bool; fn new_game( ref self: TState, client_reward_address: ContractAddress, @@ -201,9 +216,10 @@ pub trait ILootSurvivorMock { ) -> felt252; fn set_adventurer(ref self: TState, adventurer_id: felt252, adventurer: Adventurer); fn set_adventurer_meta( - ref self: TState, adventurer_id: felt252, adventurer_meta: AdventurerMetadata + ref self: TState, adventurer_id: felt252, adventurer_meta: AdventurerMetadataStorage ); fn set_bag(ref self: TState, adventurer_id: felt252, bag: Bag); + fn set_free_game_available(ref self: TState, free_game_type: FreeGameTokenType, token_id: u128); fn initializer( ref self: TState, diff --git a/contracts/src/ls15_components/tests/libs/store.cairo b/contracts/src/ls15_components/tests/libs/store.cairo index bd53864..18394d5 100644 --- a/contracts/src/ls15_components/tests/libs/store.cairo +++ b/contracts/src/ls15_components/tests/libs/store.cairo @@ -3,13 +3,14 @@ use dojo::world::{WorldStorage}; use dojo::model::{ModelStorage}; use tournament::ls15_components::models::loot_survivor::{ - AdventurerModel, AdventurerMetaModel, BagModel, GameCountModel, Contracts + AdventurerModel, AdventurerMetaModel, BagModel, GameCountModel, FreeGameAvailableModel, + Contracts }; use tournament::ls15_components::models::tournament::{ TournamentTotalsModel, TournamentModel, TournamentEntriesModel, TournamentPrizeKeysModel, PrizesModel, TournamentScoresModel, TokenModel, TournamentEntriesAddressModel, TournamentEntryAddressesModel, TournamentStartsAddressModel, TournamentGameModel, - TournamentConfig, TournamentStartIdsModel + TournamentConfig, TournamentStartIdsModel, FreeGameTokenType }; #[derive(Copy, Drop)] @@ -50,6 +51,12 @@ pub impl StoreImpl of StoreTrait { (self.world.read_model(contract)) } + #[inline(always)] + fn get_free_game_available_model( + ref self: Store, free_game_type: FreeGameTokenType, token_id: u128 + ) -> FreeGameAvailableModel { + (self.world.read_model((free_game_type, token_id),)) + } #[inline(always)] fn get_contracts_model(ref self: Store, contract: ContractAddress) -> Contracts { @@ -150,6 +157,11 @@ pub impl StoreImpl of StoreTrait { self.world.write_model(model); } + #[inline(always)] + fn set_free_game_available_model(ref self: Store, model: @FreeGameAvailableModel) { + self.world.write_model(model); + } + #[inline(always)] fn set_contracts_model(ref self: Store, model: @Contracts) { self.world.write_model(model); diff --git a/contracts/src/ls15_components/tests/test_tournament.cairo b/contracts/src/ls15_components/tests/test_tournament.cairo index 219b674..bfb24f9 100644 --- a/contracts/src/ls15_components/tests/test_tournament.cairo +++ b/contracts/src/ls15_components/tests/test_tournament.cairo @@ -1,6 +1,6 @@ use core::option::Option; use starknet::{ContractAddress, get_block_timestamp, testing}; -use dojo::world::WorldStorage; +use dojo::world::{WorldStorage}; use dojo_cairo_test::{ spawn_test_world, NamespaceDef, TestResource, ContractDefTrait, ContractDef, WorldStorageTestTrait @@ -11,19 +11,20 @@ use tournament::ls15_components::constants::{ MIN_TOURNAMENT_LENGTH, MAX_TOURNAMENT_LENGTH }; -use tournament::ls15_components::interfaces::{WorldTrait}; +use tournament::ls15_components::tests::interfaces::WorldTrait; use tournament::ls15_components::models::{ loot_survivor::{ - m_AdventurerModel, m_AdventurerMetaModel, m_BagModel, m_GameCountModel, m_Contracts + m_AdventurerModel, m_AdventurerMetaModel, m_BagModel, m_GameCountModel, + m_FreeGameAvailableModel, m_Contracts }, tournament::{ m_TournamentModel, m_TournamentGameModel, m_TournamentEntryAddressesModel, m_TournamentEntriesAddressModel, m_TournamentStartsAddressModel, m_TournamentStartIdsModel, m_TournamentEntriesModel, m_TournamentScoresModel, m_TournamentTotalsModel, m_TournamentPrizeKeysModel, m_PrizesModel, m_TokenModel, m_TournamentConfig, ERC20Data, - ERC721Data, Token, Premium, GatedToken, EntryCriteria, TokenDataType, GatedType, - GatedEntryType, GatedSubmissionType + ERC721Data, Premium, GatedToken, EntryCriteria, TokenDataType, GatedType, GatedEntryType, + GatedSubmissionType, FreeGameTokenType } }; @@ -31,12 +32,12 @@ use tournament::tests::{ utils, constants::{ OWNER, TOURNAMENT_NAME, TOURNAMENT_DESCRIPTION, STARTING_BALANCE, TEST_START_TIME, - TEST_END_TIME + TEST_END_TIME, ZERO }, }; use tournament::ls15_components::tests::helpers::{ - approve_game_costs, create_basic_tournament, create_adventurer_metadata_with_death_date, - create_dead_adventurer_with_xp, register_tokens_for_test + approve_game_costs, approve_free_game_cost, create_basic_tournament, + create_adventurer_metadata_with_death_date, create_dead_adventurer_with_xp }; use tournament::ls15_components::tests::{ erc20_mock::{erc20_mock}, interfaces::{IERC20MockDispatcher, IERC20MockDispatcherTrait}, @@ -59,6 +60,21 @@ use tournament::ls15_components::tests::{ use openzeppelin_token::erc721::interface; use openzeppelin_token::erc721::{ERC721Component::{Transfer, Approval,}}; +#[derive(Drop)] +struct TestContracts { + world: WorldStorage, + tournament: ITournamentMockDispatcher, + loot_survivor: ILootSurvivorMockDispatcher, + pragma: IPragmaMockDispatcher, + eth: IERC20MockDispatcher, + lords: IERC20MockDispatcher, + erc20: IERC20MockDispatcher, + erc721: IERC721MockDispatcher, + golden_token: IERC721MockDispatcher, + blobert: IERC721MockDispatcher, +} + + // // events helpers // @@ -100,7 +116,13 @@ fn assert_only_event_approval( // Setup // -fn setup_uninitialized() -> (WorldStorage, IERC20MockDispatcher, IERC20MockDispatcher) { +fn setup_uninitialized() -> ( + WorldStorage, + IERC20MockDispatcher, + IERC20MockDispatcher, + IERC721MockDispatcher, + IERC721MockDispatcher +) { testing::set_block_number(1); testing::set_block_timestamp(1); @@ -111,6 +133,7 @@ fn setup_uninitialized() -> (WorldStorage, IERC20MockDispatcher, IERC20MockDispa TestResource::Model(m_AdventurerMetaModel::TEST_CLASS_HASH.try_into().unwrap()), TestResource::Model(m_BagModel::TEST_CLASS_HASH.try_into().unwrap()), TestResource::Model(m_GameCountModel::TEST_CLASS_HASH.try_into().unwrap()), + TestResource::Model(m_FreeGameAvailableModel::TEST_CLASS_HASH.try_into().unwrap()), TestResource::Model(m_Contracts::TEST_CLASS_HASH.try_into().unwrap()), // tournament models TestResource::Model(m_TournamentModel::TEST_CLASS_HASH.try_into().unwrap()), @@ -166,20 +189,19 @@ fn setup_uninitialized() -> (WorldStorage, IERC20MockDispatcher, IERC20MockDispa let contract = utils::deploy(erc20_mock::TEST_CLASS_HASH, 'salt5', call_data); let mut lords = IERC20MockDispatcher { contract_address: contract }; - (world, eth, lords) + let call_data: Array = array![]; + let contract = utils::deploy(erc721_mock::TEST_CLASS_HASH, 'salt6', call_data); + let mut golden_token = IERC721MockDispatcher { contract_address: contract }; + + let call_data: Array = array![]; + let contract = utils::deploy(erc721_mock::TEST_CLASS_HASH, 'salt7', call_data); + let mut blobert = IERC721MockDispatcher { contract_address: contract }; + + (world, eth, lords, golden_token, blobert) } -pub fn setup() -> ( - WorldStorage, - ITournamentMockDispatcher, - ILootSurvivorMockDispatcher, - IPragmaMockDispatcher, - IERC20MockDispatcher, - IERC20MockDispatcher, - IERC20MockDispatcher, - IERC721MockDispatcher, -) { - let (mut world, mut eth, mut lords) = setup_uninitialized(); +pub fn setup() -> TestContracts { + let (mut world, mut eth, mut lords, mut golden_token, mut blobert) = setup_uninitialized(); let tournament = world.tournament_mock_dispatcher(); let loot_survivor = world.loot_survivor_mock_dispatcher(); @@ -194,8 +216,12 @@ pub fn setup() -> ( lords.contract_address, loot_survivor.contract_address, pragma.contract_address, + golden_token.contract_address, + blobert.contract_address, false, - false + false, + erc20.contract_address, + erc721.contract_address, ); loot_survivor .initializer(eth.contract_address, lords.contract_address, pragma.contract_address); @@ -206,13 +232,72 @@ pub fn setup() -> ( lords.mint(OWNER(), STARTING_BALANCE); erc20.mint(OWNER(), STARTING_BALANCE); erc721.mint(OWNER(), 1); + golden_token.mint(OWNER(), 1); + blobert.mint(OWNER(), 1); + + // set LS free games + loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + + loot_survivor.set_free_game_available(FreeGameTokenType::LaunchTournamentChampion, 1); // drop all events utils::drop_all_events(world.dispatcher.contract_address); utils::drop_all_events(tournament.contract_address); utils::drop_all_events(loot_survivor.contract_address); - (world, tournament, loot_survivor, pragma, eth, lords, erc20, erc721) + TestContracts { + world, tournament, loot_survivor, pragma, eth, lords, erc20, erc721, golden_token, blobert, + } +} + +pub fn setup_safe_mode() -> TestContracts { + let (mut world, mut eth, mut lords, mut golden_token, mut blobert) = setup_uninitialized(); + + let tournament = world.tournament_mock_dispatcher(); + let loot_survivor = world.loot_survivor_mock_dispatcher(); + let pragma = world.pragma_mock_dispatcher(); + let erc20 = world.erc20_mock_dispatcher(); + let erc721 = world.erc721_mock_dispatcher(); + + // initialize contracts + tournament + .initializer( + eth.contract_address, + lords.contract_address, + loot_survivor.contract_address, + pragma.contract_address, + golden_token.contract_address, + blobert.contract_address, + true, + false, + erc20.contract_address, + erc721.contract_address, + ); + loot_survivor + .initializer(eth.contract_address, lords.contract_address, pragma.contract_address); + + // mint tokens + utils::impersonate(OWNER()); + eth.mint(OWNER(), STARTING_BALANCE); + lords.mint(OWNER(), STARTING_BALANCE); + erc20.mint(OWNER(), STARTING_BALANCE); + erc721.mint(OWNER(), 1); + golden_token.mint(OWNER(), 1); + blobert.mint(OWNER(), 1); + + // set LS free games + loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + + loot_survivor.set_free_game_available(FreeGameTokenType::LaunchTournamentChampion, 1); + + // drop all events + utils::drop_all_events(world.dispatcher.contract_address); + utils::drop_all_events(tournament.contract_address); + utils::drop_all_events(loot_survivor.contract_address); + + TestContracts { + world, tournament, loot_survivor, pragma, eth, lords, erc20, erc721, golden_token, blobert, + } } // @@ -221,54 +306,56 @@ pub fn setup() -> ( #[test] fn test_initializer() { - let ( - _world, _tournament, mut loot_survivor, _pragma, mut eth, mut lords, mut erc20, mut erc721, - ) = - setup(); + let contracts = setup(); - assert(loot_survivor.symbol() == "LSVR", 'Symbol is wrong'); + assert(contracts.loot_survivor.symbol() == "LSVR", 'Symbol is wrong'); assert( - loot_survivor.supports_interface(interface::IERC721_ID) == true, 'should support IERC721_ID' + contracts.loot_survivor.supports_interface(interface::IERC721_ID) == true, + 'should support IERC721_ID' ); assert( - loot_survivor.supports_interface(interface::IERC721_METADATA_ID) == true, + contracts.loot_survivor.supports_interface(interface::IERC721_METADATA_ID) == true, 'should support METADATA' ); - assert(erc20.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); - assert(erc721.balance_of(OWNER()) == 1, 'Invalid balance'); - assert(eth.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); - assert(lords.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); + assert(contracts.erc20.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); + assert(contracts.erc721.balance_of(OWNER()) == 1, 'Invalid balance'); + assert(contracts.eth.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); + assert(contracts.lords.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); } + // // Test creating tournaments // #[test] fn test_create_tournament() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - let tournament_id = create_basic_tournament(tournament); + let tournament_id = create_basic_tournament(contracts.tournament); - let tournament_data = tournament.tournament(tournament_id); + let tournament_data = contracts.tournament.tournament(tournament_id); assert(tournament_data.name == TOURNAMENT_NAME(), 'Invalid tournament name'); assert( tournament_data.description == TOURNAMENT_DESCRIPTION(), 'Invalid tournament description' ); - assert(tournament_data.start_time == TEST_START_TIME().into(), 'Invalid tournament start time'); + assert( + tournament_data.start_time == TEST_START_TIME().into(), 'Invalid tournament start time' + ); assert(tournament_data.end_time == TEST_END_TIME().into(), 'Invalid tournament end time'); assert(tournament_data.gated_type == Option::None, 'Invalid tournament gated token'); assert(tournament_data.entry_premium == Option::None, 'Invalid entry premium'); - assert(tournament.total_tournaments() == 1, 'Invalid tournaments count'); + assert(contracts.tournament.total_tournaments() == 1, 'Invalid tournaments count'); } #[test] #[should_panic(expected: ('start time too close', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_start_time_too_close() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -284,9 +371,10 @@ fn test_create_tournament_start_time_too_close() { #[test] #[should_panic(expected: ('start time too far', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_start_time_too_far() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -302,9 +390,10 @@ fn test_create_tournament_start_time_too_far() { #[test] #[should_panic(expected: ('tournament too short', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_end_time_too_close() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -320,9 +409,10 @@ fn test_create_tournament_end_time_too_close() { #[test] #[should_panic(expected: ('tournament too long', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_end_time_too_far() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -338,9 +428,10 @@ fn test_create_tournament_end_time_too_far() { #[test] #[should_panic(expected: ('submission period too short', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_submission_period_too_short() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -356,9 +447,10 @@ fn test_create_tournament_submission_period_too_short() { #[test] #[should_panic(expected: ('submission period too long', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_submission_period_too_long() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -373,82 +465,83 @@ fn test_create_tournament_submission_period_too_long() { #[test] fn test_create_tournament_with_prizes() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); - register_tokens_for_test(tournament, erc20, erc721); + let tournament_id = create_basic_tournament(contracts.tournament); - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament + contracts.erc20.approve(contracts.tournament.contract_address, STARTING_BALANCE); + contracts.erc721.approve(contracts.tournament.contract_address, 1); + contracts + .tournament .add_prize( tournament_id, - erc20.contract_address, + contracts.erc20.contract_address, TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), 1 ); - tournament + contracts + .tournament .add_prize( tournament_id, - erc721.contract_address, + contracts.erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 ); - assert(erc20.balance_of(OWNER()) == 0, 'Invalid balance'); - assert(erc721.balance_of(OWNER()) == 0, 'Invalid balance'); + assert(contracts.erc20.balance_of(OWNER()) == 0, 'Invalid balance'); + assert(contracts.erc721.balance_of(OWNER()) == 0, 'Invalid balance'); } -#[test] -#[should_panic(expected: ('prize token not registered', 'ENTRYPOINT_FAILED'))] -fn test_create_tournament_with_prizes_token_not_registered() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); - - utils::impersonate(OWNER()); - create_basic_tournament(tournament); - erc20.approve(tournament.contract_address, 1); - erc721.approve(tournament.contract_address, 1); - - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament - .add_prize( - 1, - erc20.contract_address, - TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), - 1 - ); - tournament - .add_prize( - 1, erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 - ); -} +// #[test] +// #[should_panic(expected: ('prize token not registered', 'ENTRYPOINT_FAILED'))] +// fn test_create_tournament_with_prizes_token_not_registered() { +// let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721, +// _golden_token, _blobert) = +// setup(); + +// utils::impersonate(OWNER()); +// create_basic_tournament(tournament); +// erc20.approve(tournament.contract_address, 1); +// erc721.approve(tournament.contract_address, 1); + +// erc20.approve(tournament.contract_address, STARTING_BALANCE); +// erc721.approve(tournament.contract_address, 1); +// tournament +// .add_prize( +// 1, +// erc20.contract_address, +// TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), +// 1 +// ); +// tournament +// .add_prize( +// 1, erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 +// ); +// } #[test] #[should_panic(expected: ('prize position too large', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_with_prizes_position_too_large() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); - register_tokens_for_test(tournament, erc20, erc721); + let tournament_id = create_basic_tournament(contracts.tournament); - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament + contracts.erc20.approve(contracts.tournament.contract_address, STARTING_BALANCE); + contracts.erc721.approve(contracts.tournament.contract_address, 1); + contracts + .tournament .add_prize( tournament_id, - erc20.contract_address, + contracts.erc20.contract_address, TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), 2 ); - tournament + contracts + .tournament .add_prize( tournament_id, - erc721.contract_address, + contracts.erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 2 ); @@ -457,20 +550,19 @@ fn test_create_tournament_with_prizes_position_too_large() { #[test] #[should_panic(expected: ('premium distributions too long', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_with_premiums_too_long() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 1, token_distribution: array![100, 0].span(), creator_fee: 0, }; - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -486,20 +578,19 @@ fn test_create_tournament_with_premiums_too_long() { #[test] #[should_panic(expected: ('premium distributions not 100%', 'ENTRYPOINT_FAILED'))] fn test_create_tournament_with_premiums_not_100() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 1, token_distribution: array![95].span(), creator_fee: 0, }; - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -515,13 +606,13 @@ fn test_create_tournament_with_premiums_not_100() { #[test] #[should_panic(expected: ('tournament not settled', 'ENTRYPOINT_FAILED'))] fn test_create_gated_tournament_with_unsettled_tournament() { - let (_world, mut tournament, _loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // Create first tournament - let first_tournament_id = tournament + let first_tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -534,14 +625,18 @@ fn test_create_gated_tournament_with_unsettled_tournament() { ); // Enter first tournament - tournament.enter_tournament(first_tournament_id, Option::None); + contracts.tournament.enter_tournament(first_tournament_id, Option::None); // Move to tournament start time testing::set_block_timestamp(TEST_START_TIME().into()); // Start first tournament - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(first_tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament( + first_tournament_id, false, Option::None, ZERO(), array![].span(), array![].span() + ); // Try to create a second tournament gated by the first (unsettled) tournament let gated_type = GatedType::tournament(array![first_tournament_id].span()); @@ -549,7 +644,8 @@ fn test_create_gated_tournament_with_unsettled_tournament() { let current_time = get_block_timestamp(); // This should panic because the first tournament hasn't been settled yet - tournament + contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -564,13 +660,13 @@ fn test_create_gated_tournament_with_unsettled_tournament() { #[test] fn test_create_tournament_gated_by_multiple_tournaments() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // Create first tournament - let first_tournament_id = tournament + let first_tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -583,7 +679,8 @@ fn test_create_tournament_gated_by_multiple_tournaments() { ); // Create second tournament - let second_tournament_id = tournament + let second_tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -596,27 +693,35 @@ fn test_create_tournament_gated_by_multiple_tournaments() { ); // Enter and complete first tournament - tournament.enter_tournament(first_tournament_id, Option::None); + contracts.tournament.enter_tournament(first_tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(first_tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament( + first_tournament_id, false, Option::None, ZERO(), array![].span(), array![].span() + ); testing::set_block_timestamp(TEST_END_TIME().into()); let submitted_adventurer = create_dead_adventurer_with_xp(10); - loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(first_tournament_id, array![1]); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.tournament.submit_scores(first_tournament_id, array![1]); // Enter and complete second tournament testing::set_block_timestamp(1); - tournament.enter_tournament(second_tournament_id, Option::None); + contracts.tournament.enter_tournament(second_tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(second_tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament( + second_tournament_id, false, Option::None, ZERO(), array![].span(), array![].span() + ); testing::set_block_timestamp(TEST_END_TIME().into()); let submitted_adventurer = create_dead_adventurer_with_xp(20); - loot_survivor.set_adventurer(2, submitted_adventurer); - tournament.submit_scores(second_tournament_id, array![2]); + contracts.loot_survivor.set_adventurer(2, submitted_adventurer); + contracts.tournament.submit_scores(second_tournament_id, array![2]); // Settle tournaments testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); @@ -627,7 +732,8 @@ fn test_create_tournament_gated_by_multiple_tournaments() { ); let current_time = get_block_timestamp(); - let gated_tournament_id = tournament + let gated_tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -640,22 +746,23 @@ fn test_create_tournament_gated_by_multiple_tournaments() { ); // Verify the gated tournament was created with correct parameters - let gated_tournament = tournament.tournament(gated_tournament_id); - assert(gated_tournament.gated_type == Option::Some(gated_type), 'Invalid tournament gate type'); + let gated_tournament = contracts.tournament.tournament(gated_tournament_id); + assert( + gated_tournament.gated_type == Option::Some(gated_type), 'Invalid tournament gate type' + ); let gated_submission_type = GatedSubmissionType::game_id(array![1, 2].span()); // This should succeed since we completed both required tournaments - tournament.enter_tournament(gated_tournament_id, Option::Some(gated_submission_type)); + contracts.tournament.enter_tournament(gated_tournament_id, Option::Some(gated_submission_type)); // Verify entry was successful - let entries = tournament.tournament_entries(gated_tournament_id); + let entries = contracts.tournament.tournament_entries(gated_tournament_id); assert(entries == 1, 'Invalid entry count'); } #[test] fn test_create_tournament_gated_accounts() { - let (_world, mut tournament, _loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); @@ -666,7 +773,8 @@ fn test_create_tournament_gated_accounts() { // Create tournament gated by account list let gated_type = GatedType::address(allowed_accounts); - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -679,31 +787,37 @@ fn test_create_tournament_gated_accounts() { ); // Verify tournament was created with correct gating - let tournament_data = tournament.tournament(tournament_id); - assert(tournament_data.gated_type == Option::Some(gated_type), 'Invalid tournament gate type'); + let tournament_data = contracts.tournament.tournament(tournament_id); + assert( + tournament_data.gated_type == Option::Some(gated_type), 'Invalid tournament gate type' + ); // Allowed account (owner) can enter - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Allowed player can enter utils::impersonate(allowed_player); - eth.mint(allowed_player, STARTING_BALANCE); - lords.mint(allowed_player, STARTING_BALANCE); - tournament.enter_tournament(tournament_id, Option::None); + contracts.eth.mint(allowed_player, STARTING_BALANCE); + contracts.lords.mint(allowed_player, STARTING_BALANCE); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Start tournament entries testing::set_block_timestamp(TEST_START_TIME().into()); utils::impersonate(OWNER()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(allowed_player); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // Verify entries were successful - let entries = tournament.tournament_entries(tournament_id); + let entries = contracts.tournament.tournament_entries(tournament_id); assert(entries == 2, 'Invalid entry count'); } @@ -711,65 +825,67 @@ fn test_create_tournament_gated_accounts() { // Test registering tokens // -#[test] -fn test_register_token() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); - - utils::impersonate(OWNER()); - erc20.approve(tournament.contract_address, 1); - erc721.approve(tournament.contract_address, 1); - let tokens = array![ - Token { - token: erc20.contract_address, - token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }) - }, - Token { - token: erc721.contract_address, - token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }) - }, - ]; - - tournament.register_tokens(tokens); - assert(erc20.balance_of(OWNER()) == 1000000000000000000000, 'Invalid balance'); - assert(erc721.balance_of(OWNER()) == 1, 'Invalid balance'); - assert(tournament.is_token_registered(erc20.contract_address), 'Invalid registration'); - assert(tournament.is_token_registered(erc721.contract_address), 'Invalid registration'); -} - -#[test] -#[should_panic(expected: ('token already registered', 'ENTRYPOINT_FAILED'))] -fn test_register_token_already_registered() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); - - utils::impersonate(OWNER()); - erc20.approve(tournament.contract_address, 1); - erc721.approve(tournament.contract_address, 1); - let tokens = array![ - Token { - token: erc20.contract_address, - token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }) - }, - Token { - token: erc721.contract_address, - token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }) - }, - ]; - - tournament.register_tokens(tokens); - let tokens = array![ - Token { - token: erc20.contract_address, - token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }) - }, - Token { - token: erc721.contract_address, - token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }) - }, - ]; - tournament.register_tokens(tokens); -} +// #[test] +// fn test_register_token() { +// let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut +// erc721,) = +// setup(); + +// utils::impersonate(OWNER()); +// erc20.approve(tournament.contract_address, 1); +// erc721.approve(tournament.contract_address, 1); +// let tokens = array![ +// Token { +// token: erc20.contract_address, +// token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }) +// }, +// Token { +// token: erc721.contract_address, +// token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }) +// }, +// ]; + +// tournament.register_tokens(tokens); +// assert(erc20.balance_of(OWNER()) == 1000000000000000000000, 'Invalid balance'); +// assert(erc721.balance_of(OWNER()) == 1, 'Invalid balance'); +// assert(tournament.is_token_registered(erc20.contract_address), 'Invalid registration'); +// assert(tournament.is_token_registered(erc721.contract_address), 'Invalid registration'); +// } + +// #[test] +// #[should_panic(expected: ('token already registered', 'ENTRYPOINT_FAILED'))] +// fn test_register_token_already_registered() { +// let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721, +// _golden_token, _blobert) = +// setup(); + +// utils::impersonate(OWNER()); +// erc20.approve(tournament.contract_address, 1); +// erc721.approve(tournament.contract_address, 1); +// let tokens = array![ +// Token { +// token: erc20.contract_address, +// token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }) +// }, +// Token { +// token: erc721.contract_address, +// token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }) +// }, +// ]; + +// tournament.register_tokens(tokens); +// let tokens = array![ +// Token { +// token: erc20.contract_address, +// token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }) +// }, +// Token { +// token: erc721.contract_address, +// token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }) +// }, +// ]; +// tournament.register_tokens(tokens); +// } // // Test entering tournaments @@ -777,37 +893,37 @@ fn test_register_token_already_registered() { #[test] fn test_enter_tournament() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); + let tournament_id = create_basic_tournament(contracts.tournament); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); } #[test] #[should_panic(expected: ('tournament already started', 'ENTRYPOINT_FAILED'))] fn test_enter_tournament_already_started() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, _erc20, _erc721) = setup(); + let contracts = setup(); - let tournament_id = create_basic_tournament(tournament); + let tournament_id = create_basic_tournament(contracts.tournament); testing::set_block_timestamp(TEST_START_TIME().into()); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); } #[test] #[should_panic(expected: ('invalid gated submission type', 'ENTRYPOINT_FAILED'))] fn test_enter_tournament_wrong_submission_type() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // First create and complete a tournament that will be used as a gate - let first_tournament_id = tournament + let first_tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -820,15 +936,19 @@ fn test_enter_tournament_wrong_submission_type() { ); // Complete the first tournament - tournament.enter_tournament(first_tournament_id, Option::None); + contracts.tournament.enter_tournament(first_tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(first_tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament( + first_tournament_id, false, Option::None, ZERO(), array![].span(), array![].span() + ); testing::set_block_timestamp(TEST_END_TIME().into()); let submitted_adventurer = create_dead_adventurer_with_xp(10); - loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(first_tournament_id, array![1]); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.tournament.submit_scores(first_tournament_id, array![1]); // Settle first tournament testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); @@ -837,7 +957,8 @@ fn test_enter_tournament_wrong_submission_type() { let gated_type = GatedType::tournament(array![first_tournament_id].span()); let current_time = get_block_timestamp(); - let gated_tournament_id = tournament + let gated_tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -854,7 +975,7 @@ fn test_enter_tournament_wrong_submission_type() { // This should panic because we're using token_id submission type for a tournament-gated // tournament - tournament.enter_tournament(gated_tournament_id, Option::Some(wrong_submission_type)); + contracts.tournament.enter_tournament(gated_tournament_id, Option::Some(wrong_submission_type)); } // @@ -863,102 +984,284 @@ fn test_enter_tournament_wrong_submission_type() { #[test] fn test_start_tournament() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); + let tournament_id = create_basic_tournament(contracts.tournament); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // check tournament entries - assert(tournament.tournament_entries(tournament_id) == 1, 'Invalid entries'); + assert(contracts.tournament.tournament_entries(tournament_id) == 1, 'Invalid entries'); // check owner now has game token - assert(loot_survivor.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.loot_survivor.owner_of(1) == OWNER(), 'Invalid owner'); // check lords and eth balances of loot survivor after starting assert( - lords.balance_of(loot_survivor.contract_address) == 50000000000000000000, - 'Invalid - balance' + contracts + .lords + .balance_of(contracts.loot_survivor.contract_address) == 50000000000000000000, + 'Invalid balance' + ); + assert( + contracts.eth.balance_of(contracts.loot_survivor.contract_address) == 200000000000000, + 'Invalid balance' ); - assert(eth.balance_of(loot_survivor.contract_address) == 200000000000000, 'Invalid balance'); // check lords and eth balances of owner after starting assert( - lords.balance_of(OWNER()) == STARTING_BALANCE - 50000000000000000000, 'Invalid - balance' + contracts.lords.balance_of(OWNER()) == STARTING_BALANCE - 50000000000000000000, + 'Invalid balance' + ); + assert( + contracts.eth.balance_of(OWNER()) == STARTING_BALANCE - 200000000000000, + 'Invalid balance' ); - assert(eth.balance_of(OWNER()) == STARTING_BALANCE - 200000000000000, 'Invalid balance'); } #[test] #[should_panic(expected: ('all entries started', 'ENTRYPOINT_FAILED'))] fn test_start_tournament_entry_already_started() { - let (_world, mut tournament, _loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); + + utils::impersonate(OWNER()); + + let tournament_id = create_basic_tournament(contracts.tournament); + + contracts.tournament.enter_tournament(tournament_id, Option::None); + + testing::set_block_timestamp(TEST_START_TIME().into()); + + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 2); + + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); +} + +#[test] +fn test_start_tournament_with_free_game() { + let contracts = setup(); + + utils::impersonate(OWNER()); + + let tournament_id = create_basic_tournament(contracts.tournament); + + contracts.tournament.enter_tournament(tournament_id, Option::None); + + testing::set_block_timestamp(TEST_START_TIME().into()); + + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + + approve_free_game_cost(contracts.eth, contracts.golden_token, 1, contracts.tournament); + + contracts + .tournament + .start_tournament( + tournament_id, false, Option::None, ZERO(), array![1].span(), array![].span() + ); + + // check tournament entries + assert(contracts.tournament.tournament_entries(tournament_id) == 1, 'Invalid entries'); + + // check golden tokens have returned back + assert(contracts.golden_token.owner_of(1) == OWNER(), 'Invalid owner'); +} + +#[test] +fn test_start_tournament_with_free_game_multiple() { + let contracts = setup(); + + utils::impersonate(OWNER()); + + let tournament_id = create_basic_tournament(contracts.tournament); + + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + + testing::set_block_timestamp(TEST_START_TIME().into()); + + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 2); + + contracts.golden_token.mint(OWNER(), 2); + + contracts.eth.approve(contracts.tournament.contract_address, 400000000000000); + contracts.golden_token.approve(contracts.tournament.contract_address, 1); + contracts.golden_token.approve(contracts.tournament.contract_address, 2); + + contracts + .tournament + .start_tournament( + tournament_id, false, Option::None, ZERO(), array![1, 2].span(), array![].span() + ); + + // check tournament entries + assert(contracts.tournament.tournament_entries(tournament_id) == 2, 'Invalid entries'); + + // check golden tokens have returned back + assert(contracts.golden_token.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.golden_token.owner_of(2) == OWNER(), 'Invalid owner'); +} + +#[test] +fn test_start_tournament_with_free_game_multiple_and_lords() { + let contracts = setup(); + + utils::impersonate(OWNER()); + + let tournament_id = create_basic_tournament(contracts.tournament); + + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + + testing::set_block_timestamp(TEST_START_TIME().into()); + + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 2); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::LaunchTournamentChampion, 1); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::LaunchTournamentChampion, 2); + + contracts.golden_token.mint(OWNER(), 2); + contracts.blobert.mint(OWNER(), 2); + + contracts.eth.approve(contracts.tournament.contract_address, 1000000000000000); + contracts.golden_token.approve(contracts.tournament.contract_address, 1); + contracts.golden_token.approve(contracts.tournament.contract_address, 2); + contracts.blobert.approve(contracts.tournament.contract_address, 1); + contracts.blobert.approve(contracts.tournament.contract_address, 2); + contracts.lords.approve(contracts.tournament.contract_address, 50000000000000000000); + + contracts + .tournament + .start_tournament( + tournament_id, false, Option::None, ZERO(), array![1, 2].span(), array![1, 2].span() + ); + + // check tournament entries + assert(contracts.tournament.tournament_entries(tournament_id) == 5, 'Invalid entries'); + + // check golden tokens and bloberts have returned back + assert(contracts.golden_token.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.golden_token.owner_of(2) == OWNER(), 'Invalid owner'); + assert(contracts.blobert.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.blobert.owner_of(2) == OWNER(), 'Invalid owner'); +} + +#[test] +#[should_panic(expected: ('too many free games', 'ENTRYPOINT_FAILED'))] +fn test_start_tournament_with_free_game_over_start_count() { + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); + let tournament_id = create_basic_tournament(contracts.tournament); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 2); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 2); + + contracts.golden_token.mint(OWNER(), 2); + + contracts.eth.approve(contracts.tournament.contract_address, 400000000000000); + contracts.golden_token.approve(contracts.tournament.contract_address, 1); + contracts.golden_token.approve(contracts.tournament.contract_address, 2); - tournament.start_tournament(tournament_id, false, Option::None); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament( + tournament_id, false, Option::Some(1), ZERO(), array![1, 2].span(), array![].span() + ); } +#[test] +#[should_panic(expected: ('too many free games', 'ENTRYPOINT_FAILED'))] +fn test_start_tournament_with_free_game_over_entries() { + let contracts = setup(); + + utils::impersonate(OWNER()); + + let tournament_id = create_basic_tournament(contracts.tournament); + + contracts.tournament.enter_tournament(tournament_id, Option::None); + + testing::set_block_timestamp(TEST_START_TIME().into()); + + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 1); + contracts.loot_survivor.set_free_game_available(FreeGameTokenType::GoldenToken, 2); + + contracts.golden_token.mint(OWNER(), 2); + + contracts.eth.approve(contracts.tournament.contract_address, 400000000000000); + contracts.golden_token.approve(contracts.tournament.contract_address, 1); + contracts.golden_token.approve(contracts.tournament.contract_address, 2); + + contracts + .tournament + .start_tournament( + tournament_id, false, Option::None, ZERO(), array![1, 2].span(), array![].span() + ); +} // // Test submitting scores // #[test] fn test_submit_scores() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); + let tournament_id = create_basic_tournament(contracts.tournament); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); - let scores = tournament.top_scores(tournament_id); + contracts.tournament.submit_scores(tournament_id, array![1]); + let scores = contracts.tournament.top_scores(tournament_id); assert(scores.len() == 1, 'Invalid scores length'); assert(*scores.at(0) == 1, 'Invalid score'); } #[test] fn test_submit_multiple_scores() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -970,34 +1273,36 @@ fn test_submit_multiple_scores() { Option::None, // zero entry premium ); - tournament.enter_tournament(tournament_id, Option::None); - tournament.enter_tournament(tournament_id, Option::None); - tournament.enter_tournament(tournament_id, Option::None); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 4); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 4); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); let submitted_adventurer = create_dead_adventurer_with_xp(2); - loot_survivor.set_adventurer(2, submitted_adventurer); + contracts.loot_survivor.set_adventurer(2, submitted_adventurer); let submitted_adventurer = create_dead_adventurer_with_xp(5); - loot_survivor.set_adventurer(3, submitted_adventurer); + contracts.loot_survivor.set_adventurer(3, submitted_adventurer); let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(4, submitted_adventurer); + contracts.loot_survivor.set_adventurer(4, submitted_adventurer); - tournament.submit_scores(tournament_id, array![3, 2, 1]); - let scores = tournament.top_scores(tournament_id); + contracts.tournament.submit_scores(tournament_id, array![3, 2, 1]); + let scores = contracts.tournament.top_scores(tournament_id); assert(scores.len() == 3, 'Invalid scores length'); assert(*scores.at(0) == 3, 'Invalid score'); assert(*scores.at(1) == 2, 'Invalid score'); @@ -1006,11 +1311,12 @@ fn test_submit_multiple_scores() { #[test] fn test_submit_scores_tiebreaker() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); + utils::impersonate(OWNER()); - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1023,31 +1329,33 @@ fn test_submit_scores_tiebreaker() { ); // Complete tournament with tied scores but different death dates - tournament.enter_tournament(tournament_id, Option::None); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 2); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 2); - tournament.start_tournament(tournament_id, true, Option::None); + contracts + .tournament + .start_tournament(tournament_id, true, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); let adventurer1 = create_dead_adventurer_with_xp(1); let adventurer2 = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, adventurer1); - loot_survivor.set_adventurer(2, adventurer2); + contracts.loot_survivor.set_adventurer(1, adventurer1); + contracts.loot_survivor.set_adventurer(2, adventurer2); // Same score (1) but different death timestamps let adventurer1_metadata = create_adventurer_metadata_with_death_date(100); let adventurer2_metadata = create_adventurer_metadata_with_death_date(50); - loot_survivor.set_adventurer_meta(1, adventurer1_metadata); - loot_survivor.set_adventurer_meta(2, adventurer2_metadata); + contracts.loot_survivor.set_adventurer_meta(1, adventurer1_metadata); + contracts.loot_survivor.set_adventurer_meta(2, adventurer2_metadata); - tournament.submit_scores(tournament_id, array![2, 1]); + contracts.tournament.submit_scores(tournament_id, array![2, 1]); - let scores = tournament.top_scores(tournament_id); + let scores = contracts.tournament.top_scores(tournament_id); assert(*scores.at(0) == 2, 'Wrong tiebreaker winner'); assert(*scores.at(1) == 1, 'Wrong tiebreaker loser'); } @@ -1055,13 +1363,13 @@ fn test_submit_scores_tiebreaker() { #[test] #[should_panic(expected: ('tournament already settled', 'ENTRYPOINT_FAILED'))] fn test_submit_scores_after_submission_period() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // Create tournament with specific timing - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1074,37 +1382,39 @@ fn test_submit_scores_after_submission_period() { ); // Enter tournament - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Move to tournament start time testing::set_block_timestamp(TEST_START_TIME().into()); // Start tournament - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // Create adventurer with score let submitted_adventurer = create_dead_adventurer_with_xp(10); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); // Move timestamp to after submission period ends // Tournament end (3 + MIN_REGISTRATION_PERIOD) + submission period testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); // This should panic with 'tournament already settled' - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); } #[test] #[should_panic(expected: ('tournament not ended', 'ENTRYPOINT_FAILED'))] fn test_submit_scores_before_tournament_ends() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // Create tournament with future start time - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1117,33 +1427,35 @@ fn test_submit_scores_before_tournament_ends() { ); // Enter tournament - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Set timestamp before tournament start time testing::set_block_timestamp(TEST_START_TIME().into()); // Start tournament - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // Create adventurer with score let submitted_adventurer = create_dead_adventurer_with_xp(10); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); // Attempt to submit scores before tournament starts // This should panic with 'tournament not started' - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); } #[test] fn test_submit_scores_replace_lower_score() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // Create tournament with multiple top scores - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1160,32 +1472,38 @@ fn test_submit_scores_replace_lower_score() { let player3 = starknet::contract_address_const::<0x789>(); // Enter tournament with all players - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); utils::impersonate(player2); - eth.mint(player2, STARTING_BALANCE); - lords.mint(player2, STARTING_BALANCE); - tournament.enter_tournament(tournament_id, Option::None); + contracts.eth.mint(player2, STARTING_BALANCE); + contracts.lords.mint(player2, STARTING_BALANCE); + contracts.tournament.enter_tournament(tournament_id, Option::None); utils::impersonate(player3); - eth.mint(player3, STARTING_BALANCE); - lords.mint(player3, STARTING_BALANCE); - tournament.enter_tournament(tournament_id, Option::None); + contracts.eth.mint(player3, STARTING_BALANCE); + contracts.lords.mint(player3, STARTING_BALANCE); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Start tournament for all players testing::set_block_timestamp(TEST_START_TIME().into()); utils::impersonate(OWNER()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player2); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player3); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); @@ -1194,23 +1512,23 @@ fn test_submit_scores_replace_lower_score() { let mid_score = create_dead_adventurer_with_xp(10); let high_score = create_dead_adventurer_with_xp(15); - loot_survivor.set_adventurer(1, low_score); // Owner's adventurer - loot_survivor.set_adventurer(2, mid_score); // Player2's adventurer - loot_survivor.set_adventurer(3, high_score); // Player3's adventurer + contracts.loot_survivor.set_adventurer(1, low_score); // Owner's adventurer + contracts.loot_survivor.set_adventurer(2, mid_score); // Player2's adventurer + contracts.loot_survivor.set_adventurer(3, high_score); // Player3's adventurer utils::impersonate(OWNER()); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); // // Verify initial rankings - let scores = tournament.top_scores(tournament_id); + let scores = contracts.tournament.top_scores(tournament_id); assert(scores.len() == 1, 'Invalid scores length'); assert(*scores.at(0) == 1, 'Wrong top score'); // owner utils::impersonate(player2); - tournament.submit_scores(tournament_id, array![1, 3, 2]); + contracts.tournament.submit_scores(tournament_id, array![1, 3, 2]); // Verify updated rankings - let updated_scores = tournament.top_scores(tournament_id); + let updated_scores = contracts.tournament.top_scores(tournament_id); assert(updated_scores.len() == 3, 'Invalid updated scores length'); assert(*updated_scores.at(0) == 1, 'Wrong new top score'); // Owner assert(*updated_scores.at(1) == 3, 'Wrong new second score'); // Player3 @@ -1223,149 +1541,128 @@ fn test_submit_scores_replace_lower_score() { #[test] fn test_distribute_prizes_with_prizes() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); - register_tokens_for_test(tournament, erc20, erc721); + let tournament_id = create_basic_tournament(contracts.tournament); + // register_tokens_for_test(tournament, erc20, erc721); - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament + contracts.erc20.approve(contracts.tournament.contract_address, STARTING_BALANCE); + contracts.erc721.approve(contracts.tournament.contract_address, 1); + contracts + .tournament .add_prize( tournament_id, - erc20.contract_address, + contracts.erc20.contract_address, TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), 1 ); - tournament + contracts + .tournament .add_prize( tournament_id, - erc721.contract_address, + contracts.erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 ); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); - tournament.distribute_prizes(tournament_id, array![1, 2]); + contracts.tournament.distribute_prizes(tournament_id, array![1, 2]); // check balances of owner after claiming prizes - assert(erc20.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); - assert(erc721.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.erc20.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); + assert(contracts.erc721.owner_of(1) == OWNER(), 'Invalid owner'); } #[test] #[should_panic(expected: ('prize already claimed', 'ENTRYPOINT_FAILED'))] fn test_distribute_prizes_prize_already_claimed() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - let tournament_id = create_basic_tournament(tournament); - register_tokens_for_test(tournament, erc20, erc721); + let tournament_id = create_basic_tournament(contracts.tournament); + // register_tokens_for_test(tournament, erc20, erc721); - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament + contracts.erc20.approve(contracts.tournament.contract_address, STARTING_BALANCE); + contracts.erc721.approve(contracts.tournament.contract_address, 1); + contracts + .tournament .add_prize( tournament_id, - erc20.contract_address, + contracts.erc20.contract_address, TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), 1 ); - tournament + contracts + .tournament .add_prize( tournament_id, - erc721.contract_address, + contracts.erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 ); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); - tournament.distribute_prizes(tournament_id, array![1, 2]); - tournament.distribute_prizes(tournament_id, array![1, 2]); + contracts.tournament.distribute_prizes(tournament_id, array![1, 2]); + contracts.tournament.distribute_prizes(tournament_id, array![1, 2]); } #[test] fn test_distribute_prizes_with_gated_tokens_criteria() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); + // register_tokens_for_test(tournament, erc20, erc721); let gated_type = GatedType::token( GatedToken { - token: erc721.contract_address, + token: contracts.erc721.contract_address, entry_type: GatedEntryType::criteria( array![EntryCriteria { token_id: 1, entry_count: 2 }].span() ), } ); - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1377,65 +1674,62 @@ fn test_distribute_prizes_with_gated_tokens_criteria() { Option::None, // zero entry premium ); - let tournament_data = tournament.tournament(tournament_id); + let tournament_data = contracts.tournament.tournament(tournament_id); assert( tournament_data.gated_type == Option::Some(gated_type), 'Invalid tournament gated token' ); let gated_submission_type = GatedSubmissionType::token_id(1); - tournament.enter_tournament(tournament_id, Option::Some(gated_submission_type)); + contracts.tournament.enter_tournament(tournament_id, Option::Some(gated_submission_type)); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 2); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 2); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // check tournament entries - assert(tournament.tournament_entries(tournament_id) == 2, 'Invalid entries'); + assert(contracts.tournament.tournament_entries(tournament_id) == 2, 'Invalid entries'); // check owner now has game token - assert(loot_survivor.owner_of(1) == OWNER(), 'Invalid owner'); - assert(loot_survivor.owner_of(2) == OWNER(), 'Invalid owner'); + assert(contracts.loot_survivor.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.loot_survivor.owner_of(2) == OWNER(), 'Invalid owner'); // check lords and eth balances of loot survivor after starting assert( - lords.balance_of(loot_survivor.contract_address) == 2 * 50000000000000000000, + contracts.lords.balance_of(contracts.loot_survivor.contract_address) == 2 + * 50000000000000000000, 'Invalid balance' ); assert( - eth.balance_of(loot_survivor.contract_address) == 2 * 200000000000000, 'Invalid balance' + contracts.eth.balance_of(contracts.loot_survivor.contract_address) == 2 * 200000000000000, + 'Invalid balance' ); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); } #[test] fn test_distribute_prizes_with_gated_tokens_uniform() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); + // register_tokens_for_test(tournament, erc20, erc721); let gated_type = GatedType::token( - GatedToken { token: erc721.contract_address, entry_type: GatedEntryType::uniform(3), } + GatedToken { + token: contracts.erc721.contract_address, entry_type: GatedEntryType::uniform(3), + } ); - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1447,54 +1741,58 @@ fn test_distribute_prizes_with_gated_tokens_uniform() { Option::None, // zero entry premium ); - let tournament_data = tournament.tournament(tournament_id); + let tournament_data = contracts.tournament.tournament(tournament_id); assert( tournament_data.gated_type == Option::Some(gated_type), 'Invalid tournament gated token' ); let gated_submission_type = GatedSubmissionType::token_id(1); - tournament.enter_tournament(tournament_id, Option::Some(gated_submission_type)); + contracts.tournament.enter_tournament(tournament_id, Option::Some(gated_submission_type)); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 3); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 3); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // check tournament entries - assert(tournament.tournament_entries(tournament_id) == 3, 'Invalid entries'); + assert(contracts.tournament.tournament_entries(tournament_id) == 3, 'Invalid entries'); // check owner now has game token - assert(loot_survivor.owner_of(1) == OWNER(), 'Invalid owner'); - assert(loot_survivor.owner_of(2) == OWNER(), 'Invalid owner'); - assert(loot_survivor.owner_of(3) == OWNER(), 'Invalid owner'); + assert(contracts.loot_survivor.owner_of(1) == OWNER(), 'Invalid owner'); + assert(contracts.loot_survivor.owner_of(2) == OWNER(), 'Invalid owner'); + assert(contracts.loot_survivor.owner_of(3) == OWNER(), 'Invalid owner'); // check lords and eth balances of loot survivor after starting assert( - lords.balance_of(loot_survivor.contract_address) == 3 * 50000000000000000000, + contracts.lords.balance_of(contracts.loot_survivor.contract_address) == 3 + * 50000000000000000000, 'Invalid balance' ); assert( - eth.balance_of(loot_survivor.contract_address) == 3 * 200000000000000, 'Invalid balance' + contracts.eth.balance_of(contracts.loot_survivor.contract_address) == 3 * 200000000000000, + 'Invalid balance' ); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); } #[test] fn test_distribute_prizes_with_gated_tournaments() { - let (_world, mut tournament, mut loot_survivor, _pragma, mut eth, mut lords, _erc20, _erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); // create a standard tournament with one winner - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1506,21 +1804,23 @@ fn test_distribute_prizes_with_gated_tournaments() { Option::None, // zero entry premium ); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); @@ -1529,7 +1829,8 @@ fn test_distribute_prizes_with_gated_tournaments() { let current_time = get_block_timestamp(); - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1541,20 +1842,22 @@ fn test_distribute_prizes_with_gated_tournaments() { Option::None, // zero entry premium ); - let tournament_data = tournament.tournament(tournament_id); + let tournament_data = contracts.tournament.tournament(tournament_id); assert( tournament_data.gated_type == Option::Some(gated_type), 'Invalid tournament gated token' ); // submit game id 1 let gated_submission_type = GatedSubmissionType::game_id(array![1].span()); - tournament.enter_tournament(tournament_id, Option::Some(gated_submission_type)); + contracts.tournament.enter_tournament(tournament_id, Option::Some(gated_submission_type)); testing::set_block_timestamp(current_time + MIN_REGISTRATION_PERIOD.into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp( current_time + 1 + MIN_REGISTRATION_PERIOD.into() + MIN_TOURNAMENT_LENGTH.into() @@ -1563,36 +1866,27 @@ fn test_distribute_prizes_with_gated_tournaments() { // this is now adventurer 2 // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(2, submitted_adventurer); + contracts.loot_survivor.set_adventurer(2, submitted_adventurer); - tournament.submit_scores(tournament_id, array![2]); + contracts.tournament.submit_scores(tournament_id, array![2]); } #[test] fn test_distribute_prizes_with_premiums() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); + // register_tokens_for_test(tournament, erc20, erc721); let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 1, token_distribution: array![100].span(), creator_fee: 0, }; - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1604,70 +1898,66 @@ fn test_distribute_prizes_with_premiums() { Option::Some(entry_premium), // zero entry premium ); - let tournament_data = tournament.tournament(tournament_id); + let tournament_data = contracts.tournament.tournament(tournament_id); assert( tournament_data.entry_premium == Option::Some(entry_premium), 'Invalid entry premium' ); // handle approval for the premium - erc20.approve(tournament.contract_address, 1); + contracts.erc20.approve(contracts.tournament.contract_address, 1); - tournament.enter_tournament(tournament_id, Option::None); + contracts.tournament.enter_tournament(tournament_id, Option::None); // check owner now has 1 less premium token - assert(erc20.balance_of(OWNER()) == STARTING_BALANCE - 1, 'Invalid balance'); + assert(contracts.erc20.balance_of(OWNER()) == STARTING_BALANCE - 1, 'Invalid balance'); // check tournament now has premium funds - assert(erc20.balance_of(tournament.contract_address) == 1, 'Invalid balance'); + assert( + contracts.erc20.balance_of(contracts.tournament.contract_address) == 1, + 'Invalid + balance' + ); testing::set_block_timestamp(TEST_START_TIME().into()); - approve_game_costs(eth, lords, tournament, 1); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // set data to a dead adventurer with 1 xp let submitted_adventurer = create_dead_adventurer_with_xp(1); - loot_survivor.set_adventurer(1, submitted_adventurer); + contracts.loot_survivor.set_adventurer(1, submitted_adventurer); - tournament.submit_scores(tournament_id, array![1]); + contracts.tournament.submit_scores(tournament_id, array![1]); testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); - tournament.distribute_prizes(tournament_id, array![1]); + contracts.tournament.distribute_prizes(tournament_id, array![1]); // check owner now has all premium funds back - assert(erc20.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); + assert(contracts.erc20.balance_of(OWNER()) == STARTING_BALANCE, 'Invalid balance'); } #[test] fn test_distribute_prizes_with_premium_creator_fee() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); // Create premium with 10% creator fee and 90% to winner let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 100, // 100 tokens per entry token_distribution: array![100].span(), // 100% to winner creator_fee: 10, // 10% creator fee }; - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1681,84 +1971,83 @@ fn test_distribute_prizes_with_premium_creator_fee() { // Enter tournament with two players utils::impersonate(OWNER()); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); let player2 = starknet::contract_address_const::<0x456>(); utils::impersonate(player2); - erc20.mint(player2, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player2, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Start tournament and submit scores testing::set_block_timestamp(TEST_START_TIME().into()); - let creator_initial_balance = erc20.balance_of(OWNER()); + let creator_initial_balance = contracts.erc20.balance_of(OWNER()); utils::impersonate(OWNER()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); - - // Verify creator fee distribution (10% of 200 total = 20) - assert(erc20.balance_of(OWNER()) == creator_initial_balance + 20, 'Invalid creator fee'); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player2); - eth.mint(player2, STARTING_BALANCE); - lords.mint(player2, STARTING_BALANCE); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts.eth.mint(player2, STARTING_BALANCE); + contracts.lords.mint(player2, STARTING_BALANCE); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); // Set scores (player2 wins) let winner_adventurer = create_dead_adventurer_with_xp(10); let loser_adventurer = create_dead_adventurer_with_xp(5); - loot_survivor.set_adventurer(1, loser_adventurer); - loot_survivor.set_adventurer(2, winner_adventurer); + contracts.loot_survivor.set_adventurer(1, loser_adventurer); + contracts.loot_survivor.set_adventurer(2, winner_adventurer); utils::impersonate(OWNER()); - tournament.submit_scores(tournament_id, array![2]); + contracts.tournament.submit_scores(tournament_id, array![2]); + + // Verify creator fee distribution (10% of 200 total = 20) + assert( + contracts.erc20.balance_of(OWNER()) == creator_initial_balance + 20, + 'Invalid creator + fee' + ); // Check initial balances - let winner_initial_balance = erc20.balance_of(player2); + let winner_initial_balance = contracts.erc20.balance_of(player2); // Distribute rewards testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); - tournament.distribute_prizes(tournament_id, array![1]); + contracts.tournament.distribute_prizes(tournament_id, array![1]); // Verify winner prize distribution (90% of 200 total = 180) assert( - erc20.balance_of(player2) == winner_initial_balance + 180, 'Invalid winner distribution' + contracts.erc20.balance_of(player2) == winner_initial_balance + 180, + 'Invalid winner distribution' ); } #[test] fn test_distribute_prizes_with_premium_multiple_winners() { - let ( - _world, - mut tournament, - mut loot_survivor, - _pragma, - mut eth, - mut lords, - mut erc20, - mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); // Create premium with 10% creator fee and split remaining 90% between top 3: // 1st: 50%, 2nd: 30%, 3rd: 20% let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 100, // 100 tokens per entry token_distribution: array![50, 30, 20].span(), // Distribution percentages creator_fee: 10, // 10% creator fee }; - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1777,54 +2066,62 @@ fn test_distribute_prizes_with_premium_multiple_winners() { // Owner enters utils::impersonate(OWNER()); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Player 2 enters utils::impersonate(player2); - erc20.mint(player2, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player2, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Player 3 enters utils::impersonate(player3); - erc20.mint(player3, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player3, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Player 4 enters utils::impersonate(player4); - erc20.mint(player4, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player4, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Start tournament testing::set_block_timestamp(TEST_START_TIME().into()); - let third_initial = erc20.balance_of(OWNER()); + let third_initial = contracts.erc20.balance_of(OWNER()); // Start games for all players utils::impersonate(OWNER()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player2); - eth.mint(player2, STARTING_BALANCE); - lords.mint(player2, STARTING_BALANCE); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts.eth.mint(player2, STARTING_BALANCE); + contracts.lords.mint(player2, STARTING_BALANCE); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player3); - eth.mint(player3, STARTING_BALANCE); - lords.mint(player3, STARTING_BALANCE); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts.eth.mint(player3, STARTING_BALANCE); + contracts.lords.mint(player3, STARTING_BALANCE); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player4); - eth.mint(player4, STARTING_BALANCE); - lords.mint(player4, STARTING_BALANCE); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts.eth.mint(player4, STARTING_BALANCE); + contracts.lords.mint(player4, STARTING_BALANCE); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); testing::set_block_timestamp(TEST_END_TIME().into()); @@ -1834,23 +2131,23 @@ fn test_distribute_prizes_with_premium_multiple_winners() { let third_place = create_dead_adventurer_with_xp(50); let fourth_place = create_dead_adventurer_with_xp(25); - loot_survivor.set_adventurer(2, first_place); // player2's adventurer - loot_survivor.set_adventurer(3, second_place); // player3's adventurer - loot_survivor.set_adventurer(1, third_place); // owner's adventurer - loot_survivor.set_adventurer(4, fourth_place); // player4's adventurer + contracts.loot_survivor.set_adventurer(2, first_place); // player2's adventurer + contracts.loot_survivor.set_adventurer(3, second_place); // player3's adventurer + contracts.loot_survivor.set_adventurer(1, third_place); // owner's adventurer + contracts.loot_survivor.set_adventurer(4, fourth_place); // player4's adventurer // Submit scores utils::impersonate(player2); - tournament.submit_scores(tournament_id, array![2, 3, 1]); + contracts.tournament.submit_scores(tournament_id, array![2, 3, 1]); // Store initial balances - let first_initial = erc20.balance_of(player2); - let second_initial = erc20.balance_of(player3); + let first_initial = contracts.erc20.balance_of(player2); + let second_initial = contracts.erc20.balance_of(player3); // Distribute rewards testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); // 3 premium prizes - tournament.distribute_prizes(tournament_id, array![1, 2, 3]); + contracts.tournament.distribute_prizes(tournament_id, array![1, 2, 3]); // Total pool = 4 players * 100 tokens = 400 tokens // Creator fee = 10% of 400 = 40 tokens @@ -1860,30 +2157,38 @@ fn test_distribute_prizes_with_premium_multiple_winners() { // 3rd place (20%) = 72 tokens // Verify winner distributions - assert(erc20.balance_of(player2) == first_initial + 180, 'Invalid first distribution'); - assert(erc20.balance_of(player3) == second_initial + 108, 'Invalid second distribution'); - assert(erc20.balance_of(OWNER()) == third_initial + 72 + 40, 'Invalid third distribution'); + assert( + contracts.erc20.balance_of(player2) == first_initial + 180, 'Invalid first + distribution' + ); + assert( + contracts.erc20.balance_of(player3) == second_initial + 108, + 'Invalid second + distribution' + ); + assert( + contracts.erc20.balance_of(OWNER()) == third_initial + 72 + 40, + 'Invalid third + distribution' + ); } #[test] fn test_tournament_with_no_submissions() { - let ( - _world, mut tournament, _loot_survivor, _pragma, mut eth, mut lords, mut erc20, mut erc721, - ) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); // Create tournament with prizes and premium let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 100, token_distribution: array![100].span(), // 100% to winner creator_fee: 10, // 10% creator fee }; - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -1896,19 +2201,21 @@ fn test_tournament_with_no_submissions() { ); // Add some prizes - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament + contracts.erc20.approve(contracts.tournament.contract_address, STARTING_BALANCE); + contracts.erc721.approve(contracts.tournament.contract_address, 1); + contracts + .tournament .add_prize( tournament_id, - erc20.contract_address, + contracts.erc20.contract_address, TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), 1 ); - tournament + contracts + .tournament .add_prize( tournament_id, - erc721.contract_address, + contracts.erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 ); @@ -1918,41 +2225,47 @@ fn test_tournament_with_no_submissions() { let player3 = starknet::contract_address_const::<0x789>(); // Enter tournament with all players - erc20.mint(OWNER(), 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(OWNER(), 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); utils::impersonate(player2); - erc20.mint(player2, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player2, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); utils::impersonate(player3); - erc20.mint(player3, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player3, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Start tournament for all players testing::set_block_timestamp(TEST_START_TIME().into()); // Store initial balances - let creator_initial = erc20.balance_of(OWNER()); + let creator_initial = contracts.erc20.balance_of(OWNER()); utils::impersonate(OWNER()); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player2); - eth.mint(player2, STARTING_BALANCE); - lords.mint(player2, STARTING_BALANCE); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts.eth.mint(player2, STARTING_BALANCE); + contracts.lords.mint(player2, STARTING_BALANCE); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); utils::impersonate(player3); - eth.mint(player3, STARTING_BALANCE); - lords.mint(player3, STARTING_BALANCE); - approve_game_costs(eth, lords, tournament, 1); - tournament.start_tournament(tournament_id, false, Option::None); + contracts.eth.mint(player3, STARTING_BALANCE); + contracts.lords.mint(player3, STARTING_BALANCE); + approve_game_costs(contracts.eth, contracts.lords, contracts.tournament, 1); + contracts + .tournament + .start_tournament(tournament_id, false, Option::None, ZERO(), array![].span(), array![].span()); // Move to after tournament and submission period without any score submissions testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); @@ -1960,42 +2273,41 @@ fn test_tournament_with_no_submissions() { // Distribute rewards utils::impersonate(OWNER()); // 2 deposited prizes and 1 tournament premium prize - tournament.distribute_prizes(tournament_id, array![1, 2, 3]); + contracts.tournament.distribute_prizes(tournament_id, array![1, 2, 3]); // Verify final state - let final_scores = tournament.top_scores(tournament_id); + let final_scores = contracts.tournament.top_scores(tournament_id); assert(final_scores.len() == 0, 'Should have no scores'); // Verify first caller gets all prizes // creator also gets the prize balance back (STARTING BALANCE) assert( - erc20.balance_of(OWNER()) == creator_initial + 300 + STARTING_BALANCE, + contracts.erc20.balance_of(OWNER()) == creator_initial + 300 + STARTING_BALANCE, 'Invalid owner refund' ); - assert(erc20.balance_of(player2) == 0, 'Invalid player2 refund'); - assert(erc20.balance_of(player3) == 0, 'Invalid player3 refund'); + assert(contracts.erc20.balance_of(player2) == 0, 'Invalid player2 refund'); + assert(contracts.erc20.balance_of(player3) == 0, 'Invalid player3 refund'); // Verify prize returns to tournament creator - assert(erc721.owner_of(1) == OWNER(), 'Prize should return to caller'); + assert(contracts.erc721.owner_of(1) == OWNER(), 'Prize should return to caller'); } #[test] fn test_tournament_with_no_starts() { - let (_world, mut tournament, _loot_survivor, _pragma, _eth, _lords, mut erc20, mut erc721,) = - setup(); + let contracts = setup(); utils::impersonate(OWNER()); - register_tokens_for_test(tournament, erc20, erc721); // Create tournament with prizes and premium let entry_premium = Premium { - token: erc20.contract_address, + token: contracts.erc20.contract_address, token_amount: 100, token_distribution: array![100].span(), // 100% to winner creator_fee: 10, // 10% creator fee }; - let tournament_id = tournament + let tournament_id = contracts + .tournament .create_tournament( TOURNAMENT_NAME(), TOURNAMENT_DESCRIPTION(), @@ -2008,19 +2320,21 @@ fn test_tournament_with_no_starts() { ); // Add some prizes - erc20.approve(tournament.contract_address, STARTING_BALANCE); - erc721.approve(tournament.contract_address, 1); - tournament + contracts.erc20.approve(contracts.tournament.contract_address, STARTING_BALANCE); + contracts.erc721.approve(contracts.tournament.contract_address, 1); + contracts + .tournament .add_prize( tournament_id, - erc20.contract_address, + contracts.erc20.contract_address, TokenDataType::erc20(ERC20Data { token_amount: STARTING_BALANCE.low }), 1 ); - tournament + contracts + .tournament .add_prize( tournament_id, - erc721.contract_address, + contracts.erc721.contract_address, TokenDataType::erc721(ERC721Data { token_id: 1 }), 1 ); @@ -2030,25 +2344,25 @@ fn test_tournament_with_no_starts() { let player3 = starknet::contract_address_const::<0x789>(); // Enter tournament with all players - erc20.mint(OWNER(), 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(OWNER(), 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); utils::impersonate(player2); - erc20.mint(player2, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player2, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); utils::impersonate(player3); - erc20.mint(player3, 100); - erc20.approve(tournament.contract_address, 100); - tournament.enter_tournament(tournament_id, Option::None); + contracts.erc20.mint(player3, 100); + contracts.erc20.approve(contracts.tournament.contract_address, 100); + contracts.tournament.enter_tournament(tournament_id, Option::None); // Start tournament for all players testing::set_block_timestamp(TEST_START_TIME().into()); // Store initial balances - let creator_initial = erc20.balance_of(OWNER()); + let creator_initial = contracts.erc20.balance_of(OWNER()); // Move to after tournament and submission period without any score submissions testing::set_block_timestamp((TEST_END_TIME() + MIN_SUBMISSION_PERIOD).into()); @@ -2056,22 +2370,21 @@ fn test_tournament_with_no_starts() { // Distribute rewards utils::impersonate(OWNER()); // 2 deposited prizes and 1 tournament premium prize - tournament.distribute_prizes(tournament_id, array![1, 2, 3]); + contracts.tournament.distribute_prizes(tournament_id, array![1, 2, 3]); // Verify final state - let final_scores = tournament.top_scores(tournament_id); + let final_scores = contracts.tournament.top_scores(tournament_id); assert(final_scores.len() == 0, 'Should have no scores'); // Verify first caller gets all prizes // creator also gets the prize balance back (STARTING BALANCE) assert( - erc20.balance_of(OWNER()) == creator_initial + 300 + STARTING_BALANCE, + contracts.erc20.balance_of(OWNER()) == creator_initial + 300 + STARTING_BALANCE, 'Invalid owner refund' ); - assert(erc20.balance_of(player2) == 0, 'Invalid player2 refund'); - assert(erc20.balance_of(player3) == 0, 'Invalid player3 refund'); + assert(contracts.erc20.balance_of(player2) == 0, 'Invalid player2 refund'); + assert(contracts.erc20.balance_of(player3) == 0, 'Invalid player3 refund'); // Verify prize returns to tournament creator - assert(erc721.owner_of(1) == OWNER(), 'Prize should return to caller'); + assert(contracts.erc721.owner_of(1) == OWNER(), 'Prize should return to caller'); } - diff --git a/contracts/src/ls15_components/tests/tournament_mock.cairo b/contracts/src/ls15_components/tests/tournament_mock.cairo index f2d85c8..0d80992 100644 --- a/contracts/src/ls15_components/tests/tournament_mock.cairo +++ b/contracts/src/ls15_components/tests/tournament_mock.cairo @@ -1,7 +1,7 @@ use starknet::ContractAddress; use dojo::world::IWorldDispatcher; use tournament::ls15_components::models::tournament::{ - TournamentModel, Token, Premium, TokenDataType, GatedType, GatedSubmissionType + TournamentModel, Premium, TokenDataType, GatedType, GatedSubmissionType }; #[starknet::interface] @@ -15,6 +15,8 @@ pub trait ITournamentMock { fn tournament_prize_keys(self: @TState, tournament_id: u64) -> Array; fn top_scores(self: @TState, tournament_id: u64) -> Array; fn is_token_registered(self: @TState, token: ContractAddress) -> bool; + // TODO: add for V2 (only ERC721 tokens) + // fn register_tokens(ref self: TState, tokens: Array); fn create_tournament( ref self: TState, name: felt252, @@ -26,12 +28,17 @@ pub trait ITournamentMock { gated_type: Option, entry_premium: Option, ) -> u64; - fn register_tokens(ref self: TState, tokens: Array); fn enter_tournament( ref self: TState, tournament_id: u64, gated_submission_type: Option ); fn start_tournament( - ref self: TState, tournament_id: u64, start_all: bool, start_count: Option + ref self: TState, + tournament_id: u64, + start_all: bool, + start_count: Option, + client_reward_address: ContractAddress, + golden_token_free_game_ids: Option>, + blobert_free_game_ids: Option>, ); fn submit_scores(ref self: TState, tournament_id: u64, game_ids: Array); fn add_prize( @@ -49,8 +56,12 @@ pub trait ITournamentMock { lords_address: ContractAddress, loot_survivor_address: ContractAddress, oracle_address: ContractAddress, + golden_token: ContractAddress, + blobert: ContractAddress, safe_mode: bool, - test_mode: bool + test_mode: bool, + test_erc20: ContractAddress, + test_erc721: ContractAddress, ); } @@ -62,8 +73,12 @@ trait ITournamentMockInit { lords_address: ContractAddress, loot_survivor_address: ContractAddress, oracle_address: ContractAddress, + golden_token: ContractAddress, + blobert: ContractAddress, safe_mode: bool, - test_mode: bool + test_mode: bool, + test_erc20: ContractAddress, + test_erc721: ContractAddress, ); } @@ -99,8 +114,12 @@ pub mod tournament_mock { lords_address: ContractAddress, loot_survivor_address: ContractAddress, oracle_address: ContractAddress, + golden_token: ContractAddress, + blobert: ContractAddress, safe_mode: bool, - test_mode: bool + test_mode: bool, + test_erc20: ContractAddress, + test_erc721: ContractAddress, ) { self .tournament @@ -109,9 +128,13 @@ pub mod tournament_mock { lords_address, loot_survivor_address, oracle_address, + golden_token, + blobert, safe_mode, test_mode ); + self.tournament.initialize_erc20(test_erc20, "Test ERC20", "TERC20"); + self.tournament.initialize_erc721(test_erc721, "Test ERC721", "TERC721"); } } } diff --git a/contracts/src/ls15_components/tournament.cairo b/contracts/src/ls15_components/tournament.cairo index ce80474..2c980e9 100644 --- a/contracts/src/ls15_components/tournament.cairo +++ b/contracts/src/ls15_components/tournament.cairo @@ -1,6 +1,6 @@ use starknet::ContractAddress; use tournament::ls15_components::models::tournament::{ - TournamentModel, GatedType, Premium, Token, GatedSubmissionType, TokenDataType + TournamentModel, GatedType, Premium, GatedSubmissionType, TokenDataType }; /// @@ -15,7 +15,8 @@ trait ITournament { fn tournament_prize_keys(self: @TState, tournament_id: u64) -> Array; fn top_scores(self: @TState, tournament_id: u64) -> Array; fn is_token_registered(self: @TState, token: ContractAddress) -> bool; - fn register_tokens(ref self: TState, tokens: Array); + // TODO: add for V2 (only ERC721 tokens) + // fn register_tokens(ref self: TState, tokens: Array); fn create_tournament( ref self: TState, name: felt252, @@ -31,7 +32,13 @@ trait ITournament { ref self: TState, tournament_id: u64, gated_submission_type: Option ); fn start_tournament( - ref self: TState, tournament_id: u64, start_all: bool, start_count: Option + ref self: TState, + tournament_id: u64, + start_all: bool, + start_count: Option, + client_reward_address: ContractAddress, + golden_token_free_game_token_ids: Span, + blobert_free_game_token_ids: Span, ); fn submit_scores(ref self: TState, tournament_id: u64, game_ids: Array); fn add_prize( @@ -55,11 +62,10 @@ pub mod tournament_component { use core::num::traits::Zero; use tournament::ls15_components::constants::{ - VRF_COST_PER_GAME, TWO_POW_128, MIN_REGISTRATION_PERIOD, MAX_REGISTRATION_PERIOD, - MIN_TOURNAMENT_LENGTH, MAX_TOURNAMENT_LENGTH, MIN_SUBMISSION_PERIOD, MAX_SUBMISSION_PERIOD, + VRF_COST_PER_GAME, MIN_REGISTRATION_PERIOD, MAX_REGISTRATION_PERIOD, MIN_TOURNAMENT_LENGTH, + MAX_TOURNAMENT_LENGTH, MIN_SUBMISSION_PERIOD, MAX_SUBMISSION_PERIOD, TEST_MIN_REGISTRATION_PERIOD, TEST_MIN_SUBMISSION_PERIOD, TEST_MIN_TOURNAMENT_LENGTH, - GAME_EXPIRATION_PERIOD, ETHEREUM_ADDRESS, LORDS_ADDRESS, SURVIVORS_ADDRESS, ETH_SAFE_AMOUNT, - LORDS_SAFE_AMOUNT + GAME_EXPIRATION_PERIOD, ETHEREUM_ADDRESS, ETH_SAFE_AMOUNT, LORDS_SAFE_AMOUNT }; use tournament::ls15_components::interfaces::{ ILootSurvivorDispatcher, ILootSurvivorDispatcherTrait, IPragmaABIDispatcher, @@ -71,7 +77,7 @@ pub mod tournament_component { TournamentStartIdsModel, TournamentScoresModel, TournamentTotalsModel, TournamentPrizeKeysModel, PrizesModel, TokenModel, TournamentConfig, TokenDataType, EntryStatus, GatedType, GatedSubmissionType, GatedEntryType, GatedToken, Premium, ERC20Data, - Token + ERC721Data, FreeGameTokenType }; use tournament::ls15_components::interfaces::{WorldTrait, WorldImpl,}; use tournament::ls15_components::libs::store::{Store, StoreTrait}; @@ -85,14 +91,8 @@ pub mod tournament_component { contract_address_const }; - use openzeppelin_token::erc20::interface::{ - IERC20Dispatcher, IERC20DispatcherTrait, IERC20MetadataDispatcher, - IERC20MetadataDispatcherTrait - }; - use openzeppelin_token::erc721::interface::{ - IERC721Dispatcher, IERC721DispatcherTrait, IERC721MetadataDispatcher, - IERC721MetadataDispatcherTrait - }; + use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + use openzeppelin_token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; use adventurer::{adventurer::Adventurer}; @@ -119,6 +119,7 @@ pub mod tournament_component { pub const PREMIUM_DISTRIBUTIONS_NOT_100: felt252 = 'premium distributions not 100%'; pub const SUBMISSION_PERIOD_TOO_SHORT: felt252 = 'submission period too short'; pub const SUBMISSION_PERIOD_TOO_LONG: felt252 = 'submission period too long'; + pub const NOT_TOKEN_OWNER: felt252 = 'not token owner'; // // Register Tokens // @@ -148,6 +149,8 @@ pub mod tournament_component { pub const ADDRESS_ENTRIES_STARTED: felt252 = 'address entries started'; pub const START_COUNT_TOO_LARGE: felt252 = 'start count too large'; pub const TOURNAMENT_PERIOD_TOO_LONG: felt252 = 'period too long to start all'; + pub const FREE_GAME_NOT_AVAILABLE: felt252 = 'free game not available'; + pub const TOO_MANY_FREE_GAMES: felt252 = 'too many free games'; // // Submit Scores // @@ -232,13 +235,14 @@ pub mod tournament_component { self._is_token_registered(ref store, token) } - fn register_tokens(ref self: ComponentState, tokens: Array) { - let mut world = WorldTrait::storage( - self.get_contract().world_dispatcher(), @"tournament" - ); - let mut store: Store = StoreTrait::new(world); - self._register_tokens(ref store, tokens); - } + // TODO: add for V2 (use Ekubo tokens) + // fn register_tokens(ref self: ComponentState, tokens: Array) { + // let mut world = WorldTrait::storage( + // self.get_contract().world_dispatcher(), @"tournament" + // ); + // let mut store: Store = StoreTrait::new(world); + // self._register_tokens(ref store, tokens); + // } fn create_tournament( ref self: ComponentState, @@ -346,6 +350,9 @@ pub mod tournament_component { tournament_id: u64, start_all: bool, start_count: Option, + client_reward_address: ContractAddress, + golden_token_free_game_token_ids: Span, + blobert_free_game_token_ids: Span, ) { let mut world = WorldTrait::storage( self.get_contract().world_dispatcher(), @"tournament" @@ -353,17 +360,23 @@ pub mod tournament_component { let mut store: Store = StoreTrait::new(world); // assert tournament is active self._assert_tournament_active(ref store, tournament_id); + // assert not too many free games if start count supplied + self + ._assert_free_game_ids_not_larger_than_start_count( + start_count, golden_token_free_game_token_ids, blobert_free_game_token_ids + ); // if starting all games, assert the tournament period is within max if (start_all) { self._assert_tournament_period_within_max(ref store, tournament_id); } - let total_entries = store.get_total_entries(tournament_id); + let tournament_config = store.get_tournament_config(get_contract_address()); - // handle formatiing of premium config into prize keys - if (!total_entries.premiums_formatted) { - self._format_premium_config_into_prize_keys(ref store, tournament_id); - } + let mut ls_dispatcher = ILootSurvivorDispatcher { + contract_address: tournament_config.loot_survivor + }; + + // first get the number of entries for calculations and allowed starts let mut entries = 0; @@ -393,11 +406,59 @@ pub mod tournament_component { }; } - // define contract interfaces - let tournament_config = store.get_tournament_config(get_contract_address()); - let mut ls_dispatcher = ILootSurvivorDispatcher { - contract_address: tournament_config.loot_survivor + // assert not free games not longer + self + ._assert_free_game_ids_not_larger_than_entries( + entries, golden_token_free_game_token_ids, blobert_free_game_token_ids + ); + + let mut free_games = 0; + + let mut golden_token_index = 0; + loop { + if golden_token_index == golden_token_free_game_token_ids.len() { + break; + } + let golden_token_id = *golden_token_free_game_token_ids.at(golden_token_index); + self + ._assert_token_owner( + tournament_config.golden_token, golden_token_id, get_caller_address() + ); + // check if golden token free game is available + let free_game_available = ls_dispatcher + .free_game_available(FreeGameTokenType::GoldenToken, golden_token_id.low); + assert(free_game_available, Errors::FREE_GAME_NOT_AVAILABLE); + // flash loan golden tokens + IERC721Dispatcher { contract_address: tournament_config.golden_token } + .transfer_from(get_caller_address(), get_contract_address(), golden_token_id); + free_games += 1; + golden_token_index += 1; + }; + + let mut blobert_token_index = 0; + loop { + if blobert_token_index == blobert_free_game_token_ids.len() { + break; + } + let blobert_token_id = *blobert_free_game_token_ids.at(blobert_token_index); + self + ._assert_token_owner( + tournament_config.blobert, blobert_token_id, get_caller_address() + ); + // check if caller has blobert + let free_game_available = ls_dispatcher + .free_game_available( + FreeGameTokenType::LaunchTournamentChampion, blobert_token_id.low + ); + assert(free_game_available, Errors::FREE_GAME_NOT_AVAILABLE); + // flash loan bloberts + IERC721Dispatcher { contract_address: tournament_config.blobert } + .transfer_from(get_caller_address(), get_contract_address(), blobert_token_id); + free_games += 1; + blobert_token_index += 1; }; + + // define contract interfaces let lords_dispatcher: IERC20Dispatcher = IERC20Dispatcher { contract_address: tournament_config.lords }; @@ -407,14 +468,11 @@ pub mod tournament_component { // get current game cost let cost_to_play = ls_dispatcher.get_cost_to_play(); + let entries_cost = (entries.into() - free_games.into()) * cost_to_play.into(); // transfer base game cost lords_dispatcher - .transfer_from( - get_caller_address(), - get_contract_address(), - entries.into() * cost_to_play.into() - ); + .transfer_from(get_caller_address(), get_contract_address(), entries_cost); // transfer VRF cost let vrf_cost = self @@ -423,8 +481,7 @@ pub mod tournament_component { .transfer_from(get_caller_address(), get_contract_address(), vrf_cost.into()); // set the approvals according to entries - lords_dispatcher - .approve(tournament_config.loot_survivor, entries.into() * cost_to_play.into()); + lords_dispatcher.approve(tournament_config.loot_survivor, entries_cost); eth_dispatcher.approve(tournament_config.loot_survivor, vrf_cost.into()); let tournament = store.get_tournament(tournament_id); @@ -447,17 +504,50 @@ pub mod tournament_component { if entry_index == address_entries.entry_count { break; } - let game_id = ls_dispatcher - .new_game( - get_contract_address(), - 12, // wand - tournament.name, - 0, - true, - contract_address_const::<0>(), - 0, - address - ); + let mut game_id = 0; + let mut golden_token_index = 0; + let mut blobert_token_index = 0; + if (golden_token_index != golden_token_free_game_token_ids.len()) { + let token_id = *golden_token_free_game_token_ids.at(golden_token_index); + game_id = ls_dispatcher + .new_game( + client_reward_address, + 12, // wand + tournament.name, + (token_id.low).try_into().unwrap(), + true, + contract_address_const::<0>(), + 0, + address + ); + golden_token_index += 1; + } else if (blobert_token_index != blobert_free_game_token_ids.len()) { + let token_id = *blobert_free_game_token_ids.at(blobert_token_index); + game_id = ls_dispatcher + .new_game( + client_reward_address, + 12, // wand + tournament.name, + 0, + true, + contract_address_const::<0>(), + token_id.low, + address + ); + blobert_token_index += 1; + } else { + game_id = ls_dispatcher + .new_game( + client_reward_address, + 12, // wand + tournament.name, + 0, + true, + contract_address_const::<0>(), + 0, + address + ); + } game_ids.append(game_id.try_into().unwrap()); let game = TournamentGameModel { tournament_id, @@ -483,22 +573,55 @@ pub mod tournament_component { let mut start_index = store .get_tournament_starts(tournament_id, get_caller_address()) .start_count; + let mut game_id = 0; let mut game_ids = ArrayTrait::::new(); loop { if start_index == entries { break; } - let game_id = ls_dispatcher - .new_game( - get_contract_address(), - 12, // wand - tournament.name, - 0, - true, - contract_address_const::<0>(), - 0, - get_caller_address() - ); + let mut golden_token_index = 0; + let mut blobert_token_index = 0; + if (golden_token_index != golden_token_free_game_token_ids.len()) { + let token_id = *golden_token_free_game_token_ids.at(golden_token_index); + game_id = ls_dispatcher + .new_game( + client_reward_address, + 12, // wand + tournament.name, + (token_id.low).try_into().unwrap(), + true, + contract_address_const::<0>(), + 0, + get_caller_address() + ); + golden_token_index += 1; + } else if (blobert_token_index != blobert_free_game_token_ids.len()) { + let token_id = *blobert_free_game_token_ids.at(blobert_token_index); + game_id = ls_dispatcher + .new_game( + client_reward_address, + 12, // wand + tournament.name, + 0, + true, + contract_address_const::<0>(), + token_id.low, + get_caller_address() + ); + blobert_token_index += 1; + } else { + game_id = ls_dispatcher + .new_game( + client_reward_address, + 12, // wand + tournament.name, + 0, + true, + contract_address_const::<0>(), + 0, + get_caller_address() + ); + } game_ids.append(game_id.try_into().unwrap()); let game = TournamentGameModel { tournament_id, @@ -519,6 +642,36 @@ pub mod tournament_component { tournament_id, address: get_caller_address(), start_count: entries }; store.set_address_starts(@address_starts); + + // send the free game tokens back if (any were available to use) + + let mut golden_token_index = 0; + loop { + if golden_token_index == golden_token_free_game_token_ids.len() { + break; + } + IERC721Dispatcher { contract_address: tournament_config.golden_token } + .transfer_from( + get_contract_address(), + get_caller_address(), + *golden_token_free_game_token_ids.at(golden_token_index) + ); + golden_token_index += 1; + }; + + let mut blobert_token_index = 0; + loop { + if blobert_token_index == blobert_free_game_token_ids.len() { + break; + } + IERC721Dispatcher { contract_address: tournament_config.blobert } + .transfer_from( + get_contract_address(), + get_caller_address(), + *blobert_free_game_token_ids.at(blobert_token_index) + ); + blobert_token_index += 1; + }; } } @@ -538,6 +691,13 @@ pub mod tournament_component { // assert submission period is not over self._assert_tournament_not_settled(ref tournament); + let total_entries = store.get_total_entries(tournament_id); + + // handle formatiing of premium config into prize keys + if (!total_entries.premiums_formatted) { + self._format_premium_config_into_prize_keys(ref store, tournament_id); + } + let tournament_config = store.get_tournament_config(get_contract_address()); let mut ls_dispatcher = ILootSurvivorDispatcher { contract_address: tournament_config.loot_survivor @@ -644,14 +804,17 @@ pub mod tournament_component { +Drop > of InternalTrait { // - // INITIALIZE + // INITIALIZE COMPONENT // + fn initialize( self: @ComponentState, eth: ContractAddress, lords: ContractAddress, loot_survivor: ContractAddress, oracle: ContractAddress, + golden_token: ContractAddress, + blobert: ContractAddress, safe_mode: bool, test_mode: bool ) { @@ -659,6 +822,7 @@ pub mod tournament_component { self.get_contract().world_dispatcher(), @"tournament" ); let mut store: Store = StoreTrait::new(world); + // Store the config store .set_tournament_config( @TournamentConfig { @@ -667,12 +831,65 @@ pub mod tournament_component { lords, loot_survivor, oracle, + golden_token, + blobert, safe_mode, test_mode } ); } + // + // INITIALIZE TOKENS + // + + fn initialize_erc20( + self: @ComponentState, + token: ContractAddress, + name: ByteArray, + symbol: ByteArray, + ) { + let mut world = WorldTrait::storage( + self.get_contract().world_dispatcher(), @"tournament" + ); + let mut store: Store = StoreTrait::new(world); + assert(!self._is_token_registered(ref store, token), Errors::TOKEN_ALREADY_REGISTERED); + store + .set_token( + @TokenModel { + token: token, + name: name, + symbol: symbol, + token_data_type: TokenDataType::erc20(ERC20Data { token_amount: 1 }), + is_registered: true + } + ); + } + + + fn initialize_erc721( + self: @ComponentState, + token: ContractAddress, + name: ByteArray, + symbol: ByteArray + ) { + let mut world = WorldTrait::storage( + self.get_contract().world_dispatcher(), @"tournament" + ); + let mut store: Store = StoreTrait::new(world); + assert(!self._is_token_registered(ref store, token), Errors::TOKEN_ALREADY_REGISTERED); + store + .set_token( + @TokenModel { + token: token, + name: name, + symbol: symbol, + token_data_type: TokenDataType::erc721(ERC721Data { token_id: 1 }), + is_registered: true + } + ); + } + // // GETTERS // @@ -1002,6 +1219,37 @@ pub mod tournament_component { ); } + fn _assert_free_game_ids_not_larger_than_start_count( + self: @ComponentState, + start_count: Option, + golden_token_free_game_token_ids: Span, + blobert_free_game_token_ids: Span + ) { + match start_count { + Option::Some(start_count) => { + assert( + golden_token_free_game_token_ids.len() + + blobert_free_game_token_ids.len() <= start_count.try_into().unwrap(), + Errors::TOO_MANY_FREE_GAMES + ); + }, + Option::None => {}, + } + } + + fn _assert_free_game_ids_not_larger_than_entries( + self: @ComponentState, + entries: u64, + golden_token_free_game_token_ids: Span, + blobert_free_game_token_ids: Span + ) { + assert( + golden_token_free_game_token_ids.len() + + blobert_free_game_token_ids.len() <= entries.try_into().unwrap(), + Errors::TOO_MANY_FREE_GAMES + ); + } + fn _assert_scores_count_valid( self: @ComponentState, ref tournament: TournamentModel, @@ -1030,6 +1278,16 @@ pub mod tournament_component { assert(!claimed, Errors::PRIZE_ALREADY_CLAIMED); } + fn _assert_token_owner( + self: @ComponentState, + token: ContractAddress, + token_id: u256, + account: ContractAddress + ) { + let owner = self._get_owner(token, token_id); + assert(owner == account, Errors::NOT_TOKEN_OWNER); + } + fn _assert_gated_token_owner( self: @ComponentState, token: ContractAddress, @@ -1301,105 +1559,113 @@ pub mod tournament_component { store.set_tournament_totals(@tournament_totals); new_tournament_id } - fn _register_tokens( - ref self: ComponentState, ref store: Store, tokens: Array - ) { - let num_tokens = tokens.len(); - let mut token_index = 0; - let safe_mode = store.get_tournament_config(get_contract_address()).safe_mode; - loop { - if token_index == num_tokens { - break; - } - let token = *tokens.at(token_index); - assert( - !self._is_token_registered(ref store, token.token), - Errors::TOKEN_ALREADY_REGISTERED - ); - - if (safe_mode) { - assert( - token.token == ETHEREUM_ADDRESS() - || token.token == LORDS_ADDRESS() - || token.token == SURVIVORS_ADDRESS(), - Errors::INVALID_TOKEN_FOR_SAFE_MODE - ); - } - - let mut name = ""; - let mut symbol = ""; - - match token.token_data_type.into() { - TokenDataType::erc20(_) => { - let token_dispatcher = IERC20Dispatcher { contract_address: token.token }; - let token_dispatcher_metadata = IERC20MetadataDispatcher { - contract_address: token.token - }; - name = token_dispatcher_metadata.name(); - symbol = token_dispatcher_metadata.symbol(); - // check that the contract is approved for the minimal amount - let allowance = token_dispatcher - .allowance(get_caller_address(), get_contract_address()); - assert(allowance == 1, Errors::INVALID_TOKEN_ALLOWANCES); - // take a reading of the current balance (incase contract has assets - // already) - let current_balance = token_dispatcher.balance_of(get_contract_address()); - // trnsfer a minimal amount to the contract - token_dispatcher - .transfer_from(get_caller_address(), get_contract_address(), 1); - // take a reading of the new balance - let new_balance = token_dispatcher.balance_of(get_contract_address()); - assert(new_balance == current_balance + 1, Errors::INVALID_TOKEN_BALANCES); - // transfer back the minimal amount - token_dispatcher.transfer(get_caller_address(), 1); - // check the total supply is legitimate - let total_supply = token_dispatcher.total_supply(); - assert(total_supply < TWO_POW_128.into(), Errors::TOKEN_SUPPLY_TOO_LARGE); - }, - TokenDataType::erc721(token_data_type) => { - let token_dispatcher = IERC721Dispatcher { contract_address: token.token }; - let token_dispatcher_metadata = IERC721MetadataDispatcher { - contract_address: token.token - }; - name = token_dispatcher_metadata.name(); - symbol = token_dispatcher_metadata.symbol(); - // check that the contract is approved for the specific id - let approved = token_dispatcher - .get_approved(token_data_type.token_id.into()); - assert(approved == get_contract_address(), Errors::INVALID_TOKEN_APPROVALS); - // transfer a specific id to the contract - token_dispatcher - .transfer_from( - get_caller_address(), - get_contract_address(), - token_data_type.token_id.into() - ); - // check the balance of the contract - let balance = token_dispatcher.balance_of(get_contract_address()); - assert(balance == 1, Errors::INVALID_TOKEN_BALANCES); - let owner = token_dispatcher.owner_of(token_data_type.token_id.into()); - assert(owner == get_contract_address(), Errors::INVALID_TOKEN_OWNER); - // transfer back the token - token_dispatcher - .transfer_from( - get_contract_address(), - get_caller_address(), - token_data_type.token_id.into() - ); - }, - } - let token_model = TokenModel { - token: token.token, - name, - symbol, - token_data_type: token.token_data_type, - is_registered: true - }; - store.set_token(@token_model); - token_index += 1; - } - } + // TODO: add for V2 (only ERC721 tokens) + // fn _register_tokens( + // ref self: ComponentState, ref store: Store, tokens: Array + // ) { + // let num_tokens = tokens.len(); + // let mut token_index = 0; + // let safe_mode = store.get_tournament_config(get_contract_address()).safe_mode; + // loop { + // if token_index == num_tokens { + // break; + // } + // let token = *tokens.at(token_index); + + // assert( + // !self._is_token_registered(ref store, token.token), + // Errors::TOKEN_ALREADY_REGISTERED + // ); + + // if (safe_mode) { + // assert( + // token.token == ETHEREUM_ADDRESS() + // || token.token == LORDS_ADDRESS() + // || token.token == SURVIVORS_ADDRESS(), + // Errors::INVALID_TOKEN_FOR_SAFE_MODE + // ); + // } + + // let mut name = ""; + // let mut symbol = ""; + + // match token.token_data_type.into() { + // TokenDataType::erc20(_) => { + // let token_dispatcher = IERC20Dispatcher { contract_address: token.token + // }; + // let token_dispatcher_metadata = IERC20MetadataDispatcher { + // contract_address: token.token + // }; + // name = token_dispatcher_metadata.name(); + // symbol = token_dispatcher_metadata.symbol(); + // // check that the contract is approved for the minimal amount + // let allowance = token_dispatcher + // .allowance(get_caller_address(), get_contract_address()); + // assert(allowance == 1, Errors::INVALID_TOKEN_ALLOWANCES); + // // take a reading of the current balance (incase contract has assets + // // already) + // let current_balance = + // token_dispatcher.balance_of(get_contract_address()); + // // trnsfer a minimal amount to the contract + // token_dispatcher + // .transfer_from(get_caller_address(), get_contract_address(), 1); + // // take a reading of the new balance + // let new_balance = token_dispatcher.balance_of(get_contract_address()); + // assert(new_balance == current_balance + 1, + // Errors::INVALID_TOKEN_BALANCES); + // // transfer back the minimal amount + // token_dispatcher.transfer(get_caller_address(), 1); + // // check the total supply is legitimate + // let total_supply = token_dispatcher.total_supply(); + // assert(total_supply < TWO_POW_128.into(), + // Errors::TOKEN_SUPPLY_TOO_LARGE); + // }, + // TokenDataType::erc721(token_data_type) => { + // let token_dispatcher = IERC721Dispatcher { contract_address: token.token + // }; + // let token_dispatcher_metadata = IERC721MetadataDispatcher { + // contract_address: token.token + // }; + // name = token_dispatcher_metadata.name(); + // symbol = token_dispatcher_metadata.symbol(); + // // check that the contract is approved for the specific id + // let approved = token_dispatcher + // .get_approved(token_data_type.token_id.into()); + // assert(approved == get_contract_address(), + // Errors::INVALID_TOKEN_APPROVALS); + // // transfer a specific id to the contract + // token_dispatcher + // .transfer_from( + // get_caller_address(), + // get_contract_address(), + // token_data_type.token_id.into() + // ); + // // check the balance of the contract + // let balance = token_dispatcher.balance_of(get_contract_address()); + // assert(balance == 1, Errors::INVALID_TOKEN_BALANCES); + // let owner = token_dispatcher.owner_of(token_data_type.token_id.into()); + // assert(owner == get_contract_address(), Errors::INVALID_TOKEN_OWNER); + // // transfer back the token + // token_dispatcher + // .transfer_from( + // get_contract_address(), + // get_caller_address(), + // token_data_type.token_id.into() + // ); + // }, + // } + // let token_model = TokenModel { + // token: token.token, + // name, + // symbol, + // token_data_type: token.token_data_type, + // is_registered: true + // }; + // store.set_token(@token_model); + // token_index += 1; + // } + // } fn _format_premium_config_into_prize_keys( ref self: ComponentState, ref store: Store, tournament_id: u64 @@ -1576,6 +1842,10 @@ pub mod tournament_component { }, TokenDataType::erc721(token_data) => { let token_dispatcher = IERC721Dispatcher { contract_address: token }; + self + ._assert_token_owner( + token, token_data.token_id.into(), get_caller_address() + ); token_dispatcher .transfer_from( get_caller_address(), get_contract_address(), token_data.token_id.into() diff --git a/contracts/src/presets/ls_tournament.cairo b/contracts/src/presets/ls_tournament.cairo index ca616eb..06e69b6 100644 --- a/contracts/src/presets/ls_tournament.cairo +++ b/contracts/src/presets/ls_tournament.cairo @@ -1,7 +1,7 @@ use starknet::ContractAddress; use dojo::world::IWorldDispatcher; use tournament::ls15_components::models::tournament::{ - TournamentModel, Token, Premium, TokenDataType, GatedType, GatedSubmissionType + TournamentModel, Premium, TokenDataType, GatedType, GatedSubmissionType }; #[starknet::interface] @@ -15,6 +15,8 @@ pub trait ILSTournament { fn tournament_prize_keys(self: @TState, tournament_id: u64) -> Array; fn top_scores(self: @TState, tournament_id: u64) -> Array; fn is_token_registered(self: @TState, token: ContractAddress) -> bool; + // TODO: add for V2 (only ERC721 tokens) + // fn register_tokens(ref self: TState, tokens: Array); fn create_tournament( ref self: TState, name: felt252, @@ -26,7 +28,6 @@ pub trait ILSTournament { gated_type: Option, entry_premium: Option, ) -> u64; - fn register_tokens(ref self: TState, tokens: Array); fn enter_tournament( ref self: TState, tournament_id: u64, gated_submission_type: Option ); @@ -46,7 +47,7 @@ pub trait ILSTournament { #[dojo::contract] pub mod LSTournament { - use starknet::ContractAddress; + use starknet::{contract_address_const}; use tournament::ls15_components::tournament::tournament_component; component!(path: tournament_component, storage: tournament, event: TournamentEvent); @@ -68,24 +69,111 @@ pub mod LSTournament { TournamentEvent: tournament_component::Event, } - fn dojo_init( - ref self: ContractState, - eth_address: ContractAddress, - lords_address: ContractAddress, - loot_survivor_address: ContractAddress, - oracle_address: ContractAddress, - safe_mode: bool, - test_mode: bool - ) { + fn dojo_init(ref self: ContractState, safe_mode: bool, test_mode: bool,) { self .tournament .initialize( - eth_address, - lords_address, - loot_survivor_address, - oracle_address, + contract_address_const::< + 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 + >(), // eth + contract_address_const::< + 0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49 + >(), // lords + contract_address_const::< + 0x018108b32cea514a78ef1b0e4a0753e855cdf620bc0565202c02456f618c4dc4 + >(), // loot survivor + contract_address_const::< + 0x2a85bd616f912537c50a49a4076db02c00b29b2cdc8a197ce92ed1837fa875b + >(), // oracle + contract_address_const::< + 0x04f5e296c805126637552cf3930e857f380e7c078e8f00696de4fc8545356b1d + >(), // golden token + contract_address_const::< + 0x00539f522b29ae9251dbf7443c7a950cf260372e69efab3710a11bf17a9599f1 + >(), // blobert safe_mode, test_mode ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 + >(), + "Ether", + "ETH" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49 + >(), + "Lords", + "LORDS" + ); + self + .tournament + .initialize_erc721( + contract_address_const::< + 0x018108b32cea514a78ef1b0e4a0753e855cdf620bc0565202c02456f618c4dc4 + >(), + "Loot Survivor", + "LSVR" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d + >(), + "Starknet Token", + "STRK" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8 + >(), + "USD Coin", + "USDC" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x4878d1148318a31829523ee9c6a5ee563af6cd87f90a30809e5b0d27db8a9b + >(), + "Standard Weighted Adalian Yield", + "SWAY" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x410466536b5ae074f7fea81e5533b8134a9fa08b3dd077dd9db08f64997d113 + >(), + "Paper", + "PAPER" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x75afe6402ad5a5c20dd25e10ec3b3986acaa647b77e4ae24b0cbc9a54a27a87 + >(), + "Ekubo Protocol", + "EKUBO" + ); + self + .tournament + .initialize_erc20( + contract_address_const::< + 0x3b405a98c9e795d427fe82cdeeeed803f221b52471e3a757574a2b4180793ee + >(), + "STARKNET BROTHER", + "BROTHER" + ); } } diff --git a/tournament-ui/src/App.tsx b/tournament-ui/src/App.tsx index 465667e..2c910f3 100644 --- a/tournament-ui/src/App.tsx +++ b/tournament-ui/src/App.tsx @@ -19,22 +19,19 @@ import { useSubscribeTournamentCountsQuery, } from "@/hooks/useSdkQueries"; import { useSystemCalls } from "@/useSystemCalls"; -import { useDojo } from "@/DojoContext"; import { Toaster } from "@/components/ui/toaster"; import { useTournamentContracts } from "@/hooks/useTournamentContracts"; import { useConfig } from "@/hooks/useConfig"; function App() { - const { - setup: { selectedChainConfig }, - } = useDojo(); const { account } = useAccount(); useConfig(); const { tournament, eth, lords } = useTournamentContracts(); const { getERC20BalanceGeneral } = useSystemCalls(); const [tokenBalance, setTokenBalance] = useState>({}); - const isMainnet = selectedChainConfig.chainId === "SN_MAINNET"; + // const isMainnet = selectedChainConfig.chainId === "SN_MAINNET"; + const isMainnet = false; // Getters useGetTournamentCountsQuery(tournament); diff --git a/tournament-ui/src/containers/RegisterToken.tsx b/tournament-ui/src/containers/RegisterToken.tsx index 9739b18..6a9fe5d 100644 --- a/tournament-ui/src/containers/RegisterToken.tsx +++ b/tournament-ui/src/containers/RegisterToken.tsx @@ -19,7 +19,7 @@ import { useTournamentContracts } from "@/hooks/useTournamentContracts"; const RegisterToken = () => { const { account } = useAccount(); - const { tournament, eth, lords } = useTournamentContracts(); + const { eth, lords } = useTournamentContracts(); const erc20_mock = useDojoSystem("erc20_mock").contractAddress ?? "0x0"; const erc721_mock = useDojoSystem("erc721_mock").contractAddress ?? "0x0"; const [tokenType, setTokenType] = useState(null); @@ -38,10 +38,8 @@ const RegisterToken = () => { mintErc20, mintErc721, getERC20Balance, - approveErc20, - approveErc721, - approveEth, - approveLords, + approveERC20General, + approveERC721General, mintEth, mintLords, getEthBalance, @@ -119,13 +117,13 @@ const RegisterToken = () => { const handleRegisterToken = async () => { if (tokenType !== null) { if (tokenType === TokenDataEnum.erc20) { - if (tokenAddress === padAddress(eth)) { - await approveEth(tournament, 1n, 0n); - } else if (tokenAddress === padAddress(lords)) { - await approveLords(tournament, 1n, 0n); - } else { - await approveErc20(tournament, 1n, 0n); - } + const tokenDataType = new CairoCustomEnum({ + erc20: { + token_amount: 1, + }, + erc721: undefined, + }) as TokenDataTypeEnum; + await approveERC20General({ token: tokenAddress, tokenDataType }); await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait for 5 second await registerTokens([ { @@ -139,7 +137,13 @@ const RegisterToken = () => { }, ]); } else { - await approveErc721(tournament, BigInt(tokenId), 0n); + const tokenDataType = new CairoCustomEnum({ + erc20: undefined, + erc721: { + token_id: tokenId, + }, + }) as TokenDataTypeEnum; + await approveERC721General({ token: tokenAddress, tokenDataType }); await registerTokens([ { token: tokenAddress, diff --git a/tournament-ui/src/generated/contracts.gen.ts b/tournament-ui/src/generated/contracts.gen.ts index 3bb2de3..d063c06 100644 --- a/tournament-ui/src/generated/contracts.gen.ts +++ b/tournament-ui/src/generated/contracts.gen.ts @@ -1390,13 +1390,14 @@ export async function setupWorld(provider: DojoProvider) { snAccount: Account | AccountInterface, tokens: Array ) => { + console.log(tokens); try { return await provider.execute( snAccount, { contractName: "LSTournament", entrypoint: "register_tokens", - calldata: [tokens], + calldata: CallData.compile([tokens]), }, "tournament" ); diff --git a/tournament-ui/src/useSystemCalls.ts b/tournament-ui/src/useSystemCalls.ts index 8faa041..536fdd5 100644 --- a/tournament-ui/src/useSystemCalls.ts +++ b/tournament-ui/src/useSystemCalls.ts @@ -16,7 +16,6 @@ import { byteArray, CallData, } from "starknet"; -import { useDojoSystem } from "@/hooks/useDojoSystem"; import { useToast } from "@/hooks/useToast"; import useUIStore from "@/hooks/useUIStore"; import { useOptimisticUpdates } from "@/hooks/useOptimisticUpdates"; @@ -29,7 +28,6 @@ export function selectTournament(client: any, isMainnet: boolean): any { export const useSystemCalls = () => { const state = useDojoStore((state) => state); - const tournament_mock = useDojoSystem("tournament_mock"); const { setup: { client, selectedChainConfig }, @@ -49,9 +47,7 @@ export const useSystemCalls = () => { // Tournament const registerTokens = async (tokens: Token[]) => { - const entityId = getEntityIdFromKeys([ - BigInt(tournament_mock.contractAddress), - ]); + const entityId = getEntityIdFromKeys([BigInt(tournament)]); const transactionId = uuidv4();