From 3058a9ee0678fe85140e4b6ea5a6cd556c9eed98 Mon Sep 17 00:00:00 2001 From: Jordan Prince Date: Tue, 7 Sep 2021 18:37:49 -0500 Subject: [PATCH] Lottery endpoints --- rust/fair-launch/src/lib.rs | 132 ++++++++++++++++++++++++++++++---- rust/fair-launch/src/utils.rs | 2 +- 2 files changed, 119 insertions(+), 15 deletions(-) diff --git a/rust/fair-launch/src/lib.rs b/rust/fair-launch/src/lib.rs index 01d4885..2754592 100644 --- a/rust/fair-launch/src/lib.rs +++ b/rust/fair-launch/src/lib.rs @@ -7,7 +7,7 @@ use { }, anchor_lang::{ prelude::*, - solana_program::{clock::UnixTimestamp, program_pack::Pack, system_program}, + solana_program::{program_pack::Pack, system_program}, AnchorDeserialize, AnchorSerialize, }, anchor_spl::token::{self, Mint, TokenAccount}, @@ -113,13 +113,114 @@ pub mod fair_launch { let mut start = fair_launch.data.price_range_start; while start <= fair_launch.data.price_range_end { counts_at_each_tick.push(0); - start = start.checked_add(fair_launch.data.tick_size).ok_or(ErrorCode::NumericalOverflowError)?; + start = start + .checked_add(fair_launch.data.tick_size) + .ok_or(ErrorCode::NumericalOverflowError)?; } fair_launch.counts_at_each_tick = counts_at_each_tick; Ok(()) } + + pub fn update_fair_launch( + ctx: Context, + data: FairLaunchData, + ) -> ProgramResult { + let fair_launch = &mut ctx.accounts.fair_launch; + + assert_data_valid(&data)?; + fair_launch.data = data; + + Ok(()) + } + + pub fn start_phase_three( + ctx: Context, + phase_three_start: i64, + phase_three_end: i64, + ) -> ProgramResult { + let fair_launch = &mut ctx.accounts.fair_launch; + let fair_launch_lottery_bitmap = &ctx.accounts.fair_launch_lottery_bitmap; + + if fair_launch_lottery_bitmap.bitmap_ones != fair_launch.number_tickets_sold_in_phase_1 { + return Err(ErrorCode::LotteryBitmapOnesMustEqualNumberOfTicketsSold.into()); + } + + if phase_three_start < fair_launch.data.phase_two_end { + return Err(ErrorCode::TimestampsDontLineUp.into()); + } + + if phase_three_end <= phase_three_start { + return Err(ErrorCode::TimestampsDontLineUp.into()); + } + + fair_launch.data.phase_three_start = Some(phase_three_start); + fair_launch.data.phase_three_end = Some(phase_three_end); + + Ok(()) + } + + pub fn update_fair_launch_lottery_bitmap( + ctx: Context, + index: u32, + bytes: Vec + ) -> ProgramResult { + let fair_launch_lottery_bitmap = &mut ctx.accounts.fair_launch_lottery_bitmap; + + let fair_launch_lottery_bitmap_info = fair_launch_lottery_bitmap.to_account_info(); + let mut lottery_data = fair_launch_lottery_bitmap_info.data.borrow_mut(); + let mut number_of_ones_changed: i64 = 0; + let mut curr_pos = FAIR_LAUNCH_LOTTERY_SIZE + (index as usize); + for byte in bytes { + let curr_byte = lottery_data[curr_pos]; + msg!("Curr byte is {}", curr_byte); + for bit_position in 0..7 { + msg!("Looking for position {}", bit_position); + let mask = u8::pow(2, bit_position as u32); + let curr_byte_masked = curr_byte | mask; + let byte_masked = byte | mask; + msg!("Mask is {} and this led to curr byte masked {} and new byte masked {}", mask, curr_byte_masked, byte_masked); + if curr_byte_masked > byte_masked { + msg!("Subtracting 1"); + number_of_ones_changed -= 1; // we went from a 1 to a 0 + } else if curr_byte_masked < byte_masked { + msg!("Adding 1"); + number_of_ones_changed += 1 // We went from a 0 to 1 + } else { + msg!("No change here"); // 1 and 1 or 0 and 0 + } + } + lottery_data[curr_pos] = byte; + curr_pos += 1; + } + + let new_number_of_ones: u64; + // if less than zero, do a checked sub and convert negative to positive, + // otherwise, just do normal conversion and addition. + // Dont convert bitmap_ones to i64 because in conversion you lose bit of information to sign... + // better to be verbose and stick to u64...what if its very large number? + if number_of_ones_changed < 0 { + new_number_of_ones = fair_launch_lottery_bitmap.bitmap_ones.checked_sub((-number_of_ones_changed) as u64).ok_or(ErrorCode::NumericalOverflowError)?; + } else { + new_number_of_ones = fair_launch_lottery_bitmap.bitmap_ones.checked_add(number_of_ones_changed as u64).ok_or(ErrorCode::NumericalOverflowError)?; + } + + lottery_data[FAIR_LAUNCH_LOTTERY_SIZE-4..FAIR_LAUNCH_LOTTERY_SIZE].copy_from_slice(&new_number_of_ones.to_le_bytes()); + + Ok(()) + } + + pub fn create_fair_launch_lottery_bitmap( + ctx: Context, + bump: u8, + ) -> ProgramResult { + let fair_launch_lottery_bitmap = &mut ctx.accounts.fair_launch_lottery_bitmap; + fair_launch_lottery_bitmap.fair_launch = ctx.accounts.fair_launch.key(); + fair_launch_lottery_bitmap.bump = bump; + + Ok(()) + } } #[derive(Accounts)] @@ -150,11 +251,13 @@ pub struct UpdateFairLaunch<'info> { authority: AccountInfo<'info>, } -/// Limited Update that only sets phase 3 dates once bitmap is in place. +/// Limited Update that only sets phase 3 dates once bitmap is in place and fully setup. #[derive(Accounts)] pub struct StartPhaseThree<'info> { - #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref()], bump=fair_launch.bump,has_one=authority)] + #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref()], bump=fair_launch.bump, has_one=authority)] fair_launch: ProgramAccount<'info, FairLaunch>, + #[account(seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), LOTTERY.as_bytes()], constraint=fair_launch_lottery_bitmap.to_account_info().data_len() > 0, bump=fair_launch_lottery_bitmap.bump, has_one=fair_launch)] + fair_launch_lottery_bitmap: ProgramAccount<'info, FairLaunchLotteryBitmap>, #[account(signer)] authority: AccountInfo<'info>, } @@ -249,7 +352,7 @@ pub struct PunchTicket<'info> { pub const FAIR_LAUNCH_LOTTERY_SIZE: usize = 8 + // discriminator 32 + // fair launch 1 + // bump -4; // size of bitmask ones +8; // size of bitmask ones pub const FAIR_LAUNCH_SPACE_VEC_START: usize = 8 + // discriminator 32 + // token_mint @@ -271,7 +374,7 @@ pub const FAIR_LAUNCH_SPACE_VEC_START: usize = 8 + // discriminator 8 + // number of tickets sold in phase 1 8 + // number of tickets remaining at the end in phase 2 8 + // number of tickets punched in phase 3 -9 + // decided median, +8 + // current median, 4; // u32 representing number of amounts in vec so far pub const FAIR_LAUNCH_TICKET_SIZE: usize = 8 + // discriminator @@ -291,16 +394,15 @@ pub struct FairLaunchData { pub uuid: String, pub price_range_start: u64, pub price_range_end: u64, - pub phase_one_start: UnixTimestamp, - pub phase_one_end: UnixTimestamp, - pub phase_two_end: UnixTimestamp, - pub phase_three_start: Option, - pub phase_three_end: Option, + pub phase_one_start: i64, + pub phase_one_end: i64, + pub phase_two_end: i64, + pub phase_three_start: Option, + pub phase_three_end: Option, pub tick_size: u64, pub number_of_tokens: u64, } - #[account] pub struct FairLaunch { pub token_mint: Pubkey, @@ -314,7 +416,7 @@ pub struct FairLaunch { pub number_tickets_sold_in_phase_1: u64, pub number_tickets_remaining_in_phase_2: u64, pub number_tickets_punched_in_phase_3: u64, - pub decided_median: Option, + pub current_median: u64, pub counts_at_each_tick: Vec, } @@ -323,7 +425,7 @@ pub struct FairLaunchLotteryBitmap { pub fair_launch: Pubkey, pub bump: u8, /// This must be exactly the number of winners and is incremented precisely in each strip addition - pub bitmap_ones: u32, + pub bitmap_ones: u64, } #[derive(AnchorSerialize, AnchorDeserialize, Clone)] @@ -381,4 +483,6 @@ pub enum ErrorCode { DerivedKeyInvalid, #[msg("Treasury Already Exists")] TreasuryAlreadyExists, + #[msg("The number of ones in the lottery must equal the number of tickets sold in phase 1")] + LotteryBitmapOnesMustEqualNumberOfTicketsSold, } diff --git a/rust/fair-launch/src/utils.rs b/rust/fair-launch/src/utils.rs index 0dcb589..0215b0f 100644 --- a/rust/fair-launch/src/utils.rs +++ b/rust/fair-launch/src/utils.rs @@ -98,7 +98,7 @@ pub fn assert_data_valid(data: &FairLaunchData) -> ProgramResult { return Err(ErrorCode::CannotGiveZeroTokens.into()) } - if data.price_range_end < data.price_range_start || data.price_range_end == data.price_range_start { + if data.price_range_end <= data.price_range_start { return Err(ErrorCode::InvalidPriceRanges.into()) }