percli_program/instructions/
withdraw.rs1use anchor_lang::prelude::*;
2use anchor_spl::token::{transfer_checked, Mint, Token, TokenAccount, TransferChecked};
3
4use crate::error::{from_risk_error, PercolatorError};
5use crate::state::{
6 engine_from_account_data, header_from_account_data, market_signer_seeds, MARKET_ACCOUNT_SIZE,
7};
8
9#[derive(Accounts)]
10pub struct Withdraw<'info> {
11 #[account(mut)]
12 pub user: Signer<'info>,
13
14 #[account(
16 mut,
17 owner = crate::ID @ PercolatorError::AccountNotFound,
18 constraint = market.data_len() == MARKET_ACCOUNT_SIZE @ PercolatorError::AccountNotFound,
19 )]
20 pub market: UncheckedAccount<'info>,
21
22 pub mint: Account<'info, Mint>,
24
25 #[account(
27 mut,
28 constraint = user_token_account.owner == user.key(),
29 constraint = user_token_account.mint == mint.key(),
30 )]
31 pub user_token_account: Account<'info, TokenAccount>,
32
33 #[account(
35 mut,
36 seeds = [b"vault", market.key().as_ref()],
37 bump,
38 constraint = vault.mint == mint.key(),
39 )]
40 pub vault: Account<'info, TokenAccount>,
41
42 pub token_program: Program<'info, Token>,
43}
44
45pub fn handler(
46 ctx: Context<Withdraw>,
47 account_idx: u16,
48 amount: u64,
49 funding_rate: i64,
50) -> Result<()> {
51 require!(amount > 0, PercolatorError::InsufficientBalance);
52
53 let market = &ctx.accounts.market;
55 let mut data = market.try_borrow_mut_data()?;
56
57 require!(
58 &data[0..8] == b"percmrkt",
59 PercolatorError::AccountNotFound
60 );
61
62 let header = header_from_account_data(&data)?;
63 require!(header.mint == ctx.accounts.mint.key(), PercolatorError::Unauthorized);
64
65 let engine = engine_from_account_data(&mut data);
66
67 let account_owner = engine.accounts[account_idx as usize].owner;
69 require!(
70 account_owner == ctx.accounts.user.key().to_bytes(),
71 PercolatorError::Unauthorized
72 );
73
74 let oracle_price = engine.last_oracle_price;
75 let clock = Clock::get()?;
76
77 engine
79 .withdraw_not_atomic(account_idx, amount as u128, oracle_price, clock.slot, funding_rate)
80 .map_err(from_risk_error)?;
81
82 drop(data);
84
85 let bump = [header.bump];
87 let signer_seeds = market_signer_seeds(&header.authority, &bump);
88 transfer_checked(
89 CpiContext::new_with_signer(
90 ctx.accounts.token_program.key(),
91 TransferChecked {
92 from: ctx.accounts.vault.to_account_info(),
93 to: ctx.accounts.user_token_account.to_account_info(),
94 authority: ctx.accounts.market.to_account_info(),
95 mint: ctx.accounts.mint.to_account_info(),
96 },
97 &[&signer_seeds],
98 ),
99 amount,
100 ctx.accounts.mint.decimals,
101 )?;
102
103 Ok(())
104}