diff --git a/rust/fair-launch/Cargo.toml b/rust/fair-launch/Cargo.toml index 0552717..8d736e0 100644 --- a/rust/fair-launch/Cargo.toml +++ b/rust/fair-launch/Cargo.toml @@ -15,6 +15,7 @@ cpi = ["no-entrypoint"] default = [] [dependencies] -anchor-lang = "0.13.2" +anchor-lang = "0.14.0" +anchor-spl = "0.14.0" arrayref = "0.3.6" spl-token = { version="3.1.1", features = [ "no-entrypoint" ] } diff --git a/rust/fair-launch/src/lib.rs b/rust/fair-launch/src/lib.rs index e4839d0..ea225e6 100644 --- a/rust/fair-launch/src/lib.rs +++ b/rust/fair-launch/src/lib.rs @@ -7,12 +7,14 @@ use { solana_program::{clock::UnixTimestamp, program_pack::Pack, system_program}, AnchorDeserialize, AnchorSerialize, }, + anchor_spl::token::{self, TokenAccount}, spl_token::state::{Account, Mint}, }; pub const PREFIX: &str = "fair_launch"; pub const TREASURY: &str = "treasury"; pub const MINT: &str = "mint"; +pub const LOTTERY: &str="lottery"; #[program] pub mod fair_launch { @@ -42,6 +44,7 @@ pub struct InitializeFairLaunch<'info> { rent: Sysvar<'info, Rent>, } +/// Can only update fair launch before phase 1 start. #[derive(Accounts)] pub struct UpdateFairLaunch<'info> { #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]], has_one=authority)] @@ -50,6 +53,44 @@ pub struct UpdateFairLaunch<'info> { authority: AccountInfo<'info>, } +/// Limited Update that only sets phase 3 dates once bitmap is in place. +#[derive(Accounts)] +pub struct StartPhaseThree<'info> { + #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]], has_one=authority)] + fair_launch: ProgramAccount<'info, FairLaunch>, + #[account(signer)] + authority: AccountInfo<'info>, +} + +/// Can only create the fair launch lottery bitmap after phase 1 has ended. +#[derive(Accounts)] +#[instruction(bump: u8)] +pub struct CreateFairLaunchLotteryBitmap<'info> { + #[account(seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]], has_one=authority)] + fair_launch: ProgramAccount<'info, FairLaunch>, + #[account(init, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), LOTTERY.as_bytes()], payer=payer, bump=bump, space= FAIR_LAUNCH_LOTTERY_SIZE + (fair_launch.number_tickets_sold_in_phase_1.checked_div(8).ok_or(ErrorCode::NumericalOverflowError)? as usize) + 1)] + fair_launch_lottery_bitmap: ProgramAccount<'info, FairLaunchLotteryBitmap>, + #[account(signer)] + authority: AccountInfo<'info>, + #[account(mut, signer)] + payer: AccountInfo<'info>, + #[account(address = system_program::ID)] + system_program: AccountInfo<'info>, + rent: Sysvar<'info, Rent>, +} + +/// Can only set the fair launch lottery bitmap after phase 2 has ended. +#[derive(Accounts)] +pub struct UpdateFairLaunchLotteryBitmap<'info> { + #[account(seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]], has_one=authority)] + fair_launch: ProgramAccount<'info, FairLaunch>, + #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), LOTTERY.as_bytes(), &[fair_launch_lottery_bitmap.bump]])] + fair_launch_lottery_bitmap: ProgramAccount<'info, FairLaunchLotteryBitmap>, + #[account(signer)] + authority: AccountInfo<'info>, +} + +/// Can only purchase a ticket in phase 1. #[derive(Accounts)] #[instruction(bump: u8, amount: u64)] pub struct PurchaseTicket<'info> { @@ -59,6 +100,8 @@ pub struct PurchaseTicket<'info> { treasury: AccountInfo<'info>, #[account(init, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), buyer.key.as_ref()], payer=payer, bump=bump, space=FAIR_LAUNCH_TICKET_SIZE)] fair_launch_ticket: ProgramAccount<'info, FairLaunchTicket>, + #[account(init, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &fair_launch.number_tickets_sold_in_phase_1.to_le_bytes()], payer=payer, bump=bump, space=FAIR_LAUNCH_TICKET_SEQ_SIZE)] + fair_launch_ticket_seq_lookup: ProgramAccount<'info, FairLaunchTicket>, #[account(mut, signer, constraint= (treasury.owner == &spl_token::id() && buyer.owner == &spl_token::id()) || (treasury.owner != &spl_token::id() && buyer.data_is_empty() && buyer.lamports() > 0) )] buyer: AccountInfo<'info>, #[account(mut, signer)] @@ -70,11 +113,17 @@ pub struct PurchaseTicket<'info> { rent: Sysvar<'info, Rent>, } + +/// IN phase 1, you can adjust up or down in any way +/// In phase 2, you can adjust up or down in any way +/// In phase 3, if you are above the decided_median, you can only adjust down to decided median. If below, you can only +/// adjust down, never up. #[derive(Accounts)] #[instruction(amount: u64)] pub struct AdjustTicket<'info> { #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), buyer.key.as_ref(), &[fair_launch_ticket.bump]], has_one=buyer, has_one=fair_launch)] fair_launch_ticket: ProgramAccount<'info, FairLaunchTicket>, + #[account(seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]])] fair_launch: ProgramAccount<'info, FairLaunch>, #[account(mut)] treasury: AccountInfo<'info>, @@ -85,6 +134,26 @@ pub struct AdjustTicket<'info> { #[account(address = system_program::ID)] system_program: AccountInfo<'info>, } +#[derive(Accounts)] +pub struct PunchTicket<'info> { + #[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), buyer.key.as_ref(), &[fair_launch_ticket.bump]], has_one=buyer, has_one=fair_launch)] + fair_launch_ticket: ProgramAccount<'info, FairLaunchTicket>, + #[account(seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]], has_one=token_mint)] + fair_launch: ProgramAccount<'info, FairLaunch>, + #[account(mut, signer)] + buyer: AccountInfo<'info>, + #[account(mut, owner=spl_token::id(), token::mint=token_mint, token::authority=buyer)] + buyer_token_account: CpiAccount<'info, TokenAccount>, + #[account(seeds=[PREFIX.as_bytes(), fair_launch.authority.as_ref(), MINT.as_bytes(), fair_launch.data.uuid.as_bytes()])] + token_mint: AccountInfo<'info>, + #[account(address = spl_token::id())] + token_program: AccountInfo<'info>, +} + +pub const FAIR_LAUNCH_LOTTERY_SIZE: usize = 8 + // discriminator +32 + // fair launch +1 + // bump +4; // size of bitmask ones pub const FAIR_LAUNCH_SPACE_VEC_START: usize = 8 + // discriminator 32 + // token_mint @@ -99,10 +168,14 @@ pub const FAIR_LAUNCH_SPACE_VEC_START: usize = 8 + // discriminator 8 + // phase one start 8 + // phase one end 8 + // phase two end -8 + // phase three end +9 + // phase three start +9 + // phase three end 8 + // tick size -8 + // number of tokens provided -8 + // number of tickets sold +8 + // number of tokens +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, 4; // u32 representing number of amounts in vec so far pub const FAIR_LAUNCH_TICKET_SIZE: usize = 8 + // discriminator @@ -110,7 +183,12 @@ pub const FAIR_LAUNCH_TICKET_SIZE: usize = 8 + // discriminator 32 + // buyer 8 + // amount paid in so far 1 + // state -1; // bump +1 + // bump +8; // seq + +pub const FAIR_LAUNCH_TICKET_SEQ_SIZE: usize = 8 + //discriminator +32 + // fair launch ticket reverse lookup +8; //seq #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] pub struct FairLaunchData { @@ -120,9 +198,10 @@ pub struct FairLaunchData { pub phase_one_start: UnixTimestamp, pub phase_one_end: UnixTimestamp, pub phase_two_end: UnixTimestamp, - pub phase_three_end: UnixTimestamp, + pub phase_three_start: Option, + pub phase_three_end: Option, pub tick_size: u64, - pub number_of_tokens_provided: u64, + pub number_of_tokens: u64, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] @@ -137,10 +216,21 @@ pub struct FairLaunch { pub treasury_bump: u8, pub token_mint_bump: u8, pub data: FairLaunchData, - pub number_tickets_sold: u64, + 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 median: Vec, } +#[account] +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 +} + #[derive(AnchorSerialize, AnchorDeserialize, Clone)] pub enum FairLaunchTicketState { Unpunched, @@ -155,8 +245,17 @@ pub struct FairLaunchTicket { pub amount: u64, pub state: FairLaunchTicketState, pub bump: u8, + pub seq: u64, } + +#[account] +pub struct FairLaunchTicketSeqLookup { + pub fair_launch_ticket: Pubkey, + pub seq: u64, +} + + #[error] pub enum ErrorCode { #[msg("Account does not have correct owner!")]