diff --git a/launchpad-guaranteed-tickets/src/guaranteed_tickets_init.rs b/launchpad-guaranteed-tickets/src/guaranteed_tickets_init.rs index 805acf5..fcc276d 100644 --- a/launchpad-guaranteed-tickets/src/guaranteed_tickets_init.rs +++ b/launchpad-guaranteed-tickets/src/guaranteed_tickets_init.rs @@ -1,4 +1,23 @@ multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub const STAKING_GUARANTEED_TICKETS_NO: usize = 1; +pub const MIGRATION_GUARANTEED_TICKETS_NO: usize = 1; + +#[derive(NestedEncode, NestedDecode, TopEncode, TopDecode, PartialEq, Eq, TypeAbi, Clone)] +pub struct UserGuaranteedTickets { + pub address: ManagedAddress, + pub guaranteed_tickets: usize, +} + +impl UserGuaranteedTickets { + pub fn new(address: ManagedAddress, guaranteed_tickets: usize) -> Self { + Self { + address, + guaranteed_tickets, + } + } +} #[multiversx_sc::module] pub trait GuaranteedTicketsInitModule: @@ -16,10 +35,12 @@ pub trait GuaranteedTicketsInitModule: let min_confirmed_for_guaranteed_ticket = self.min_confirmed_for_guaranteed_ticket().get(); let mut guranteed_ticket_whitelist = self.users_with_guaranteed_ticket(); let mut total_winning_tickets = self.nr_winning_tickets().get(); + let mut total_guaranteed_tickets = self.total_guaranteed_tickets().get(); for multi_arg in address_number_pairs { let (buyer, nr_tickets) = multi_arg.into_tuple(); self.try_create_tickets(buyer.clone(), nr_tickets); + self.user_total_allocated_tickets(&buyer).set(nr_tickets); if nr_tickets >= min_confirmed_for_guaranteed_ticket { require!( @@ -27,12 +48,56 @@ pub trait GuaranteedTicketsInitModule: "Too many users with guaranteed ticket" ); - let _ = guranteed_ticket_whitelist.insert(buyer); - total_winning_tickets -= 1; + let user_guaranteed_tickets = + UserGuaranteedTickets::new(buyer, STAKING_GUARANTEED_TICKETS_NO); + let _ = guranteed_ticket_whitelist.insert(user_guaranteed_tickets); + total_winning_tickets -= STAKING_GUARANTEED_TICKETS_NO; + total_guaranteed_tickets += STAKING_GUARANTEED_TICKETS_NO; } } self.nr_winning_tickets().set(total_winning_tickets); + self.total_guaranteed_tickets() + .set(total_guaranteed_tickets); + } + + fn add_more_guaranteed_tickets(&self, addresses: MultiValueEncoded) { + self.require_add_tickets_period(); + + let mut guranteed_ticket_whitelist = self.users_with_guaranteed_ticket(); + let mut total_winning_tickets = self.nr_winning_tickets().get(); + let mut total_guaranteed_tickets = self.total_guaranteed_tickets().get(); + + for user in addresses { + let mut user_new_guaranteed_tickets = MIGRATION_GUARANTEED_TICKETS_NO; + let user_initial_guaranteed_tickets = + UserGuaranteedTickets::new(user.clone(), STAKING_GUARANTEED_TICKETS_NO); + if guranteed_ticket_whitelist.swap_remove(&user_initial_guaranteed_tickets) { + user_new_guaranteed_tickets += 1; + } + let user_ticket_range = self.ticket_range_for_address(&user).get(); + let user_total_tickets_no = user_ticket_range.last_id - user_ticket_range.first_id + 1; + + require!( + total_winning_tickets > 0, + "Too many users with guaranteed ticket" + ); + require!( + user_total_tickets_no >= user_new_guaranteed_tickets, + "The guaranteed tickets number is bigger than the user's total tickets" + ); + + let new_user_guaranteed_tickets = + UserGuaranteedTickets::new(user, user_new_guaranteed_tickets); + + let _ = guranteed_ticket_whitelist.insert(new_user_guaranteed_tickets); + total_winning_tickets -= MIGRATION_GUARANTEED_TICKETS_NO; + total_guaranteed_tickets += MIGRATION_GUARANTEED_TICKETS_NO; + } + + self.nr_winning_tickets().set(total_winning_tickets); + self.total_guaranteed_tickets() + .set(total_guaranteed_tickets); } fn clear_users_with_guaranteed_ticket_after_blacklist( @@ -40,23 +105,65 @@ pub trait GuaranteedTicketsInitModule: users: &ManagedVec, ) { let mut whitelist = self.users_with_guaranteed_ticket(); - let mut nr_users_removed = 0; + let mut nr_tickets_removed = 0; for user in users { - let was_whitelisted = whitelist.swap_remove(&user); - if was_whitelisted { - nr_users_removed += 1; + let user_staking_guaranteed_tickets = + UserGuaranteedTickets::new(user.clone(), STAKING_GUARANTEED_TICKETS_NO); + let user_migration_guaranteed_tickets = UserGuaranteedTickets::new( + user, + STAKING_GUARANTEED_TICKETS_NO + MIGRATION_GUARANTEED_TICKETS_NO, + ); + if whitelist.swap_remove(&user_staking_guaranteed_tickets) { + nr_tickets_removed += user_staking_guaranteed_tickets.guaranteed_tickets; + } + if whitelist.swap_remove(&user_migration_guaranteed_tickets) { + nr_tickets_removed += user_migration_guaranteed_tickets.guaranteed_tickets; } } - if nr_users_removed > 0 { + if nr_tickets_removed > 0 { self.nr_winning_tickets() - .update(|nr_winning| *nr_winning += nr_users_removed); + .update(|nr_winning| *nr_winning += nr_tickets_removed); } } + fn get_user_guaranteed_tickets_no(&self, address: ManagedAddress) -> usize { + let mut user_guaranteed_tickets = UserGuaranteedTickets { + address, + guaranteed_tickets: STAKING_GUARANTEED_TICKETS_NO, + }; + let mut user_guaranteed_tickets_no = 0; + if self + .users_with_guaranteed_ticket() + .contains(&user_guaranteed_tickets) + { + user_guaranteed_tickets_no = user_guaranteed_tickets.guaranteed_tickets; + } + user_guaranteed_tickets.guaranteed_tickets = + STAKING_GUARANTEED_TICKETS_NO + MIGRATION_GUARANTEED_TICKETS_NO; + if self + .users_with_guaranteed_ticket() + .contains(&user_guaranteed_tickets) + { + require!( + user_guaranteed_tickets_no == 0, + "Multiple guaranteed tickets entries for the user" + ); + user_guaranteed_tickets_no = user_guaranteed_tickets.guaranteed_tickets; + } + + user_guaranteed_tickets_no + } + #[storage_mapper("minConfirmedForGuaranteedTicket")] fn min_confirmed_for_guaranteed_ticket(&self) -> SingleValueMapper; #[storage_mapper("usersWithGuaranteedTicket")] - fn users_with_guaranteed_ticket(&self) -> UnorderedSetMapper; + fn users_with_guaranteed_ticket(&self) -> UnorderedSetMapper>; + + #[storage_mapper("userTotalAllocatedTickets")] + fn user_total_allocated_tickets(&self, address: &ManagedAddress) -> SingleValueMapper; + + #[storage_mapper("totalGuaranteedTickets")] + fn total_guaranteed_tickets(&self) -> SingleValueMapper; } diff --git a/launchpad-guaranteed-tickets/src/guranteed_ticket_winners.rs b/launchpad-guaranteed-tickets/src/guranteed_ticket_winners.rs index 21268f2..a054d2e 100644 --- a/launchpad-guaranteed-tickets/src/guranteed_ticket_winners.rs +++ b/launchpad-guaranteed-tickets/src/guranteed_ticket_winners.rs @@ -46,7 +46,6 @@ pub trait GuaranteedTicketWinnersModule: &self, op: &mut GuaranteedTicketsSelectionOperation, ) -> OperationCompletionStatus { - let min_confirmed_for_guaranteed_ticket = self.min_confirmed_for_guaranteed_ticket().get(); let mut users_whitelist = self.users_with_guaranteed_ticket(); let mut users_left = users_whitelist.len(); @@ -55,23 +54,42 @@ pub trait GuaranteedTicketWinnersModule: return STOP_OP; } - let current_user = users_whitelist.get_by_index(VEC_MAPPER_START_INDEX); - let _ = users_whitelist.swap_remove(¤t_user); + let user_guaranteed_tickets = users_whitelist.get_by_index(VEC_MAPPER_START_INDEX); + let _ = users_whitelist.swap_remove(&user_guaranteed_tickets); users_left -= 1; - let user_confirmed_tickets = self.nr_confirmed_tickets(¤t_user).get(); - if user_confirmed_tickets >= min_confirmed_for_guaranteed_ticket { - let ticket_range = self.ticket_range_for_address(¤t_user).get(); - if !self.has_any_winning_tickets(&ticket_range) { - self.ticket_status(ticket_range.first_id) - .set(WINNING_TICKET); - - op.total_additional_winning_tickets += 1; - } else { - op.leftover_tickets += 1; + let user_confirmed_tickets = self + .nr_confirmed_tickets(&user_guaranteed_tickets.address) + .get(); + let user_total_allocated_tickets_no = self + .user_total_allocated_tickets(&user_guaranteed_tickets.address) + .get(); + if user_confirmed_tickets == user_total_allocated_tickets_no { + let ticket_range = self + .ticket_range_for_address(&user_guaranteed_tickets.address) + .get(); + // We keep this function to determine in advance the number of winning tickets the user has + let mut remaining_winning_tickets = self.remaining_user_winning_tickets_no( + &ticket_range, + user_guaranteed_tickets.guaranteed_tickets, + ); + op.leftover_tickets += + user_guaranteed_tickets.guaranteed_tickets - remaining_winning_tickets; + if remaining_winning_tickets > 0 { + for ticket_id in ticket_range.first_id..=ticket_range.last_id { + if remaining_winning_tickets == 0 { + break; + } + let ticket_status = self.ticket_status(ticket_id).get(); + if ticket_status != WINNING_TICKET { + self.ticket_status(ticket_id).set(WINNING_TICKET); + op.total_additional_winning_tickets += 1; + remaining_winning_tickets -= 1; + } + } } } else { - op.leftover_tickets += 1; + op.leftover_tickets += user_guaranteed_tickets.guaranteed_tickets; } CONTINUE_OP @@ -110,15 +128,23 @@ pub trait GuaranteedTicketWinnersModule: }) } - fn has_any_winning_tickets(&self, ticket_range: &TicketRange) -> bool { + fn remaining_user_winning_tickets_no( + &self, + ticket_range: &TicketRange, + guaranteed_tickets: usize, + ) -> usize { + let mut remaining_winning_tickets = guaranteed_tickets; for ticket_id in ticket_range.first_id..=ticket_range.last_id { + if remaining_winning_tickets == 0 { + return 0; + } let ticket_status = self.ticket_status(ticket_id).get(); if ticket_status == WINNING_TICKET { - return true; + remaining_winning_tickets -= 1; } } - false + remaining_winning_tickets } fn try_select_winning_ticket( diff --git a/launchpad-guaranteed-tickets/src/lib.rs b/launchpad-guaranteed-tickets/src/lib.rs index 81d7f52..2395616 100644 --- a/launchpad-guaranteed-tickets/src/lib.rs +++ b/launchpad-guaranteed-tickets/src/lib.rs @@ -10,6 +10,8 @@ use crate::guranteed_ticket_winners::GuaranteedTicketsSelectionOperation; pub mod guaranteed_tickets_init; pub mod guranteed_ticket_winners; +pub type UserTicketsStatus = MultiValue3; + #[multiversx_sc::contract] pub trait LaunchpadGuaranteedTickets: launchpad_common::LaunchpadMain @@ -69,12 +71,18 @@ pub trait LaunchpadGuaranteedTickets: self.add_tickets_with_guaranteed_winners(address_number_pairs); } + #[only_owner] + #[endpoint(addMoreGuaranteedTickets)] + fn add_more_guaranteed_tickets_endpoint(&self, addresses: MultiValueEncoded) { + self.add_more_guaranteed_tickets(addresses); + } + #[only_owner] #[payable("*")] #[endpoint(depositLaunchpadTokens)] fn deposit_launchpad_tokens_endpoint(&self) { let base_selection_winning_tickets = self.nr_winning_tickets().get(); - let reserved_tickets = self.users_with_guaranteed_ticket().len(); + let reserved_tickets = self.total_guaranteed_tickets().get(); let total_tickets = base_selection_winning_tickets + reserved_tickets; self.deposit_launchpad_tokens(total_tickets); @@ -145,4 +153,18 @@ pub trait LaunchpadGuaranteedTickets: fn claim_ticket_payment_endpoint(&self) { self.claim_ticket_payment(); } + + #[view(getUserTicketsStatus)] + fn user_tickets_status(&self, address: ManagedAddress) -> UserTicketsStatus { + let user_total_allocated_tickets_no = self.user_total_allocated_tickets(&address).get(); + let user_confirmed_tickets_no = self.nr_confirmed_tickets(&address).get(); + let user_guaranteed_tickets_no = self.get_user_guaranteed_tickets_no(address); + + ( + user_total_allocated_tickets_no, + user_confirmed_tickets_no, + user_guaranteed_tickets_no, + ) + .into() + } } diff --git a/launchpad-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs b/launchpad-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs index b68aa63..b206a07 100644 --- a/launchpad-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs +++ b/launchpad-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs @@ -10,7 +10,10 @@ use launchpad_common::{ winner_selection::WinnerSelectionModule, }; use launchpad_guaranteed_tickets::{ - guaranteed_tickets_init::GuaranteedTicketsInitModule, LaunchpadGuaranteedTickets, + guaranteed_tickets_init::{ + GuaranteedTicketsInitModule, UserGuaranteedTickets, STAKING_GUARANTEED_TICKETS_NO, + }, + LaunchpadGuaranteedTickets, }; use multiversx_sc_scenario::{ managed_address, managed_biguint, managed_token_id, rust_biguint, @@ -44,11 +47,11 @@ impl LaunchpadSetup where LaunchpadBuilder: 'static + Copy + Fn() -> launchpad_guaranteed_tickets::ContractObj, { - pub fn new(lp_builder: LaunchpadBuilder) -> Self { + pub fn new(nr_winning_tickets: usize, lp_builder: LaunchpadBuilder) -> Self { let rust_zero = rust_biguint!(0u64); let user_balance = rust_biguint!(TICKET_COST * MAX_TIER_TICKETS as u64); let total_launchpad_tokens = - rust_biguint!(LAUNCHPAD_TOKENS_PER_TICKET * NR_WINNING_TICKETS as u64); + rust_biguint!(LAUNCHPAD_TOKENS_PER_TICKET * nr_winning_tickets as u64); let mut b_mock = BlockchainStateWrapper::new(); let owner_address = b_mock.create_user_account(&rust_zero); @@ -76,7 +79,7 @@ where managed_biguint!(LAUNCHPAD_TOKENS_PER_TICKET), EgldOrEsdtTokenIdentifier::egld(), managed_biguint!(TICKET_COST), - NR_WINNING_TICKETS, + nr_winning_tickets, CONFIRM_START_BLOCK, WINNER_SELECTION_START_BLOCK, CLAIM_START_BLOCK, @@ -97,11 +100,15 @@ where sc.add_tickets_endpoint(args); // 1 ticket for the max tier gets removed - assert_eq!(sc.nr_winning_tickets().get(), NR_WINNING_TICKETS - 1); + assert_eq!(sc.nr_winning_tickets().get(), nr_winning_tickets - 1); assert_eq!(sc.users_with_guaranteed_ticket().len(), 1); + let user_guaranteed_tickets = UserGuaranteedTickets::new( + managed_address!(participants.last().unwrap()), + STAKING_GUARANTEED_TICKETS_NO, + ); assert!(sc .users_with_guaranteed_ticket() - .contains(&managed_address!(participants.last().unwrap()))); + .contains(&user_guaranteed_tickets)); }) .assert_ok(); @@ -152,13 +159,17 @@ where ) } - pub fn select_base_winners_mock(&mut self, nr_whales: usize) -> TxResult { + pub fn select_base_winners_mock( + &mut self, + nr_winning_tickets: usize, + guaranteed_tickets: usize, + ) -> TxResult { self.b_mock.execute_tx( &self.owner_address, &self.lp_wrapper, &rust_biguint!(0), |sc| { - let base_winning = NR_WINNING_TICKETS - nr_whales; + let base_winning = nr_winning_tickets - guaranteed_tickets; for ticket_id in 1..=base_winning { sc.ticket_status(ticket_id).set(WINNING_TICKET); } diff --git a/launchpad-guaranteed-tickets/tests/guaranteed_tickets_test.rs b/launchpad-guaranteed-tickets/tests/guaranteed_tickets_test.rs index 83d7f5b..8545742 100644 --- a/launchpad-guaranteed-tickets/tests/guaranteed_tickets_test.rs +++ b/launchpad-guaranteed-tickets/tests/guaranteed_tickets_test.rs @@ -25,12 +25,18 @@ use crate::guaranteed_tickets_setup::NR_WINNING_TICKETS; #[test] fn init_test() { - let _ = LaunchpadSetup::new(launchpad_guaranteed_tickets::contract_obj); + let _ = LaunchpadSetup::new( + NR_WINNING_TICKETS, + launchpad_guaranteed_tickets::contract_obj, + ); } #[test] fn confirm_all_test() { - let mut lp_setup = LaunchpadSetup::new(launchpad_guaranteed_tickets::contract_obj); + let mut lp_setup = LaunchpadSetup::new( + NR_WINNING_TICKETS, + launchpad_guaranteed_tickets::contract_obj, + ); let participants = lp_setup.participants.clone(); for (i, p) in participants.iter().enumerate() { @@ -42,7 +48,9 @@ fn confirm_all_test() { .set_block_nonce(WINNER_SELECTION_START_BLOCK); lp_setup.filter_tickets().assert_ok(); - lp_setup.select_base_winners_mock(1).assert_ok(); + lp_setup + .select_base_winners_mock(NR_WINNING_TICKETS, 1) + .assert_ok(); lp_setup .b_mock @@ -143,7 +151,10 @@ fn confirm_all_test() { #[test] fn redistribute_test() { - let mut lp_setup = LaunchpadSetup::new(launchpad_guaranteed_tickets::contract_obj); + let mut lp_setup = LaunchpadSetup::new( + NR_WINNING_TICKETS, + launchpad_guaranteed_tickets::contract_obj, + ); let participants = lp_setup.participants.clone(); lp_setup.confirm(&participants[0], 1).assert_ok(); @@ -155,7 +166,9 @@ fn redistribute_test() { .set_block_nonce(WINNER_SELECTION_START_BLOCK); lp_setup.filter_tickets().assert_ok(); - lp_setup.select_base_winners_mock(1).assert_ok(); + lp_setup + .select_base_winners_mock(NR_WINNING_TICKETS, 1) + .assert_ok(); lp_setup .b_mock @@ -217,7 +230,10 @@ fn redistribute_test() { #[test] fn combined_scenario_test() { - let mut lp_setup = LaunchpadSetup::new(launchpad_guaranteed_tickets::contract_obj); + let mut lp_setup = LaunchpadSetup::new( + NR_WINNING_TICKETS, + launchpad_guaranteed_tickets::contract_obj, + ); let mut participants = lp_setup.participants.clone(); let new_participant = lp_setup @@ -260,7 +276,9 @@ fn combined_scenario_test() { .set_block_nonce(WINNER_SELECTION_START_BLOCK); lp_setup.filter_tickets().assert_ok(); - lp_setup.select_base_winners_mock(2).assert_ok(); + lp_setup + .select_base_winners_mock(NR_WINNING_TICKETS, 2) + .assert_ok(); lp_setup .b_mock @@ -325,3 +343,148 @@ fn combined_scenario_test() { ) .assert_ok(); } + +#[test] +fn add_more_guaranteed_tickets_scenario_test() { + let nr_random_tickets = 1; + let nr_staking_guaranteed_tickets = 2; + let nr_migration_guaranteed_tickets = 2; + let nr_winning_tickets = + nr_random_tickets + nr_staking_guaranteed_tickets + nr_migration_guaranteed_tickets; + let mut lp_setup = LaunchpadSetup::new( + nr_winning_tickets, + launchpad_guaranteed_tickets::contract_obj, + ); + let mut participants = lp_setup.participants.clone(); + + let new_participant = lp_setup + .b_mock + .create_user_account(&rust_biguint!(TICKET_COST * MAX_TIER_TICKETS as u64)); + participants.push(new_participant.clone()); + + let second_new_participant = lp_setup + .b_mock + .create_user_account(&rust_biguint!(TICKET_COST * MAX_TIER_TICKETS as u64)); + participants.push(second_new_participant.clone()); + + // add another "whale" + lp_setup.b_mock.set_block_nonce(CONFIRM_START_BLOCK - 1); + lp_setup + .b_mock + .execute_tx( + &lp_setup.owner_address, + &lp_setup.lp_wrapper, + &rust_biguint!(0), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push((managed_address!(&new_participant), 1).into()); + args.push((managed_address!(&second_new_participant), MAX_TIER_TICKETS).into()); + + sc.add_tickets_endpoint(args); + }, + ) + .assert_ok(); + + // add migration guaranteed tickets for 2 users + lp_setup + .b_mock + .execute_tx( + &lp_setup.owner_address, + &lp_setup.lp_wrapper, + &rust_biguint!(0), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push(managed_address!(&participants[2])); + args.push(managed_address!(&participants[4])); + + sc.add_more_guaranteed_tickets_endpoint(args); + }, + ) + .assert_ok(); + + lp_setup.b_mock.set_block_nonce(CONFIRM_START_BLOCK); + + // user[0] and user[1] will not confirm, so they get filtered + lp_setup.confirm(&participants[2], 3).assert_ok(); + lp_setup.confirm(&participants[3], 1).assert_ok(); + lp_setup.confirm(&participants[4], 3).assert_ok(); + + lp_setup + .b_mock + .set_block_nonce(WINNER_SELECTION_START_BLOCK); + + lp_setup.filter_tickets().assert_ok(); + + // 4 guaranteed tickets + lp_setup + .select_base_winners_mock(nr_winning_tickets, 4) + .assert_ok(); + + lp_setup + .b_mock + .execute_query(&lp_setup.lp_wrapper, |sc| { + assert_eq!(sc.ticket_status(1).get(), WINNING_TICKET); + assert_eq!(sc.ticket_status(2).get(), false); + assert_eq!(sc.ticket_status(3).get(), false); + assert_eq!(sc.ticket_status(4).get(), false); + assert_eq!(sc.ticket_status(5).get(), false); + assert_eq!(sc.ticket_status(6).get(), false); + assert_eq!(sc.ticket_status(7).get(), false); + assert_eq!(sc.ticket_status(8).get(), false); + assert_eq!(sc.ticket_status(9).get(), false); + + assert_eq!( + sc.nr_winning_tickets().get(), + nr_winning_tickets + - nr_staking_guaranteed_tickets + - nr_migration_guaranteed_tickets + ); + assert_eq!(sc.users_with_guaranteed_ticket().len(), 2); + }) + .assert_ok(); + + // distribute by steps, to isolate each step's effect + lp_setup + .b_mock + .execute_tx( + &lp_setup.owner_address, + &lp_setup.lp_wrapper, + &rust_biguint!(0), + |sc| { + let mut op = GuaranteedTicketsSelectionOperation::default(); + + // first step + sc.select_guaranteed_tickets(&mut op); + + assert_eq!(sc.ticket_status(1).get(), WINNING_TICKET); // randomly selected -> leftover_ticket + assert_eq!(sc.ticket_status(2).get(), WINNING_TICKET); // migration guaranteed ticket -> additional_winning_tickets + assert_eq!(sc.ticket_status(3).get(), false); + assert_eq!(sc.ticket_status(4).get(), false); + assert_eq!(sc.ticket_status(5).get(), WINNING_TICKET); // staking guaranteed ticket -> additional_winning_tickets + assert_eq!(sc.ticket_status(6).get(), WINNING_TICKET); // migration guaranteed ticket -> additional_winning_tickets + assert_eq!(sc.ticket_status(7).get(), false); + + assert_eq!(op.leftover_tickets, 1); + assert_eq!(op.total_additional_winning_tickets, 3); + assert_eq!(op.leftover_ticket_pos_offset, 1); + + // second step + sc.distribute_leftover_tickets(&mut op); + + assert_eq!(sc.ticket_status(1).get(), WINNING_TICKET); + assert_eq!(sc.ticket_status(2).get(), WINNING_TICKET); + assert_eq!(sc.ticket_status(3).get(), false); + assert_eq!(sc.ticket_status(4).get(), WINNING_TICKET); + assert_eq!(sc.ticket_status(5).get(), WINNING_TICKET); + assert_eq!(sc.ticket_status(6).get(), WINNING_TICKET); + assert_eq!(sc.ticket_status(7).get(), false); + + assert_eq!(op.leftover_tickets, 0); + assert_eq!(op.total_additional_winning_tickets, 4); + assert_eq!(op.leftover_ticket_pos_offset, 3); + + assert_eq!(sc.users_with_guaranteed_ticket().len(), 0); + }, + ) + .assert_ok(); +} diff --git a/launchpad-guaranteed-tickets/wasm/src/lib.rs b/launchpad-guaranteed-tickets/wasm/src/lib.rs index 6b66472..e88be13 100644 --- a/launchpad-guaranteed-tickets/wasm/src/lib.rs +++ b/launchpad-guaranteed-tickets/wasm/src/lib.rs @@ -5,9 +5,9 @@ //////////////////////////////////////////////////// // Init: 1 -// Endpoints: 31 +// Endpoints: 33 // Async Callback (empty): 1 -// Total number of exported functions: 33 +// Total number of exported functions: 35 #![no_std] #![feature(lang_items)] @@ -19,11 +19,13 @@ multiversx_sc_wasm_adapter::endpoints! { launchpad_guaranteed_tickets ( addTickets + addMoreGuaranteedTickets depositLaunchpadTokens addUsersToBlacklist distributeGuaranteedTickets claimLaunchpadTokens claimTicketPayment + getUserTicketsStatus getLaunchStageFlags getConfiguration getLaunchpadTokenId diff --git a/launchpad-locked-tokens-and-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs b/launchpad-locked-tokens-and-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs index 63ce431..1766c54 100644 --- a/launchpad-locked-tokens-and-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs +++ b/launchpad-locked-tokens-and-guaranteed-tickets/tests/guaranteed_tickets_setup/mod.rs @@ -5,7 +5,9 @@ use launchpad_common::{ user_interactions::UserInteractionsModule, winner_selection::WinnerSelectionModule, }; -use launchpad_guaranteed_tickets::guaranteed_tickets_init::GuaranteedTicketsInitModule; +use launchpad_guaranteed_tickets::guaranteed_tickets_init::{ + GuaranteedTicketsInitModule, UserGuaranteedTickets, STAKING_GUARANTEED_TICKETS_NO, +}; use launchpad_locked_tokens_and_guaranteed_tickets::LaunchpadLockedTokensAndGuaranteedTickets; use multiversx_sc::types::{ Address, EgldOrEsdtTokenIdentifier, EsdtLocalRole, MultiValueEncoded, OperationCompletionStatus, @@ -124,9 +126,13 @@ where // 1 ticket for the max tier gets removed assert_eq!(sc.nr_winning_tickets().get(), NR_WINNING_TICKETS - 1); assert_eq!(sc.users_with_guaranteed_ticket().len(), 1); + let user_guaranteed_tickets = UserGuaranteedTickets::new( + managed_address!(participants.last().unwrap()), + STAKING_GUARANTEED_TICKETS_NO, + ); assert!(sc .users_with_guaranteed_ticket() - .contains(&managed_address!(participants.last().unwrap()))); + .contains(&user_guaranteed_tickets)); }) .assert_ok(); diff --git a/launchpad-nft-and-guaranteed-tickets/tests/combined_selection_test.rs b/launchpad-nft-and-guaranteed-tickets/tests/combined_selection_test.rs index 4b09893..c8c0312 100644 --- a/launchpad-nft-and-guaranteed-tickets/tests/combined_selection_test.rs +++ b/launchpad-nft-and-guaranteed-tickets/tests/combined_selection_test.rs @@ -8,7 +8,9 @@ use launchpad_common::{ config::ConfigModule, tickets::{TicketsModule, WINNING_TICKET}, }; -use launchpad_guaranteed_tickets::guaranteed_tickets_init::GuaranteedTicketsInitModule; +use launchpad_guaranteed_tickets::guaranteed_tickets_init::{ + GuaranteedTicketsInitModule, UserGuaranteedTickets, STAKING_GUARANTEED_TICKETS_NO, +}; use launchpad_with_nft::{ confirm_nft::ConfirmNftModule, mystery_sft::MysterySftTypes, nft_winners_selection::NftWinnersSelectionModule, @@ -34,9 +36,13 @@ fn setup_test() { MAX_TIER_TICKETS ); assert_eq!(sc.users_with_guaranteed_ticket().len(), 1); + let user_guaranteed_tickets = UserGuaranteedTickets::new( + managed_address!(part.last().unwrap()), + STAKING_GUARANTEED_TICKETS_NO, + ); assert!(sc .users_with_guaranteed_ticket() - .contains(&managed_address!(part.last().unwrap()))); + .contains(&user_guaranteed_tickets)); assert_eq!(sc.total_available_nfts().get(), TOTAL_NFTS); assert_eq!(sc.confirmed_nft_user_list().len(), 2);