perp cancel order instructions

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2022-05-16 10:34:22 +02:00
parent 67c1d5d451
commit db82fcbbcc
10 changed files with 644 additions and 22 deletions

View File

@ -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;

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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);
}
}