Skip to main content

percli_program/instructions/
liquidate.rs

1use anchor_lang::prelude::*;
2use percli_core::LiquidationPolicy;
3
4use crate::error::{from_risk_error, PercolatorError};
5use crate::instructions::events;
6use crate::state::{engine_from_account_data, MARKET_ACCOUNT_SIZE};
7
8#[derive(Accounts)]
9pub struct Liquidate<'info> {
10    /// Permissionless — anyone can liquidate.
11    pub liquidator: Signer<'info>,
12
13    /// CHECK: Validated via owner, discriminator, and size.
14    #[account(
15        mut,
16        owner = crate::ID @ PercolatorError::AccountNotFound,
17        constraint = market.data_len() >= MARKET_ACCOUNT_SIZE @ PercolatorError::AccountNotFound,
18    )]
19    pub market: UncheckedAccount<'info>,
20}
21
22pub fn handler(ctx: Context<Liquidate>, account_idx: u16, funding_rate: i64) -> Result<bool> {
23    let market = &ctx.accounts.market;
24    let mut data = market.try_borrow_mut_data()?;
25
26    require!(crate::state::is_v1_market(&data), PercolatorError::AccountNotFound);
27
28    let engine = engine_from_account_data(&mut data);
29    let oracle_price = engine.last_oracle_price;
30    let clock = Clock::get()?;
31
32    let liquidated = engine
33        .liquidate_at_oracle_not_atomic(
34            account_idx,
35            clock.slot,
36            oracle_price,
37            LiquidationPolicy::FullClose,
38            funding_rate,
39        )
40        .map_err(from_risk_error)?;
41
42    emit!(events::Liquidated {
43        liquidator: ctx.accounts.liquidator.key(),
44        account_idx,
45        liquidated,
46    });
47
48    Ok(liquidated)
49}