Skip to main content

percli_program/instructions/
withdraw_insurance.rs

1use anchor_lang::prelude::*;
2use anchor_spl::token::{transfer_checked, Mint, Token, TokenAccount, TransferChecked};
3
4use crate::error::{from_risk_error, PercolatorError};
5use crate::instructions::events;
6use crate::state::{
7    engine_from_account_data, header_from_account_data, market_signer_seeds, MARKET_ACCOUNT_SIZE,
8};
9
10#[derive(Accounts)]
11pub struct WithdrawInsurance<'info> {
12    #[account(mut)]
13    pub authority: Signer<'info>,
14
15    /// CHECK: Validated via owner, discriminator, and size.
16    #[account(
17        mut,
18        owner = crate::ID @ PercolatorError::AccountNotFound,
19        constraint = market.data_len() >= MARKET_ACCOUNT_SIZE @ PercolatorError::AccountNotFound,
20    )]
21    pub market: UncheckedAccount<'info>,
22
23    pub mint: Account<'info, Mint>,
24
25    /// Authority's token account to receive withdrawn insurance tokens.
26    #[account(
27        mut,
28        constraint = authority_token_account.owner == authority.key(),
29        constraint = authority_token_account.mint == mint.key(),
30    )]
31    pub authority_token_account: Account<'info, TokenAccount>,
32
33    /// Vault token account to transfer from.
34    #[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(ctx: Context<WithdrawInsurance>, amount: u64) -> Result<()> {
46    require!(amount > 0, PercolatorError::InsufficientBalance);
47
48    let market = &ctx.accounts.market;
49    let mut data = market.try_borrow_mut_data()?;
50
51    require!(
52        &data[0..8] == b"percmrkt",
53        PercolatorError::AccountNotFound
54    );
55
56    let header = header_from_account_data(&data)?;
57    require!(
58        header.authority == ctx.accounts.authority.key(),
59        PercolatorError::Unauthorized
60    );
61    require!(
62        header.mint == ctx.accounts.mint.key(),
63        PercolatorError::Unauthorized
64    );
65
66    let engine = engine_from_account_data(&mut data);
67    let clock = Clock::get()?;
68
69    engine
70        .withdraw_insurance(amount as u128, clock.slot)
71        .map_err(from_risk_error)?;
72
73    drop(data);
74
75    let bump = [header.bump];
76    let signer_seeds = market_signer_seeds(&header.authority, &bump);
77    transfer_checked(
78        CpiContext::new_with_signer(
79            ctx.accounts.token_program.key(),
80            TransferChecked {
81                from: ctx.accounts.vault.to_account_info(),
82                to: ctx.accounts.authority_token_account.to_account_info(),
83                authority: ctx.accounts.market.to_account_info(),
84                mint: ctx.accounts.mint.to_account_info(),
85            },
86            &[&signer_seeds],
87        ),
88        amount,
89        ctx.accounts.mint.decimals,
90    )?;
91
92    emit!(events::InsuranceWithdrawn {
93        authority: ctx.accounts.authority.key(),
94        amount,
95    });
96
97    Ok(())
98}