Skip to main content

percli_chain/commands/
deploy.rs

1use anyhow::{bail, Result};
2use solana_sdk::pubkey::Pubkey;
3use solana_sdk::system_instruction;
4
5use crate::config::ChainConfig;
6use crate::ix::{self, InitializeMarketArgs, RiskParamsInput};
7use crate::rpc::ChainRpc;
8
9/// Market account size: 8 (discriminator) + 168 (v1 header) + size_of::<RiskEngine>().
10///
11/// Must be >= the on-chain program's `MARKET_ACCOUNT_SIZE`. Note that the SBF
12/// `size_of::<RiskEngine>()` differs from the host value by ~536 bytes due to
13/// `i128`/`u128` alignment in the large `[Account; MAX_ACCOUNTS]` array — the
14/// host value is *strictly larger*, so allocating with the host constant always
15/// satisfies the program's size check.
16const MARKET_ACCOUNT_SIZE: usize = 8 + 168 + std::mem::size_of::<percli_core::RiskEngine>();
17
18pub fn run(
19    config: &ChainConfig,
20    mint: &Pubkey,
21    oracle: &Pubkey,
22    matcher: &Pubkey,
23    init_oracle_price: u64,
24) -> Result<()> {
25    let rpc = ChainRpc::new(&config.rpc_url);
26    let (market_pda, _bump) = config.market_pda();
27    let (vault_pda, _) = config.vault_pda();
28
29    if rpc.account_exists(&market_pda)? {
30        bail!("Market already exists at {market_pda}");
31    }
32
33    let slot = rpc.get_slot()?;
34
35    // Default risk params for devnet testing
36    let params = RiskParamsInput {
37        warmup_period_slots: 0,
38        maintenance_margin_bps: 500,
39        initial_margin_bps: 1000,
40        trading_fee_bps: 10,
41        max_accounts: 4096,
42        new_account_fee: 0,
43        maintenance_fee_per_slot: 0,
44        max_crank_staleness_slots: 100,
45        liquidation_fee_bps: 50,
46        liquidation_fee_cap: 1_000_000,
47        min_liquidation_abs: 100,
48        min_initial_deposit: 1000,
49        min_nonzero_mm_req: 100,
50        min_nonzero_im_req: 200,
51        insurance_floor: 0,
52    };
53
54    let args = InitializeMarketArgs {
55        init_slot: slot,
56        init_oracle_price,
57        params,
58    };
59
60    // Step 1: Create the market PDA account (large accounts can't be created via CPI)
61    let rent = rpc.get_minimum_balance(MARKET_ACCOUNT_SIZE)?;
62    let create_ix = system_instruction::create_account(
63        &config.authority(),
64        &market_pda,
65        rent,
66        MARKET_ACCOUNT_SIZE as u64,
67        &config.program_id,
68    );
69
70    // Step 2: Initialize the market
71    let init_ix = ix::initialize_market_ix(
72        &config.program_id,
73        &market_pda,
74        &config.authority(),
75        mint,
76        &vault_pda,
77        oracle,
78        matcher,
79        &spl_token::id(),
80        args,
81    );
82
83    println!("Deploying market...");
84    println!("  Authority: {}", config.authority());
85    println!("  Market PDA: {market_pda}");
86    println!("  Vault PDA:  {vault_pda}");
87    println!("  Mint:       {mint}");
88    println!("  Oracle:     {oracle}");
89    println!("  Matcher:    {matcher}");
90    println!("  RPC: {}", config.rpc_url);
91
92    let sig = rpc.send_tx(&[create_ix, init_ix], &config.keypair)?;
93    println!("  Tx: {sig}");
94    println!("Market deployed.");
95
96    Ok(())
97}