diff --git a/dex/crank/src/lib.rs b/dex/crank/src/lib.rs index 9e96b85a..297b4bd6 100644 --- a/dex/crank/src/lib.rs +++ b/dex/crank/src/lib.rs @@ -42,8 +42,8 @@ use serum_common::client::Cluster; use serum_context::Context; use serum_dex::instruction::{ cancel_order_by_client_order_id as cancel_order_by_client_order_id_ix, - close_open_orders as close_open_orders_ix, MarketInstruction, NewOrderInstructionV3, - SelfTradeBehavior, + close_open_orders as close_open_orders_ix, init_open_orders as init_open_orders_ix, + MarketInstruction, NewOrderInstructionV3, SelfTradeBehavior, }; use serum_dex::matching::{OrderType, Side}; use serum_dex::state::gen_vault_signer_key; @@ -850,8 +850,12 @@ fn whole_shebang(client: &RpcClient, program_id: &Pubkey, payer: &Keypair) -> Re )?; debug_println!("Minted {}", pc_wallet.pubkey()); - debug_println!("Placing bid..."); let mut orders = None; + + debug_println!("Initializing open orders"); + init_open_orders(client, program_id, payer, &market_keys, &mut orders)?; + + debug_println!("Placing bid..."); place_order( client, program_id, @@ -997,6 +1001,51 @@ pub fn close_open_orders( Ok(()) } +pub fn init_open_orders( + client: &RpcClient, + program_id: &Pubkey, + owner: &Keypair, + state: &MarketPubkeys, + orders: &mut Option, +) -> Result<()> { + let mut instructions = Vec::new(); + let orders_keypair; + let mut signers = Vec::new(); + let orders_pubkey = match *orders { + Some(pk) => pk, + None => { + let (orders_key, instruction) = create_dex_account( + client, + program_id, + &owner.pubkey(), + size_of::(), + )?; + orders_keypair = orders_key; + signers.push(&orders_keypair); + instructions.push(instruction); + orders_keypair.pubkey() + } + }; + *orders = Some(orders_pubkey); + instructions.push(init_open_orders_ix( + program_id, + &orders_pubkey, + &owner.pubkey(), + &state.market, + )?); + signers.push(owner); + + let (recent_hash, _fee_calc) = client.get_recent_blockhash()?; + let txn = Transaction::new_signed_with_payer( + &instructions, + Some(&owner.pubkey()), + &signers, + recent_hash, + ); + send_txn(client, &txn, false)?; + Ok(()) +} + pub fn place_order( client: &RpcClient, program_id: &Pubkey, diff --git a/dex/fuzz/fuzz_targets/multiple_orders.rs b/dex/fuzz/fuzz_targets/multiple_orders.rs index 40015d66..40a69e78 100644 --- a/dex/fuzz/fuzz_targets/multiple_orders.rs +++ b/dex/fuzz/fuzz_targets/multiple_orders.rs @@ -45,6 +45,9 @@ enum Action { ConsumeEvents(u16), SettleFunds(OwnerId, Option), SweepFees, + InitOpenOrders { + owner_id: OwnerId, + }, CloseOpenOrders { owner_id: OwnerId, }, @@ -585,6 +588,29 @@ fn run_action<'bump>( ) .unwrap(); } + + Action::InitOpenOrders { owner_id } => { + let owner = owners + .entry(owner_id) + .or_insert_with(|| Owner::new(&market_accounts, &bump)); + process_instruction( + market_accounts.market.owner, + &[ + owner.orders_account.clone(), + owner.signer_account.clone(), + market_accounts.market.clone(), + market_accounts.rent_sysvar.clone(), + ], + &MarketInstruction::InitOpenOrders.pack(), + ) + .map_err(|e| match e { + DexError::ErrorCode(DexErrorCode::WrongOrdersAccount) + if owner.closed_open_orders => {} + e => Err(e).unwrap(), + }) + .ok(); + } + Action::CloseOpenOrders { owner_id } => { let owner = owners .entry(owner_id) diff --git a/dex/src/instruction.rs b/dex/src/instruction.rs index d87dad78..b7dc0ce3 100644 --- a/dex/src/instruction.rs +++ b/dex/src/instruction.rs @@ -438,6 +438,11 @@ pub enum MarketInstruction { /// 2. `[writable]` the destination account to send rent exemption SOL to /// 3. `[]` market CloseOpenOrders, + /// 0. `[writable]` OpenOrders + /// 1. `[signer]` the OpenOrders owner + /// 2. `[]` market + /// 3. `[]` the rent sysvar + InitOpenOrders, } impl MarketInstruction { @@ -530,6 +535,7 @@ impl MarketInstruction { SendTakeInstruction::unpack(data_arr)? }), (14, 0) => MarketInstruction::CloseOpenOrders, + (15, 0) => MarketInstruction::InitOpenOrders, _ => return None, }) } @@ -873,6 +879,26 @@ pub fn close_open_orders( }) } +pub fn init_open_orders( + program_id: &Pubkey, + open_orders: &Pubkey, + owner: &Pubkey, + market: &Pubkey, +) -> Result { + let data = MarketInstruction::InitOpenOrders.pack(); + let accounts: Vec = vec![ + AccountMeta::new(*open_orders, false), + AccountMeta::new_readonly(*owner, true), + AccountMeta::new_readonly(*market, false), + AccountMeta::new_readonly(rent::ID, false), + ]; + Ok(Instruction { + program_id: *program_id, + data, + accounts, + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/dex/src/state.rs b/dex/src/state.rs index 8ad18b2d..99e1c8e8 100644 --- a/dex/src/state.rs +++ b/dex/src/state.rs @@ -2048,6 +2048,45 @@ pub(crate) mod account_parser { }) } } + + pub struct InitOpenOrdersArgs; + + impl InitOpenOrdersArgs { + pub fn with_parsed_args( + program_id: &Pubkey, + accounts: &[AccountInfo], + f: impl FnOnce(InitOpenOrdersArgs) -> DexResult, + ) -> DexResult { + // Parse accounts. + check_assert_eq!(accounts.len(), 4)?; + #[rustfmt::skip] + let &[ + ref open_orders_acc, + ref owner_acc, + ref market_acc, + ref rent_acc, + ] = array_ref![accounts, 0, 4]; + + // Validate the accounts given are valid. + let rent = { + let rent_sysvar = RentSysvarAccount::new(rent_acc)?; + Rent::from_account_info(rent_sysvar.inner()).or(check_unreachable!())? + }; + let owner = SignerAccount::new(owner_acc)?; + let market = MarketState::load(market_acc, program_id)?; + + // Perform open orders initialization. + let _open_orders = market.load_orders_mut( + open_orders_acc, + Some(owner.inner()), + program_id, + Some(rent), + )?; + + // Invoke processor. + f(InitOpenOrdersArgs) + } + } } #[inline] @@ -2150,6 +2189,13 @@ impl State { Self::process_close_open_orders, )? } + MarketInstruction::InitOpenOrders => { + account_parser::InitOpenOrdersArgs::with_parsed_args( + program_id, + accounts, + Self::process_init_open_orders, + )? + } }; Ok(()) } @@ -2159,6 +2205,10 @@ impl State { unimplemented!() } + fn process_init_open_orders(_args: account_parser::InitOpenOrdersArgs) -> DexResult { + Ok(()) + } + fn process_close_open_orders(args: account_parser::CloseOpenOrdersArgs) -> DexResult { let account_parser::CloseOpenOrdersArgs { open_orders,