perp cancel order instructions
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
67c1d5d451
commit
db82fcbbcc
|
@ -6,6 +6,10 @@ pub use create_group::*;
|
|||
pub use create_stub_oracle::*;
|
||||
pub use deposit::*;
|
||||
pub use liq_token_with_token::*;
|
||||
pub use perp_cancel_all_orders::*;
|
||||
pub use perp_cancel_all_orders_by_side::*;
|
||||
pub use perp_cancel_order::*;
|
||||
pub use perp_cancel_order_by_client_order_id::*;
|
||||
pub use perp_consume_events::*;
|
||||
pub use perp_create_market::*;
|
||||
pub use perp_place_order::*;
|
||||
|
@ -28,6 +32,10 @@ mod create_stub_oracle;
|
|||
mod deposit;
|
||||
mod liq_token_with_token;
|
||||
mod margin_trade;
|
||||
mod perp_cancel_all_orders;
|
||||
mod perp_cancel_all_orders_by_side;
|
||||
mod perp_cancel_order;
|
||||
mod perp_cancel_order_by_client_order_id;
|
||||
mod perp_consume_events;
|
||||
mod perp_create_market;
|
||||
mod perp_place_order;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
use crate::error::MangoError;
|
||||
use crate::state::{Book, Group, MangoAccount, PerpMarket};
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct PerpCancelAllOrders<'info> {
|
||||
pub group: AccountLoader<'info, Group>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = owner,
|
||||
)]
|
||||
pub account: AccountLoader<'info, MangoAccount>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = bids,
|
||||
has_one = asks
|
||||
)]
|
||||
pub perp_market: AccountLoader<'info, PerpMarket>,
|
||||
#[account(mut)]
|
||||
pub asks: UncheckedAccount<'info>,
|
||||
#[account(mut)]
|
||||
pub bids: UncheckedAccount<'info>,
|
||||
|
||||
pub owner: Signer<'info>,
|
||||
}
|
||||
|
||||
pub fn perp_cancel_all_orders(ctx: Context<PerpCancelAllOrders>, limit: u8) -> Result<()> {
|
||||
let mut mango_account = ctx.accounts.account.load_mut()?;
|
||||
require!(mango_account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||
|
||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||
let bids = &ctx.accounts.bids.to_account_info();
|
||||
let asks = &ctx.accounts.asks.to_account_info();
|
||||
let mut book = Book::load_mut(bids, asks, &perp_market)?;
|
||||
|
||||
book.cancel_all_order(&mut mango_account, &mut perp_market, limit, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
use crate::error::MangoError;
|
||||
use crate::state::{Book, Group, MangoAccount, PerpMarket, Side};
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct PerpCancelAllOrdersBySide<'info> {
|
||||
pub group: AccountLoader<'info, Group>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = owner,
|
||||
)]
|
||||
pub account: AccountLoader<'info, MangoAccount>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = bids,
|
||||
has_one = asks
|
||||
)]
|
||||
pub perp_market: AccountLoader<'info, PerpMarket>,
|
||||
#[account(mut)]
|
||||
pub asks: UncheckedAccount<'info>,
|
||||
#[account(mut)]
|
||||
pub bids: UncheckedAccount<'info>,
|
||||
|
||||
pub owner: Signer<'info>,
|
||||
}
|
||||
|
||||
pub fn perp_cancel_all_orders_by_side(
|
||||
ctx: Context<PerpCancelAllOrdersBySide>,
|
||||
side_option: Option<Side>,
|
||||
limit: u8,
|
||||
) -> Result<()> {
|
||||
let mut mango_account = ctx.accounts.account.load_mut()?;
|
||||
require!(mango_account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||
|
||||
let mut perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||
let bids = &ctx.accounts.bids.to_account_info();
|
||||
let asks = &ctx.accounts.asks.to_account_info();
|
||||
let mut book = Book::load_mut(bids, asks, &perp_market)?;
|
||||
|
||||
book.cancel_all_order(&mut mango_account, &mut perp_market, limit, side_option)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
use crate::error::MangoError;
|
||||
use crate::state::{Book, Group, MangoAccount, PerpMarket};
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct PerpCancelOrder<'info> {
|
||||
pub group: AccountLoader<'info, Group>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = owner,
|
||||
)]
|
||||
pub account: AccountLoader<'info, MangoAccount>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = bids,
|
||||
has_one = asks
|
||||
)]
|
||||
pub perp_market: AccountLoader<'info, PerpMarket>,
|
||||
#[account(mut)]
|
||||
pub asks: UncheckedAccount<'info>,
|
||||
#[account(mut)]
|
||||
pub bids: UncheckedAccount<'info>,
|
||||
|
||||
pub owner: Signer<'info>,
|
||||
}
|
||||
|
||||
pub fn perp_cancel_order(ctx: Context<PerpCancelOrder>, order_id: i128) -> Result<()> {
|
||||
let mut mango_account = ctx.accounts.account.load_mut()?;
|
||||
require!(mango_account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||
|
||||
let perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||
let bids = &ctx.accounts.bids.to_account_info();
|
||||
let asks = &ctx.accounts.asks.to_account_info();
|
||||
let mut book = Book::load_mut(bids, asks, &perp_market)?;
|
||||
|
||||
let side = mango_account
|
||||
.perps
|
||||
.find_order_side(perp_market.perp_market_index, order_id)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?; // InvalidOrderId
|
||||
|
||||
let order = book.cancel_order(order_id, side)?;
|
||||
require!(
|
||||
order.owner == ctx.accounts.account.key(),
|
||||
MangoError::SomeError // InvalidOwner
|
||||
);
|
||||
|
||||
mango_account
|
||||
.perps
|
||||
.remove_order(order.owner_slot as usize, order.quantity)
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
use anchor_lang::prelude::*;
|
||||
|
||||
use crate::error::MangoError;
|
||||
use crate::state::{Book, Group, MangoAccount, PerpMarket};
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct PerpCancelOrderByClientOrderId<'info> {
|
||||
pub group: AccountLoader<'info, Group>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = owner,
|
||||
)]
|
||||
pub account: AccountLoader<'info, MangoAccount>,
|
||||
|
||||
#[account(
|
||||
mut,
|
||||
has_one = group,
|
||||
has_one = bids,
|
||||
has_one = asks
|
||||
)]
|
||||
pub perp_market: AccountLoader<'info, PerpMarket>,
|
||||
#[account(mut)]
|
||||
pub asks: UncheckedAccount<'info>,
|
||||
#[account(mut)]
|
||||
pub bids: UncheckedAccount<'info>,
|
||||
|
||||
pub owner: Signer<'info>,
|
||||
}
|
||||
|
||||
pub fn perp_cancel_order_by_client_order_id(
|
||||
ctx: Context<PerpCancelOrderByClientOrderId>,
|
||||
client_order_id: u64,
|
||||
) -> Result<()> {
|
||||
let mut mango_account = ctx.accounts.account.load_mut()?;
|
||||
require!(mango_account.is_bankrupt == 0, MangoError::IsBankrupt);
|
||||
|
||||
let perp_market = ctx.accounts.perp_market.load_mut()?;
|
||||
let bids = &ctx.accounts.bids.to_account_info();
|
||||
let asks = &ctx.accounts.asks.to_account_info();
|
||||
let mut book = Book::load_mut(bids, asks, &perp_market)?;
|
||||
|
||||
let (order_id, side) = mango_account
|
||||
.perps
|
||||
.find_order_with_client_order_id(perp_market.perp_market_index, client_order_id)
|
||||
.ok_or_else(|| error!(MangoError::SomeError))?;
|
||||
|
||||
let order = book.cancel_order(order_id, side)?;
|
||||
require!(
|
||||
order.owner == ctx.accounts.account.key(),
|
||||
MangoError::SomeError // InvalidOwner
|
||||
);
|
||||
|
||||
mango_account
|
||||
.perps
|
||||
.remove_order(order.owner_slot as usize, order.quantity)
|
||||
}
|
|
@ -70,6 +70,8 @@ pub mod mango_v4 {
|
|||
instructions::create_account(ctx, account_num, name)
|
||||
}
|
||||
|
||||
// TODO set delegate
|
||||
|
||||
pub fn close_account(ctx: Context<CloseAccount>) -> Result<()> {
|
||||
instructions::close_account(ctx)
|
||||
}
|
||||
|
@ -107,6 +109,8 @@ pub mod mango_v4 {
|
|||
/// Serum
|
||||
///
|
||||
|
||||
// TODO deposit/withdraw msrm
|
||||
|
||||
pub fn serum3_register_market(
|
||||
ctx: Context<Serum3RegisterMarket>,
|
||||
market_index: Serum3MarketIndex,
|
||||
|
@ -115,6 +119,8 @@ pub mod mango_v4 {
|
|||
instructions::serum3_register_market(ctx, market_index, name)
|
||||
}
|
||||
|
||||
// TODO serum3_change_spot_market_params
|
||||
|
||||
pub fn serum3_create_open_orders(ctx: Context<Serum3CreateOpenOrders>) -> Result<()> {
|
||||
instructions::serum3_create_open_orders(ctx)
|
||||
}
|
||||
|
@ -163,6 +169,8 @@ pub mod mango_v4 {
|
|||
instructions::serum3_liq_force_cancel_orders(ctx, limit)
|
||||
}
|
||||
|
||||
// TODO serum3_cancel_all_spot_orders
|
||||
|
||||
pub fn liq_token_with_token(
|
||||
ctx: Context<LiqTokenWithToken>,
|
||||
asset_token_index: TokenIndex,
|
||||
|
@ -216,6 +224,8 @@ pub mod mango_v4 {
|
|||
)
|
||||
}
|
||||
|
||||
// TODO perp_change_perp_market_params
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn perp_place_order(
|
||||
ctx: Context<PerpPlaceOrder>,
|
||||
|
@ -241,23 +251,35 @@ pub mod mango_v4 {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn perp_cancel_order(ctx: Context<PerpCancelOrder>, order_id: i128) -> Result<()> {
|
||||
instructions::perp_cancel_order(ctx, order_id)
|
||||
}
|
||||
|
||||
pub fn perp_cancel_order_by_client_order_id(
|
||||
ctx: Context<PerpCancelOrderByClientOrderId>,
|
||||
client_order_id: u64,
|
||||
) -> Result<()> {
|
||||
instructions::perp_cancel_order_by_client_order_id(ctx, client_order_id)
|
||||
}
|
||||
|
||||
pub fn perp_cancel_all_orders(ctx: Context<PerpCancelAllOrders>, limit: u8) -> Result<()> {
|
||||
instructions::perp_cancel_all_orders(ctx, limit)
|
||||
}
|
||||
|
||||
pub fn perp_cancel_all_orders_by_side(
|
||||
ctx: Context<PerpCancelAllOrdersBySide>,
|
||||
side_option: Option<Side>,
|
||||
limit: u8,
|
||||
) -> Result<()> {
|
||||
instructions::perp_cancel_all_orders_by_side(ctx, side_option, limit)
|
||||
}
|
||||
|
||||
pub fn perp_consume_events(ctx: Context<PerpConsumeEvents>, limit: usize) -> Result<()> {
|
||||
instructions::perp_consume_events(ctx, limit)
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
// set delegate
|
||||
|
||||
// serum3_cancel_all_spot_orders
|
||||
// serum3_change_spot_market_params
|
||||
|
||||
// msrm
|
||||
|
||||
// perp_cancel_order
|
||||
// perp_cancel_order_by_client_id
|
||||
// perp_cancel_all
|
||||
// perp_cancel_all_side
|
||||
// perp_force_cancel_order
|
||||
|
||||
// liquidate_token_and_perp
|
||||
|
@ -270,7 +292,7 @@ pub mod mango_v4 {
|
|||
///
|
||||
/// benchmark
|
||||
///
|
||||
///
|
||||
|
||||
pub fn benchmark(ctx: Context<Benchmark>) -> Result<()> {
|
||||
instructions::benchmark(ctx)
|
||||
}
|
||||
|
|
|
@ -277,7 +277,6 @@ impl MangoAccountSerum3 {
|
|||
}
|
||||
|
||||
#[zero_copy]
|
||||
#[derive(Debug)]
|
||||
pub struct PerpAccount {
|
||||
pub market_index: PerpMarketIndex,
|
||||
pub reserved: [u8; 6],
|
||||
|
@ -304,6 +303,20 @@ pub struct PerpAccount {
|
|||
pub taker_base_lots: i64,
|
||||
pub taker_quote_lots: i64,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PerpAccount {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PerpAccount")
|
||||
.field("market_index", &self.market_index)
|
||||
.field("base_position_lots", &self.base_position_lots)
|
||||
.field("quote_position_native", &self.quote_position_native)
|
||||
.field("bids_base_lots", &self.bids_base_lots)
|
||||
.field("asks_base_lots", &self.asks_base_lots)
|
||||
.field("taker_base_lots", &self.taker_base_lots)
|
||||
.field("taker_quote_lots", &self.taker_quote_lots)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
const_assert_eq!(size_of::<PerpAccount>(), 8 + 8 * 5 + 16);
|
||||
const_assert_eq!(size_of::<PerpAccount>() % 8, 0);
|
||||
|
||||
|
@ -366,7 +379,7 @@ pub struct MangoAccountPerps {
|
|||
pub order_market: [PerpMarketIndex; MAX_PERP_OPEN_ORDERS],
|
||||
pub order_side: [Side; MAX_PERP_OPEN_ORDERS], // TODO: storing enums isn't POD
|
||||
pub order_id: [i128; MAX_PERP_OPEN_ORDERS],
|
||||
pub order_client_id: [u64; MAX_PERP_OPEN_ORDERS],
|
||||
pub client_order_id: [u64; MAX_PERP_OPEN_ORDERS],
|
||||
}
|
||||
const_assert_eq!(
|
||||
size_of::<MangoAccountPerps>(),
|
||||
|
@ -412,9 +425,9 @@ impl std::fmt::Debug for MangoAccountPerps {
|
|||
.collect::<Vec<&i128>>(),
|
||||
)
|
||||
.field(
|
||||
"order_client_id",
|
||||
"client_order_id",
|
||||
&self
|
||||
.order_client_id
|
||||
.client_order_id
|
||||
.iter()
|
||||
.filter(|value| **value != 0)
|
||||
.collect::<Vec<&u64>>(),
|
||||
|
@ -430,7 +443,7 @@ impl MangoAccountPerps {
|
|||
order_market: [FREE_ORDER_SLOT; MAX_PERP_OPEN_ORDERS],
|
||||
order_side: [Side::Bid; MAX_PERP_OPEN_ORDERS],
|
||||
order_id: [0; MAX_PERP_OPEN_ORDERS],
|
||||
order_client_id: [0; MAX_PERP_OPEN_ORDERS],
|
||||
client_order_id: [0; MAX_PERP_OPEN_ORDERS],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -495,7 +508,7 @@ impl MangoAccountPerps {
|
|||
self.order_market[slot] = perp_market_index;
|
||||
self.order_side[slot] = side;
|
||||
self.order_id[slot] = order.key;
|
||||
self.order_client_id[slot] = order.client_order_id;
|
||||
self.client_order_id[slot] = order.client_order_id;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -521,10 +534,9 @@ impl MangoAccountPerps {
|
|||
// release space
|
||||
self.order_market[slot] = FREE_ORDER_SLOT;
|
||||
|
||||
// TODO OPT - remove these; unnecessary
|
||||
self.order_side[slot] = Side::Bid;
|
||||
self.order_id[slot] = 0i128;
|
||||
self.order_client_id[slot] = 0u64;
|
||||
self.client_order_id[slot] = 0u64;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -584,6 +596,28 @@ impl MangoAccountPerps {
|
|||
pa.quote_position_native += quote;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn find_order_with_client_order_id(
|
||||
&self,
|
||||
market_index: PerpMarketIndex,
|
||||
client_order_id: u64,
|
||||
) -> Option<(i128, Side)> {
|
||||
for i in 0..MAX_PERP_OPEN_ORDERS {
|
||||
if self.order_market[i] == market_index && self.client_order_id[i] == client_order_id {
|
||||
return Some((self.order_id[i], self.order_side[i]));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_order_side(&self, market_index: PerpMarketIndex, order_id: i128) -> Option<Side> {
|
||||
for i in 0..MAX_PERP_OPEN_ORDERS {
|
||||
if self.order_market[i] == market_index && self.order_id[i] == order_id {
|
||||
return Some(self.order_side[i]);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MangoAccountPerps {
|
||||
|
|
|
@ -4,7 +4,8 @@ use crate::{
|
|||
error::MangoError,
|
||||
state::{
|
||||
orderbook::{bookside::BookSide, nodes::LeafNode},
|
||||
EventQueue, MangoAccountPerps, PerpMarket,
|
||||
EventQueue, MangoAccount, MangoAccountPerps, PerpMarket, FREE_ORDER_SLOT,
|
||||
MAX_PERP_OPEN_ORDERS,
|
||||
},
|
||||
util::LoadZeroCopy,
|
||||
};
|
||||
|
@ -306,6 +307,7 @@ impl<'a> Book<'a> {
|
|||
|
||||
// If there are still quantity unmatched, place on the book
|
||||
let book_base_quantity = remaining_base_lots.min(remaining_quote_lots / price_lots);
|
||||
msg!("{:?}", post_allowed);
|
||||
if post_allowed && book_base_quantity > 0 {
|
||||
// Drop an expired order if possible
|
||||
let bookside = self.get_bookside(side);
|
||||
|
@ -378,6 +380,57 @@ impl<'a> Book<'a> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cancel_all_order(
|
||||
&mut self,
|
||||
mango_account: &mut MangoAccount,
|
||||
perp_market: &mut PerpMarket,
|
||||
mut limit: u8,
|
||||
side_to_cancel_option: Option<Side>,
|
||||
) -> Result<()> {
|
||||
for i in 0..MAX_PERP_OPEN_ORDERS {
|
||||
if mango_account.perps.order_market[i] == FREE_ORDER_SLOT
|
||||
|| mango_account.perps.order_market[i] != perp_market.perp_market_index
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let order_id = mango_account.perps.order_id[i];
|
||||
let order_side = mango_account.perps.order_side[i];
|
||||
|
||||
if let Some(side_to_cancel) = side_to_cancel_option {
|
||||
if side_to_cancel != order_side {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(leaf_node) = self.cancel_order(order_id, order_side) {
|
||||
mango_account
|
||||
.perps
|
||||
.remove_order(leaf_node.owner_slot as usize, leaf_node.quantity)?
|
||||
};
|
||||
|
||||
limit = limit - 1;
|
||||
if limit == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn cancel_order(&mut self, order_id: i128, side: Side) -> Result<LeafNode> {
|
||||
match side {
|
||||
Side::Bid => self
|
||||
.bids
|
||||
.remove_by_key(order_id)
|
||||
.ok_or_else(|| error!(MangoError::SomeError)), // InvalidOrderId
|
||||
Side::Ask => self
|
||||
.asks
|
||||
.remove_by_key(order_id)
|
||||
.ok_or_else(|| error!(MangoError::SomeError)), // InvalidOrderId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply taker fees to the taker account and update the markets' fees_accrued for
|
||||
|
|
|
@ -1356,6 +1356,7 @@ pub struct PerpPlaceOrderInstruction<'keypair> {
|
|||
pub price_lots: i64,
|
||||
pub max_base_lots: i64,
|
||||
pub max_quote_lots: i64,
|
||||
pub client_order_id: u64,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl<'keypair> ClientInstruction for PerpPlaceOrderInstruction<'keypair> {
|
||||
|
@ -1371,7 +1372,7 @@ impl<'keypair> ClientInstruction for PerpPlaceOrderInstruction<'keypair> {
|
|||
price_lots: self.price_lots,
|
||||
max_base_lots: self.max_base_lots,
|
||||
max_quote_lots: self.max_quote_lots,
|
||||
client_order_id: u64::MAX,
|
||||
client_order_id: self.client_order_id,
|
||||
order_type: OrderType::Limit,
|
||||
expiry_timestamp: 0,
|
||||
limit: 1,
|
||||
|
@ -1395,6 +1396,121 @@ impl<'keypair> ClientInstruction for PerpPlaceOrderInstruction<'keypair> {
|
|||
vec![self.owner]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerpCancelOrderInstruction<'keypair> {
|
||||
pub group: Pubkey,
|
||||
pub account: Pubkey,
|
||||
pub perp_market: Pubkey,
|
||||
pub asks: Pubkey,
|
||||
pub bids: Pubkey,
|
||||
pub owner: &'keypair Keypair,
|
||||
pub order_id: i128,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl<'keypair> ClientInstruction for PerpCancelOrderInstruction<'keypair> {
|
||||
type Accounts = mango_v4::accounts::PerpCancelOrder;
|
||||
type Instruction = mango_v4::instruction::PerpCancelOrder;
|
||||
async fn to_instruction(
|
||||
&self,
|
||||
_loader: impl ClientAccountLoader + 'async_trait,
|
||||
) -> (Self::Accounts, instruction::Instruction) {
|
||||
let program_id = mango_v4::id();
|
||||
let instruction = Self::Instruction {
|
||||
order_id: self.order_id,
|
||||
};
|
||||
let accounts = Self::Accounts {
|
||||
group: self.group,
|
||||
account: self.account,
|
||||
perp_market: self.perp_market,
|
||||
asks: self.asks,
|
||||
bids: self.bids,
|
||||
owner: self.owner.pubkey(),
|
||||
};
|
||||
|
||||
let instruction = make_instruction(program_id, &accounts, instruction);
|
||||
(accounts, instruction)
|
||||
}
|
||||
|
||||
fn signers(&self) -> Vec<&Keypair> {
|
||||
vec![self.owner]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerpCancelOrderByClientOrderIdInstruction<'keypair> {
|
||||
pub group: Pubkey,
|
||||
pub account: Pubkey,
|
||||
pub perp_market: Pubkey,
|
||||
pub asks: Pubkey,
|
||||
pub bids: Pubkey,
|
||||
pub owner: &'keypair Keypair,
|
||||
pub client_order_id: u64,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl<'keypair> ClientInstruction for PerpCancelOrderByClientOrderIdInstruction<'keypair> {
|
||||
type Accounts = mango_v4::accounts::PerpCancelOrderByClientOrderId;
|
||||
type Instruction = mango_v4::instruction::PerpCancelOrderByClientOrderId;
|
||||
async fn to_instruction(
|
||||
&self,
|
||||
_loader: impl ClientAccountLoader + 'async_trait,
|
||||
) -> (Self::Accounts, instruction::Instruction) {
|
||||
let program_id = mango_v4::id();
|
||||
let instruction = Self::Instruction {
|
||||
client_order_id: self.client_order_id,
|
||||
};
|
||||
let accounts = Self::Accounts {
|
||||
group: self.group,
|
||||
account: self.account,
|
||||
perp_market: self.perp_market,
|
||||
asks: self.asks,
|
||||
bids: self.bids,
|
||||
owner: self.owner.pubkey(),
|
||||
};
|
||||
|
||||
let instruction = make_instruction(program_id, &accounts, instruction);
|
||||
(accounts, instruction)
|
||||
}
|
||||
|
||||
fn signers(&self) -> Vec<&Keypair> {
|
||||
vec![self.owner]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerpCancelAllOrdersInstruction<'keypair> {
|
||||
pub group: Pubkey,
|
||||
pub account: Pubkey,
|
||||
pub perp_market: Pubkey,
|
||||
pub asks: Pubkey,
|
||||
pub bids: Pubkey,
|
||||
pub owner: &'keypair Keypair,
|
||||
}
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl<'keypair> ClientInstruction for PerpCancelAllOrdersInstruction<'keypair> {
|
||||
type Accounts = mango_v4::accounts::PerpCancelAllOrders;
|
||||
type Instruction = mango_v4::instruction::PerpCancelAllOrders;
|
||||
async fn to_instruction(
|
||||
&self,
|
||||
_loader: impl ClientAccountLoader + 'async_trait,
|
||||
) -> (Self::Accounts, instruction::Instruction) {
|
||||
let program_id = mango_v4::id();
|
||||
let instruction = Self::Instruction { limit: 5 };
|
||||
let accounts = Self::Accounts {
|
||||
group: self.group,
|
||||
account: self.account,
|
||||
perp_market: self.perp_market,
|
||||
asks: self.asks,
|
||||
bids: self.bids,
|
||||
owner: self.owner.pubkey(),
|
||||
};
|
||||
|
||||
let instruction = make_instruction(program_id, &accounts, instruction);
|
||||
(accounts, instruction)
|
||||
}
|
||||
|
||||
fn signers(&self) -> Vec<&Keypair> {
|
||||
vec![self.owner]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PerpConsumeEventsInstruction {
|
||||
pub group: Pubkey,
|
||||
pub perp_market: Pubkey,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![cfg(all(feature = "test-bpf"))]
|
||||
|
||||
use anchor_lang::prelude::Pubkey;
|
||||
use fixed_macro::types::I80F48;
|
||||
use mango_v4::state::*;
|
||||
use program_test::*;
|
||||
|
@ -168,6 +169,9 @@ async fn test_perp() -> Result<(), BanksClientError> {
|
|||
perp_market.native_price_to_lot(I80F48!(1))
|
||||
};
|
||||
|
||||
//
|
||||
// Place and cancel order with order_id
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
|
@ -183,6 +187,174 @@ async fn test_perp() -> Result<(), BanksClientError> {
|
|||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 0,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let order_id_to_cancel = solana
|
||||
.get_account::<MangoAccount>(account_0)
|
||||
.await
|
||||
.perps
|
||||
.order_id[0];
|
||||
send_tx(
|
||||
solana,
|
||||
PerpCancelOrderInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
owner,
|
||||
order_id: order_id_to_cancel,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_no_perp_orders(solana, account_0).await;
|
||||
|
||||
//
|
||||
// Place and cancel order with client_order_id
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: tokens[0].oracle,
|
||||
owner,
|
||||
side: Side::Bid,
|
||||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
PerpCancelOrderByClientOrderIdInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
owner,
|
||||
client_order_id: 1,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_no_perp_orders(solana, account_0).await;
|
||||
|
||||
//
|
||||
// Place and cancel all orders
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: tokens[0].oracle,
|
||||
owner,
|
||||
side: Side::Bid,
|
||||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 2,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: tokens[0].oracle,
|
||||
owner,
|
||||
side: Side::Bid,
|
||||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 3,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: tokens[0].oracle,
|
||||
owner,
|
||||
side: Side::Bid,
|
||||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 4,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
PerpCancelAllOrdersInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
owner,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_no_perp_orders(solana, account_0).await;
|
||||
|
||||
//
|
||||
// Place a bid, corresponding ask, and consume event
|
||||
//
|
||||
send_tx(
|
||||
solana,
|
||||
PerpPlaceOrderInstruction {
|
||||
group,
|
||||
account: account_0,
|
||||
perp_market,
|
||||
asks,
|
||||
bids,
|
||||
event_queue,
|
||||
oracle: tokens[0].oracle,
|
||||
owner,
|
||||
side: Side::Bid,
|
||||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 5,
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
@ -203,6 +375,7 @@ async fn test_perp() -> Result<(), BanksClientError> {
|
|||
price_lots,
|
||||
max_base_lots: 1,
|
||||
max_quote_lots: i64::MAX,
|
||||
client_order_id: 6,
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
@ -230,3 +403,14 @@ async fn test_perp() -> Result<(), BanksClientError> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn assert_no_perp_orders(solana: &SolanaCookie, account_0: Pubkey) {
|
||||
let mango_account_0 = solana.get_account::<MangoAccount>(account_0).await;
|
||||
|
||||
for i in 0..MAX_PERP_OPEN_ORDERS {
|
||||
assert!(mango_account_0.perps.order_id[i] == 0);
|
||||
assert!(mango_account_0.perps.order_side[i] == Side::Bid);
|
||||
assert!(mango_account_0.perps.client_order_id[i] == 0);
|
||||
assert!(mango_account_0.perps.order_market[i] == FREE_ORDER_SLOT);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue