From 409b6e7ea933c9cdd9c88934bcfa3d9e623311d3 Mon Sep 17 00:00:00 2001 From: Eoin Murphy Date: Thu, 21 Dec 2023 12:25:52 +0000 Subject: [PATCH] fix: ensure market position is paid out before closing This is just in case there is ever a bug that allows markets to achieve the status of ReadyToClose but with market positions that haven't been paid out yet. Unlikely unless there is a bug, so this is purely defensive. --- programs/monaco_protocol/src/error.rs | 2 + .../monaco_protocol/src/instructions/close.rs | 55 +++++++++++++++++++ programs/monaco_protocol/src/lib.rs | 5 +- 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/programs/monaco_protocol/src/error.rs b/programs/monaco_protocol/src/error.rs index dadc28e4..29ee7674 100644 --- a/programs/monaco_protocol/src/error.rs +++ b/programs/monaco_protocol/src/error.rs @@ -277,6 +277,8 @@ pub enum CoreError { */ #[msg("CloseAccount: Order not complete")] CloseAccountOrderNotComplete, + #[msg("CloseAccount: MarketPosition not paid")] + CloseAccountMarketPositionNotPaid, #[msg("CloseAccount: Purchaser does not match")] CloseAccountPurchaserMismatch, #[msg("CloseAccount: Payer does not match")] diff --git a/programs/monaco_protocol/src/instructions/close.rs b/programs/monaco_protocol/src/instructions/close.rs index c2c8a03d..ed041d2a 100644 --- a/programs/monaco_protocol/src/instructions/close.rs +++ b/programs/monaco_protocol/src/instructions/close.rs @@ -3,6 +3,7 @@ use anchor_lang::prelude::*; use crate::error::CoreError; use crate::state::market_account::MarketStatus::ReadyToClose; use crate::state::market_account::{Market, MarketStatus}; +use crate::state::market_position_account::MarketPosition; use crate::state::order_account::Order; pub fn close_market_child_account(market: &mut Market) -> Result<()> { @@ -21,6 +22,14 @@ pub fn close_order(market: &mut Market, order: &Order) -> Result<()> { close_market_child_account(market) } +pub fn close_market_position(market: &mut Market, market_position: &MarketPosition) -> Result<()> { + require!( + market_position.paid, + CoreError::CloseAccountMarketPositionNotPaid + ); + close_market_child_account(market) +} + pub fn close_market( market_status: &MarketStatus, payment_queue_len: u32, @@ -99,6 +108,39 @@ mod tests { assert_eq!(Err(error!(CoreError::CloseAccountOrderNotComplete)), result); } + // close market_position validation + + #[test] + fn test_close_market_position() { + let market = &mut test_market(); + market.market_status = ReadyToClose; + market.unclosed_accounts_count = 1; + + let market_position = &mut test_market_position(); + market_position.paid = true; + + assert!(close_market_position(market, market_position).is_ok()); + assert_eq!(market.unclosed_accounts_count, 0); + } + + #[test] + fn test_close_market_position_not_paid() { + let market = &mut test_market(); + market.market_status = ReadyToClose; + market.unclosed_accounts_count = 1; + + let market_position = &mut test_market_position(); + market_position.paid = false; + + let result = close_market_position(market, market_position); + assert!(result.is_err()); + assert_eq!( + Err(error!(CoreError::CloseAccountMarketPositionNotPaid)), + result + ); + assert_eq!(market.unclosed_accounts_count, 1); + } + // close market validation #[test] @@ -182,4 +224,17 @@ mod tests { product_commission_rate: 0.0, } } + + fn test_market_position() -> MarketPosition { + MarketPosition { + purchaser: Default::default(), + market: Default::default(), + paid: false, + market_outcome_sums: vec![], + unmatched_exposures: vec![], + payer: Default::default(), + matched_risk: 0, + matched_risk_per_product: vec![], + } + } } diff --git a/programs/monaco_protocol/src/lib.rs b/programs/monaco_protocol/src/lib.rs index e748e452..567bd9d2 100644 --- a/programs/monaco_protocol/src/lib.rs +++ b/programs/monaco_protocol/src/lib.rs @@ -571,7 +571,10 @@ pub mod monaco_protocol { } pub fn close_market_position(ctx: Context) -> Result<()> { - instructions::close::close_market_child_account(&mut ctx.accounts.market) + instructions::close::close_market_position( + &mut ctx.accounts.market, + &ctx.accounts.market_position, + ) } pub fn close_market_matching_pool(ctx: Context) -> Result<()> {