1use mem::size_of;
2
3use crate::{
4 errors::AuctionError,
5 processor::{
6 AuctionData, AuctionDataExtended, AuctionName, AuctionState, Bid, BidState, PriceFloor,
7 WinnerLimit, BASE_AUCTION_DATA_SIZE, MAX_AUCTION_DATA_EXTENDED_SIZE,
8 },
9 utils::{assert_derivation, assert_owned_by, create_or_allocate_account_raw},
10 EXTENDED, PREFIX,
11};
12
13use {
14 borsh::{BorshDeserialize, BorshSerialize},
15 solana_program::{
16 account_info::{next_account_info, AccountInfo},
17 clock::UnixTimestamp,
18 entrypoint::ProgramResult,
19 msg,
20 program_error::ProgramError,
21 pubkey::Pubkey,
22 },
23 std::mem,
24};
25
26#[repr(C)]
27#[derive(Clone, BorshSerialize, BorshDeserialize, PartialEq)]
28pub struct CreateAuctionArgs {
29 pub winners: WinnerLimit,
31 pub end_auction_at: Option<UnixTimestamp>,
33 pub end_auction_gap: Option<UnixTimestamp>,
35 pub token_mint: Pubkey,
37 pub authority: Pubkey,
39 pub resource: Pubkey,
41 pub price_floor: PriceFloor,
43 pub tick_size: Option<u64>,
45 pub gap_tick_size_percentage: Option<u8>,
47}
48
49struct Accounts<'a, 'b: 'a> {
50 auction: &'a AccountInfo<'b>,
51 auction_extended: &'a AccountInfo<'b>,
52 payer: &'a AccountInfo<'b>,
53 rent: &'a AccountInfo<'b>,
54 system: &'a AccountInfo<'b>,
55}
56
57fn parse_accounts<'a, 'b: 'a>(
58 program_id: &Pubkey,
59 accounts: &'a [AccountInfo<'b>],
60) -> Result<Accounts<'a, 'b>, ProgramError> {
61 let account_iter = &mut accounts.iter();
62 let accounts = Accounts {
63 payer: next_account_info(account_iter)?,
64 auction: next_account_info(account_iter)?,
65 auction_extended: next_account_info(account_iter)?,
66 rent: next_account_info(account_iter)?,
67 system: next_account_info(account_iter)?,
68 };
69 Ok(accounts)
70}
71
72pub fn create_auction(
73 program_id: &Pubkey,
74 accounts: &[AccountInfo],
75 args: CreateAuctionArgs,
76 instant_sale_price: Option<u64>,
77 name: Option<AuctionName>,
78) -> ProgramResult {
79 msg!("+ Processing CreateAuction");
80 let accounts = parse_accounts(program_id, accounts)?;
81
82 let auction_path = [
83 PREFIX.as_bytes(),
84 program_id.as_ref(),
85 &args.resource.to_bytes(),
86 ];
87
88 let (auction_key, bump) = Pubkey::find_program_address(&auction_path, program_id);
91 if auction_key != *accounts.auction.key {
92 return Err(AuctionError::InvalidAuctionAccount.into());
93 }
94 let auction_size = match args.winners {
96 WinnerLimit::Capped(n) => {
97 mem::size_of::<Bid>() * BidState::max_array_size_for(n) + BASE_AUCTION_DATA_SIZE
98 }
99 WinnerLimit::Unlimited(_) => BASE_AUCTION_DATA_SIZE,
100 };
101
102 let bid_state = match args.winners {
103 WinnerLimit::Capped(n) => BidState::new_english(n),
104 WinnerLimit::Unlimited(_) => BidState::new_open_edition(),
105 };
106
107 if let Some(gap_tick) = args.gap_tick_size_percentage {
108 if gap_tick > 100 {
109 return Err(AuctionError::InvalidGapTickSizePercentage.into());
110 }
111 }
112
113 create_or_allocate_account_raw(
115 *program_id,
116 accounts.auction,
117 accounts.rent,
118 accounts.system,
119 accounts.payer,
120 auction_size,
121 &[
122 PREFIX.as_bytes(),
123 program_id.as_ref(),
124 &args.resource.to_bytes(),
125 &[bump],
126 ],
127 )?;
128
129 let auction_ext_bump = assert_derivation(
130 program_id,
131 accounts.auction_extended,
132 &[
133 PREFIX.as_bytes(),
134 program_id.as_ref(),
135 &args.resource.to_bytes(),
136 EXTENDED.as_bytes(),
137 ],
138 )?;
139
140 create_or_allocate_account_raw(
141 *program_id,
142 accounts.auction_extended,
143 accounts.rent,
144 accounts.system,
145 accounts.payer,
146 MAX_AUCTION_DATA_EXTENDED_SIZE,
147 &[
148 PREFIX.as_bytes(),
149 program_id.as_ref(),
150 &args.resource.to_bytes(),
151 EXTENDED.as_bytes(),
152 &[auction_ext_bump],
153 ],
154 )?;
155
156 AuctionDataExtended {
158 total_uncancelled_bids: 0,
159 tick_size: args.tick_size,
160 gap_tick_size_percentage: args.gap_tick_size_percentage,
161 instant_sale_price,
162 name,
163 }
164 .serialize(&mut *accounts.auction_extended.data.borrow_mut())?;
165
166 AuctionData {
168 authority: args.authority,
169 bid_state: bid_state,
170 end_auction_at: args.end_auction_at,
171 end_auction_gap: args.end_auction_gap,
172 ended_at: None,
173 last_bid: None,
174 price_floor: args.price_floor,
175 state: AuctionState::create(),
176 token_mint: args.token_mint,
177 }
178 .serialize(&mut *accounts.auction.data.borrow_mut())?;
179
180 Ok(())
181}