Perp cancel all: Don't error when orders are filled/expired (#453)
This commit is contained in:
parent
02d980f4e4
commit
f6abd9579d
|
@ -93,6 +93,8 @@ pub enum MangoError {
|
|||
IxIsDisabled,
|
||||
#[msg("no liquidatable perp base position")]
|
||||
NoLiquidatablePerpBasePosition,
|
||||
#[msg("perp order id not found on the orderbook")]
|
||||
PerpOrderIdNotFound,
|
||||
}
|
||||
|
||||
impl MangoError {
|
||||
|
@ -101,6 +103,19 @@ impl MangoError {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait IsAnchorErrorWithCode {
|
||||
fn is_anchor_error_with_code(&self, code: u32) -> bool;
|
||||
}
|
||||
|
||||
impl<T> IsAnchorErrorWithCode for anchor_lang::Result<T> {
|
||||
fn is_anchor_error_with_code(&self, code: u32) -> bool {
|
||||
match self {
|
||||
Err(Error::AnchorError(error)) => error.error_code_number == code,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Contextable {
|
||||
/// Add a context string `c` to a Result or Error
|
||||
///
|
||||
|
|
|
@ -215,16 +215,12 @@ fn can_load_as<'a, T: ZeroCopy + Owner>(
|
|||
(i, ai): (usize, &'a AccountInfo),
|
||||
) -> Option<(usize, Result<Ref<'a, T>>)> {
|
||||
let load_result = ai.load::<T>();
|
||||
match load_result {
|
||||
Err(Error::AnchorError(error))
|
||||
if error.error_code_number == ErrorCode::AccountDiscriminatorMismatch as u32
|
||||
|| error.error_code_number == ErrorCode::AccountDiscriminatorNotFound as u32
|
||||
|| error.error_code_number == ErrorCode::AccountOwnedByWrongProgram as u32 =>
|
||||
if load_result.is_anchor_error_with_code(ErrorCode::AccountDiscriminatorMismatch.into())
|
||||
|| load_result.is_anchor_error_with_code(ErrorCode::AccountDiscriminatorNotFound.into())
|
||||
|| load_result.is_anchor_error_with_code(ErrorCode::AccountOwnedByWrongProgram.into())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Some((i, load_result))
|
||||
}
|
||||
|
||||
|
|
|
@ -579,17 +579,12 @@ fn find_maximum(
|
|||
}
|
||||
|
||||
fn ignore_net_borrow_limit_errors(maybe_cache: Result<HealthCache>) -> Result<Option<HealthCache>> {
|
||||
match maybe_cache {
|
||||
Ok(cache) => Ok(Some(cache)),
|
||||
// Special case net borrow errors: We want to be able to find a good
|
||||
// swap amount even if the max swap is limited by the net borrow limit.
|
||||
Err(Error::AnchorError(err))
|
||||
if err.error_code_number == MangoError::BankNetBorrowsLimitReached.error_code() =>
|
||||
{
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
if maybe_cache.is_anchor_error_with_code(MangoError::BankNetBorrowsLimitReached.error_code()) {
|
||||
return Ok(None);
|
||||
}
|
||||
maybe_cache.map(|c| Some(c))
|
||||
}
|
||||
|
||||
fn system_epoch_secs() -> u64 {
|
||||
|
|
|
@ -51,7 +51,7 @@ pub fn perp_cancel_order(ctx: Context<PerpCancelOrder>, order_id: u128) -> Resul
|
|||
let oo = account
|
||||
.perp_find_order_with_order_id(perp_market.perp_market_index, order_id)
|
||||
.ok_or_else(|| {
|
||||
error_msg!("could not find perp order with id {order_id} in perp market orderbook")
|
||||
error_msg!("could not find perp order with id {order_id} in user account")
|
||||
})?;
|
||||
let order_id = oo.id;
|
||||
let order_side_and_tree = oo.side_and_tree();
|
||||
|
|
|
@ -52,7 +52,11 @@ pub fn perp_cancel_order_by_client_order_id(
|
|||
|
||||
let oo = account
|
||||
.perp_find_order_with_client_order_id(perp_market.perp_market_index, client_order_id)
|
||||
.ok_or_else(|| error_msg!("could not find perp order with client order id {client_order_id} in perp order books"))?;
|
||||
.ok_or_else(|| {
|
||||
error_msg!(
|
||||
"could not find perp order with client order id {client_order_id} in user account"
|
||||
)
|
||||
})?;
|
||||
let order_id = oo.id;
|
||||
let order_side_and_tree = oo.side_and_tree();
|
||||
|
||||
|
|
|
@ -54,13 +54,12 @@ pub fn perp_liq_force_cancel_orders(
|
|||
}
|
||||
} else {
|
||||
// Frozen accounts can always have their orders cancelled
|
||||
if let Err(Error::AnchorError(ref inner)) = result {
|
||||
if inner.error_code_number != MangoError::HealthMustBeNegative as u32 {
|
||||
if !result.is_anchor_error_with_code(MangoError::HealthMustBeNegative.into()) {
|
||||
// Propagate unexpected errors
|
||||
result?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
health_cache
|
||||
};
|
||||
|
|
|
@ -133,14 +133,12 @@ pub fn serum3_liq_force_cancel_orders(
|
|||
}
|
||||
} else {
|
||||
// Frozen accounts can always have their orders cancelled
|
||||
if let Err(Error::AnchorError(ref inner)) = result {
|
||||
if inner.error_code_number != MangoError::HealthMustBeNegative as u32 {
|
||||
// propagate all unexpected errors
|
||||
if !result.is_anchor_error_with_code(MangoError::HealthMustBeNegative.into()) {
|
||||
// Propagate unexpected errors
|
||||
result?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
health_cache
|
||||
};
|
||||
|
|
|
@ -301,7 +301,19 @@ impl<'a> Orderbook<'a> {
|
|||
|
||||
let order_id = oo.id;
|
||||
|
||||
self.cancel_order(mango_account, order_id, order_side_and_tree, None)?;
|
||||
let cancel_result =
|
||||
self.cancel_order(mango_account, order_id, order_side_and_tree, None);
|
||||
if cancel_result.is_anchor_error_with_code(MangoError::PerpOrderIdNotFound.into()) {
|
||||
// It's possible for the order to be filled or expired already.
|
||||
// There will be an event on the queue, the perp order slot is freed once
|
||||
// it is processed.
|
||||
msg!(
|
||||
"order {} was not found on orderbook, expired or filled already",
|
||||
order_id
|
||||
);
|
||||
} else {
|
||||
cancel_result?;
|
||||
}
|
||||
|
||||
limit -= 1;
|
||||
if limit == 0 {
|
||||
|
@ -324,7 +336,8 @@ impl<'a> Orderbook<'a> {
|
|||
let book_component = side_and_tree.order_tree();
|
||||
let leaf_node = self.bookside_mut(side).
|
||||
remove_by_key(book_component, order_id).ok_or_else(|| {
|
||||
error_msg!("invalid perp order id {order_id} for side {side:?} and component {book_component:?}")
|
||||
// possibly already filled or expired?
|
||||
error_msg_typed!(MangoError::PerpOrderIdNotFound, "no perp order with id {order_id}, side {side:?}, component {book_component:?} found on the orderbook")
|
||||
})?;
|
||||
if let Some(owner) = expected_owner {
|
||||
require_keys_eq!(leaf_node.owner, owner);
|
||||
|
|
|
@ -268,6 +268,18 @@ async fn test_perp_fixed() -> Result<(), TransportError> {
|
|||
.unwrap();
|
||||
check_prev_instruction_post_health(&solana, account_1).await;
|
||||
|
||||
// Trying to cancel-all after the order was already taken: has no effect but succeeds
|
||||
send_tx(
|
||||
solana,
|
||||
PerpCancelAllOrdersInstruction {
|
||||
account: account_0,
|
||||
perp_market,
|
||||
owner,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
send_tx(
|
||||
solana,
|
||||
PerpConsumeEventsInstruction {
|
||||
|
|
Loading…
Reference in New Issue