mango-v4/programs/mango-v4/src/instructions/serum3_cancel_order.rs

135 lines
4.0 KiB
Rust
Raw Normal View History

2022-03-19 04:11:56 -07:00
use anchor_lang::prelude::*;
use arrayref::array_refs;
use borsh::{BorshDeserialize, BorshSerialize};
use num_enum::TryFromPrimitive;
use std::io::Write;
use serum_dex::matching::Side;
use crate::error::*;
use crate::state::*;
/// Unfortunately CancelOrderInstructionV2 isn't borsh serializable.
///
/// Make a newtype and implement the traits for it.
pub struct CancelOrderInstructionData(pub serum_dex::instruction::CancelOrderInstructionV2);
impl CancelOrderInstructionData {
// Copy of CancelOrderInstructionV2::unpack(), which we wish were public!
fn unpack(data: &[u8; 20]) -> Option<Self> {
let (&side_arr, &oid_arr) = array_refs![data, 4, 16];
let side = Side::try_from_primitive(u32::from_le_bytes(side_arr).try_into().ok()?).ok()?;
let order_id = u128::from_le_bytes(oid_arr);
Some(Self(serum_dex::instruction::CancelOrderInstructionV2 {
side,
order_id,
}))
}
}
impl BorshDeserialize for CancelOrderInstructionData {
fn deserialize(buf: &mut &[u8]) -> std::result::Result<Self, std::io::Error> {
let data: &[u8; 20] = buf[0..20]
.try_into()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::UnexpectedEof, e))?;
*buf = &buf[20..];
Ok(Self::unpack(data).ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
error!(MangoError::SomeError),
)
})?)
}
}
impl BorshSerialize for CancelOrderInstructionData {
fn serialize<W: Write>(&self, writer: &mut W) -> std::result::Result<(), std::io::Error> {
// serum_dex uses bincode::serialize() internally, see MarketInstruction::pack()
2022-03-19 04:53:24 -07:00
writer.write_all(&bincode::serialize(&self.0).unwrap())?;
2022-03-19 04:11:56 -07:00
Ok(())
}
}
#[derive(Accounts)]
pub struct Serum3CancelOrder<'info> {
pub group: AccountLoader<'info, Group>,
#[account(
mut,
has_one = group,
has_one = owner,
)]
pub account: AccountLoader<'info, MangoAccount>,
pub owner: Signer<'info>,
// Validated inline
#[account(mut)]
pub open_orders: UncheckedAccount<'info>,
#[account(
has_one = group,
has_one = serum_program,
has_one = serum_market_external,
)]
pub serum_market: AccountLoader<'info, Serum3Market>,
pub serum_program: UncheckedAccount<'info>,
#[account(mut)]
pub serum_market_external: UncheckedAccount<'info>,
// These accounts are forwarded directly to the serum cpi call
// and are validated there.
#[account(mut)]
pub market_bids: UncheckedAccount<'info>,
#[account(mut)]
pub market_asks: UncheckedAccount<'info>,
#[account(mut)]
pub market_event_queue: UncheckedAccount<'info>,
}
pub fn serum3_cancel_order(
ctx: Context<Serum3CancelOrder>,
order: CancelOrderInstructionData,
) -> Result<()> {
//
// Validation
//
{
let account = ctx.accounts.account.load()?;
let serum_market = ctx.accounts.serum_market.load()?;
// Validate open_orders
require!(
account
.serum3_account_map
.find(serum_market.market_index)
2022-03-19 04:53:24 -07:00
.ok_or_else(|| error!(MangoError::SomeError))?
2022-03-19 04:11:56 -07:00
.open_orders
== ctx.accounts.open_orders.key(),
MangoError::SomeError
);
}
//
// Cancel
2022-03-19 04:11:56 -07:00
//
cpi_cancel_order(&ctx.accounts, order)?;
Ok(())
}
fn cpi_cancel_order(ctx: &Serum3CancelOrder, order: CancelOrderInstructionData) -> Result<()> {
use crate::serum3_cpi;
let group = ctx.group.load()?;
serum3_cpi::CancelOrder {
program: ctx.serum_program.to_account_info(),
market: ctx.serum_market_external.to_account_info(),
bids: ctx.market_bids.to_account_info(),
asks: ctx.market_asks.to_account_info(),
event_queue: ctx.market_event_queue.to_account_info(),
open_orders: ctx.open_orders.to_account_info(),
open_orders_authority: ctx.group.to_account_info(),
}
.cancel_one(&group, order.0)
2022-03-19 04:11:56 -07:00
}