manifest/program/processor/
create_market.rs

1use std::{cell::Ref, mem::size_of};
2
3use crate::{
4    logs::{emit_stack, CreateMarketLog},
5    program::{expand_market_if_needed, invoke},
6    require,
7    state::MarketFixed,
8    utils::create_account,
9    validation::{get_vault_address, loaders::CreateMarketContext},
10};
11use hypertree::{get_mut_helper, trace};
12use solana_program::{
13    account_info::AccountInfo, entrypoint::ProgramResult, program_pack::Pack, pubkey::Pubkey,
14    rent::Rent, sysvar::Sysvar,
15};
16use spl_token_2022::{
17    extension::{
18        mint_close_authority::MintCloseAuthority, permanent_delegate::PermanentDelegate,
19        BaseStateWithExtensions, ExtensionType, PodStateWithExtensions, StateWithExtensions,
20    },
21    pod::PodMint,
22    state::{Account, Mint},
23};
24
25pub(crate) fn process_create_market(
26    _program_id: &Pubkey,
27    accounts: &[AccountInfo],
28    _data: &[u8],
29) -> ProgramResult {
30    trace!("process_create_market accs={accounts:?}");
31    let create_market_context: CreateMarketContext = CreateMarketContext::load(accounts)?;
32
33    let CreateMarketContext {
34        market,
35        payer,
36        base_mint,
37        quote_mint,
38        base_vault,
39        quote_vault,
40        system_program,
41        token_program,
42        token_program_22,
43    } = create_market_context;
44
45    require!(
46        base_mint.info.key != quote_mint.info.key,
47        crate::program::ManifestError::InvalidMarketParameters,
48        "Base and quote must be different",
49    )?;
50
51    for mint in [base_mint.as_ref(), quote_mint.as_ref()] {
52        if *mint.owner == spl_token_2022::id() {
53            let mint_data: Ref<'_, &mut [u8]> = mint.data.borrow();
54            let pool_mint: StateWithExtensions<'_, Mint> =
55                StateWithExtensions::<Mint>::unpack(&mint_data)?;
56            // Closable mints can be replaced with different ones, breaking some saved info on the market.
57            if let Ok(extension) = pool_mint.get_extension::<MintCloseAuthority>() {
58                let close_authority: Option<Pubkey> = extension.close_authority.into();
59                if close_authority.is_some() {
60                    solana_program::msg!(
61                        "Warning, you are creating a market with a close authority."
62                    );
63                }
64            }
65            // Permanent delegates can steal your tokens. This will break all
66            // accounting in the market, so there is no assertion of security
67            // against loss of funds on these markets.
68            if let Ok(extension) = pool_mint.get_extension::<PermanentDelegate>() {
69                let permanent_delegate: Option<Pubkey> = extension.delegate.into();
70                if permanent_delegate.is_some() {
71                    solana_program::msg!(
72                        "Warning, you are creating a market with a permanent delegate. There is no loss of funds protection for funds on this market"
73                    );
74                }
75            }
76        }
77    }
78
79    {
80        // Create the base and quote vaults of this market
81        let rent: Rent = Rent::get()?;
82        for (token_account, mint) in [
83            (base_vault.as_ref(), base_mint.as_ref()),
84            (quote_vault.as_ref(), quote_mint.as_ref()),
85        ] {
86            // We dont have to deserialize the mint, just check the owner.
87            let is_mint_22: bool = *mint.owner == spl_token_2022::id();
88            let token_program_for_mint: Pubkey = if is_mint_22 {
89                spl_token_2022::id()
90            } else {
91                spl_token::id()
92            };
93
94            let (_vault_key, bump) = get_vault_address(market.key, mint.key);
95            let seeds: Vec<Vec<u8>> = vec![
96                b"vault".to_vec(),
97                market.key.as_ref().to_vec(),
98                mint.key.as_ref().to_vec(),
99                vec![bump],
100            ];
101
102            if is_mint_22 {
103                let mint_data: Ref<'_, &mut [u8]> = mint.data.borrow();
104                let mint_with_extension: PodStateWithExtensions<'_, PodMint> =
105                    PodStateWithExtensions::<PodMint>::unpack(&mint_data).unwrap();
106                let mint_extensions: Vec<ExtensionType> =
107                    mint_with_extension.get_extension_types()?;
108                let required_extensions: Vec<ExtensionType> =
109                    ExtensionType::get_required_init_account_extensions(&mint_extensions);
110                let space: usize =
111                    ExtensionType::try_calculate_account_len::<Account>(&required_extensions)?;
112                create_account(
113                    payer.as_ref(),
114                    token_account,
115                    system_program.as_ref(),
116                    &token_program_for_mint,
117                    &rent,
118                    space as u64,
119                    seeds,
120                )?;
121                invoke(
122                    &spl_token_2022::instruction::initialize_account3(
123                        &token_program_for_mint,
124                        token_account.key,
125                        mint.key,
126                        token_account.key,
127                    )?,
128                    &[
129                        payer.as_ref().clone(),
130                        token_account.clone(),
131                        mint.clone(),
132                        token_program_22.as_ref().clone(),
133                    ],
134                )?;
135            } else {
136                let space: usize = spl_token::state::Account::LEN;
137                create_account(
138                    payer.as_ref(),
139                    token_account,
140                    system_program.as_ref(),
141                    &token_program_for_mint,
142                    &rent,
143                    space as u64,
144                    seeds,
145                )?;
146                invoke(
147                    &spl_token::instruction::initialize_account3(
148                        &token_program_for_mint,
149                        token_account.key,
150                        mint.key,
151                        token_account.key,
152                    )?,
153                    &[
154                        payer.as_ref().clone(),
155                        token_account.clone(),
156                        mint.clone(),
157                        token_program.as_ref().clone(),
158                    ],
159                )?;
160            }
161        }
162
163        // Do not need to initialize with the system program because it is
164        // assumed that it is done already and loaded with rent. That is not at
165        // a PDA because we do not want to be restricted to a single market for
166        // a pair. If there is lock contention and hotspotting for one market,
167        // it could be useful to have a second where it is easier to land
168        // transactions. That protection is worth the possibility that users
169        // would use an inactive market when multiple exist.
170
171        // Setup the empty market
172        let empty_market_fixed: MarketFixed =
173            MarketFixed::new_empty(&base_mint, &quote_mint, market.key);
174        assert_eq!(market.data_len(), size_of::<MarketFixed>());
175
176        let market_bytes: &mut [u8] = &mut market.try_borrow_mut_data()?[..];
177        *get_mut_helper::<MarketFixed>(market_bytes, 0_u32) = empty_market_fixed;
178
179        emit_stack(CreateMarketLog {
180            market: *market.key,
181            creator: *payer.key,
182            base_mint: *base_mint.info.key,
183            quote_mint: *quote_mint.info.key,
184        })?;
185    }
186
187    // Leave a free block on the market so takers can use and leave it.
188    expand_market_if_needed(&payer, &market)?;
189
190    Ok(())
191}