Fair launch work

This commit is contained in:
Jordan Prince 2021-09-03 12:43:10 -05:00
parent 1a9c200082
commit 21255a22cf
4 changed files with 265 additions and 0 deletions

View File

@ -0,0 +1,20 @@
[package]
name = "fair-launch"
version = "0.1.0"
description = "Created with Anchor"
edition = "2018"
[lib]
crate-type = ["cdylib", "lib"]
name = "fair_launch"
[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = "0.13.2"
arrayref = "0.3.6"
spl-token = { version="3.1.1", features = [ "no-entrypoint" ] }

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

172
rust/fair-launch/src/lib.rs Normal file
View File

@ -0,0 +1,172 @@
pub mod utils;
use {
crate::utils::{assert_initialized, assert_owned_by, spl_token_transfer, TokenTransferParams},
anchor_lang::{
prelude::*,
solana_program::{clock::UnixTimestamp, program_pack::Pack, system_program},
AnchorDeserialize, AnchorSerialize,
},
spl_token::state::{Account, Mint},
};
pub const PREFIX: &str = "fair_launch";
pub const TREASURY: &str = "treasury";
pub const MINT: &str = "mint";
#[program]
pub mod fair_launch {
use super::*;
pub fn initialize(ctx: Context<InitializeFairLaunch>) -> ProgramResult {
Ok(())
}
}
#[derive(Accounts)]
#[instruction(bump: u8, treasury_bump: u8, token_mint_bump: u8, data: FairLaunchData)]
pub struct InitializeFairLaunch<'info> {
#[account(init, seeds=[PREFIX.as_bytes(), token_mint.key.as_ref()], payer=payer, bump=bump, space=FAIR_LAUNCH_SPACE_VEC_START+16*(((data.price_range_end - data.price_range_start).checked_div(data.tick_size).ok_or(ErrorCode::NumericalOverflowError)? + 1)) as usize)]
fair_launch: ProgramAccount<'info, FairLaunch>,
#[account(init, seeds=[PREFIX.as_bytes(), authority.key.as_ref(), MINT.as_bytes(), data.uuid.as_bytes()], payer=payer, bump=token_mint_bump, space=Mint::LEN)]
token_mint: AccountInfo<'info>,
#[account(init, seeds=[PREFIX.as_bytes(), token_mint.key.as_ref(), TREASURY.as_bytes()], payer=payer, bump=treasury_bump, space=Account::LEN)]
treasury: AccountInfo<'info>,
#[account(constraint= authority.data_is_empty() && authority.lamports() > 0)]
authority: AccountInfo<'info>,
#[account(mut, signer)]
payer: AccountInfo<'info>,
#[account(address = spl_token::id())]
token_program: AccountInfo<'info>,
#[account(address = system_program::ID)]
system_program: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
}
#[derive(Accounts)]
pub struct UpdateFairLaunch<'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>,
}
#[derive(Accounts)]
#[instruction(bump: u8, amount: u64)]
pub struct PurchaseTicket<'info> {
#[account(mut, seeds=[PREFIX.as_bytes(), fair_launch.token_mint.as_ref(), &[fair_launch.bump]], has_one=treasury)]
fair_launch: ProgramAccount<'info, FairLaunch>,
#[account(mut)]
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(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)]
payer: AccountInfo<'info>,
#[account(address = spl_token::id())]
token_program: AccountInfo<'info>,
#[account(address = system_program::ID)]
system_program: AccountInfo<'info>,
rent: Sysvar<'info, Rent>,
}
#[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>,
fair_launch: ProgramAccount<'info, FairLaunch>,
#[account(mut)]
treasury: AccountInfo<'info>,
#[account(mut, signer)]
buyer: AccountInfo<'info>,
#[account(address = spl_token::id())]
token_program: AccountInfo<'info>,
#[account(address = system_program::ID)]
system_program: AccountInfo<'info>,
}
pub const FAIR_LAUNCH_SPACE_VEC_START: usize = 8 + // discriminator
32 + // token_mint
32 + // treasury
32 + // authority
1 + // bump
1 + // treasury_bump
1 + // token_mint_bump
4 + 6 + // uuid
8 + //range start
8 + // range end
8 + // phase one start
8 + // phase one end
8 + // phase two end
8 + // phase three end
8 + // tick size
8 + // number of tokens provided
8 + // number of tickets sold
4; // u32 representing number of amounts in vec so far
pub const FAIR_LAUNCH_TICKET_SIZE: usize = 8 + // discriminator
32 + // fair launch reverse lookup
32 + // buyer
8 + // amount paid in so far
1 + // state
1; // bump
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
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_end: UnixTimestamp,
pub tick_size: u64,
pub number_of_tokens_provided: u64,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
pub struct MedianTuple(pub u64, pub u64);
#[account]
pub struct FairLaunch {
pub token_mint: Pubkey,
pub treasury: Pubkey,
pub authority: Pubkey,
pub bump: u8,
pub treasury_bump: u8,
pub token_mint_bump: u8,
pub data: FairLaunchData,
pub number_tickets_sold: u64,
pub median: Vec<MedianTuple>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub enum FairLaunchTicketState {
Unpunched,
Punched,
Withdrawn,
}
#[account]
pub struct FairLaunchTicket {
pub fair_launch: Pubkey,
pub buyer: Pubkey,
pub amount: u64,
pub state: FairLaunchTicketState,
pub bump: u8,
}
#[error]
pub enum ErrorCode {
#[msg("Account does not have correct owner!")]
IncorrectOwner,
#[msg("Account is not initialized!")]
Uninitialized,
#[msg("Mint Mismatch!")]
MintMismatch,
#[msg("Token transfer failed")]
TokenTransferFailed,
#[msg("Numerical overflow error")]
NumericalOverflowError,
}

View File

@ -0,0 +1,71 @@
use {
crate::ErrorCode,
anchor_lang::{
prelude::{AccountInfo, ProgramError, ProgramResult, Pubkey},
solana_program::{
program::invoke_signed,
program_pack::{IsInitialized, Pack},
},
},
};
pub fn assert_initialized<T: Pack + IsInitialized>(
account_info: &AccountInfo,
) -> Result<T, ProgramError> {
let account: T = T::unpack_unchecked(&account_info.data.borrow())?;
if !account.is_initialized() {
Err(ErrorCode::Uninitialized.into())
} else {
Ok(account)
}
}
pub fn assert_owned_by(account: &AccountInfo, owner: &Pubkey) -> ProgramResult {
if account.owner != owner {
Err(ErrorCode::IncorrectOwner.into())
} else {
Ok(())
}
}
///TokenTransferParams
pub struct TokenTransferParams<'a: 'b, 'b> {
/// source
pub source: AccountInfo<'a>,
/// destination
pub destination: AccountInfo<'a>,
/// amount
pub amount: u64,
/// authority
pub authority: AccountInfo<'a>,
/// authority_signer_seeds
pub authority_signer_seeds: &'b [&'b [u8]],
/// token_program
pub token_program: AccountInfo<'a>,
}
#[inline(always)]
pub fn spl_token_transfer(params: TokenTransferParams<'_, '_>) -> ProgramResult {
let TokenTransferParams {
source,
destination,
authority,
token_program,
amount,
authority_signer_seeds,
} = params;
let result = invoke_signed(
&spl_token::instruction::transfer(
token_program.key,
source.key,
destination.key,
authority.key,
&[],
amount,
)?,
&[source, destination, authority, token_program],
&[authority_signer_seeds],
);
result.map_err(|_| ErrorCode::TokenTransferFailed.into())
}