Skip to content

Commit

Permalink
dex: Close open orders accounts (#112)
Browse files Browse the repository at this point in the history
* dex: Close open orders accounts

* Zero out open orders account data on close

* Fix length assertion

* Remove token account parsing

* Remove rent from close ix

* Add close open orders to whole shebang

* Add close open orders to fuzz test

* Fix fuzz

* Update u128

* Cancel orders in whole shebang

* Update docker iamge

* travis: Update solana cli

* json compact output

* Add error logging and move free slot bits check

* Add Closed AccountFlag

* Replace .bits() with as u64 cast

* Skip errors instead of removing owner from set

* Fix derive ordering

* Skip WrongOrdersAccount error if account is closed

Co-authored-by: Sebastian Conybeare <sebastian@alameda-research.com>
  • Loading branch information
armaniferrante and Sebastian Conybeare authored May 31, 2021
1 parent 60f9a32 commit 2d4a7a9
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ _localnet: &localnet
- 14
before_script:
- sudo apt-get install -y pkg-config build-essential libudev-dev
- sh -c "$(curl -sSfL https://release.solana.com/v1.5.5/install)"
- sh -c "$(curl -sSfL https://release.solana.com/v1.6.9/install)"
- export PATH="/home/travis/.local/share/solana/install/active_release/bin:$PATH"

jobs:
Expand Down
84 changes: 83 additions & 1 deletion dex/crank/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ use serum_common::client::rpc::{
};
use serum_common::client::Cluster;
use serum_context::Context;
use serum_dex::instruction::{MarketInstruction, NewOrderInstructionV3, SelfTradeBehavior};
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,
};
use serum_dex::matching::{OrderType, Side};
use serum_dex::state::gen_vault_signer_key;
use serum_dex::state::Event;
Expand Down Expand Up @@ -890,6 +894,16 @@ fn whole_shebang(client: &RpcClient, program_id: &Pubkey, payer: &Keypair) -> Re
},
)?;

// Cancel the open order so that we can close it later.
cancel_order_by_client_order_id(
client,
program_id,
payer,
&market_keys,
&orders.unwrap(),
985982,
)?;

debug_println!("Ask account: {}", orders.unwrap());

debug_println!("Consuming events in 15s ...");
Expand All @@ -912,6 +926,74 @@ fn whole_shebang(client: &RpcClient, program_id: &Pubkey, payer: &Keypair) -> Re
&coin_wallet.pubkey(),
&pc_wallet.pubkey(),
)?;
close_open_orders(
client,
program_id,
payer,
&market_keys,
orders.as_ref().unwrap(),
)?;
Ok(())
}

pub fn cancel_order_by_client_order_id(
client: &RpcClient,
program_id: &Pubkey,
owner: &Keypair,
state: &MarketPubkeys,
orders: &Pubkey,
client_order_id: u64,
) -> Result<()> {
let ixs = &[cancel_order_by_client_order_id_ix(
program_id,
&state.market,
&state.bids,
&state.asks,
orders,
&owner.pubkey(),
&state.event_q,
client_order_id,
)?];
let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
let txn = Transaction::new_signed_with_payer(ixs, Some(&owner.pubkey()), &[owner], recent_hash);

debug_println!("Canceling order by client order id instruction ...");
let result = simulate_transaction(client, &txn, true, CommitmentConfig::confirmed())?;
if let Some(e) = result.value.err {
debug_println!("{:#?}", result.value.logs);
return Err(format_err!("simulate_transaction error: {:?}", e));
}

send_txn(client, &txn, false)?;
Ok(())
}

pub fn close_open_orders(
client: &RpcClient,
program_id: &Pubkey,
owner: &Keypair,
state: &MarketPubkeys,
orders: &Pubkey,
) -> Result<()> {
debug_println!("Closing open orders...");
let ixs = &[close_open_orders_ix(
program_id,
orders,
&owner.pubkey(),
&owner.pubkey(),
&state.market,
)?];
let (recent_hash, _fee_calc) = client.get_recent_blockhash()?;
let txn = Transaction::new_signed_with_payer(ixs, Some(&owner.pubkey()), &[owner], recent_hash);

debug_println!("Simulating close open orders instruction ...");
let result = simulate_transaction(client, &txn, true, CommitmentConfig::confirmed())?;
if let Some(e) = result.value.err {
debug_println!("{:#?}", result.value.logs);
return Err(format_err!("simulate_transaction error: {:?}", e));
}

send_txn(client, &txn, false)?;
Ok(())
}

Expand Down
64 changes: 59 additions & 5 deletions dex/fuzz/fuzz_targets/multiple_orders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use serum_dex::matching::Side;
use serum_dex::state::{strip_header, MarketState, OpenOrders, ToAlignedBytes};
use serum_dex_fuzz::{
get_token_account_balance, new_dex_owned_account_with_lamports, new_sol_account,
new_token_account, process_instruction, setup_market, MarketAccounts, COIN_LOT_SIZE,
PC_LOT_SIZE, NoSolLoggingStubs,
new_token_account, process_instruction, setup_market, MarketAccounts, NoSolLoggingStubs,
COIN_LOT_SIZE, PC_LOT_SIZE,
};

#[derive(Debug, Arbitrary, Clone)]
Expand All @@ -45,6 +45,9 @@ enum Action {
ConsumeEvents(u16),
SettleFunds(OwnerId, Option<ReferrerId>),
SweepFees,
CloseOpenOrders {
owner_id: OwnerId,
},
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
Expand Down Expand Up @@ -84,6 +87,7 @@ struct Owner<'bump> {
orders_account: AccountInfo<'bump>,
coin_account: AccountInfo<'bump>,
pc_account: AccountInfo<'bump>,
closed_open_orders: bool,
}

const INITIAL_COIN_BALANCE: u64 = 1_000_000_000;
Expand Down Expand Up @@ -117,6 +121,7 @@ impl<'bump> Owner<'bump> {
orders_account,
coin_account,
pc_account,
closed_open_orders: false,
}
}

Expand All @@ -129,8 +134,13 @@ impl<'bump> Owner<'bump> {
impl<'bump> Referrer<'bump> {
fn new(market_accounts: &MarketAccounts<'bump>, bump: &'bump Bump) -> Self {
let signer_account = new_sol_account(10, &bump);
let pc_account =
new_token_account(market_accounts.pc_mint.key, signer_account.key, 0, &bump, market_accounts.rent());
let pc_account = new_token_account(
market_accounts.pc_mint.key,
signer_account.key,
0,
&bump,
market_accounts.rent(),
);
Self { pc_account }
}
}
Expand Down Expand Up @@ -220,6 +230,9 @@ fn run_actions(actions: Vec<Action>) {
Err(e) if e == DexErrorCode::RentNotProvided.into() => {
continue;
}
Err(e) if e == DexErrorCode::WrongOrdersAccount.into() && owner.closed_open_orders => {
continue
}
_ => load_orders_result.unwrap(),
};
assert_eq!(identity(open_orders.free_slot_bits), !0);
Expand Down Expand Up @@ -371,8 +384,11 @@ fn run_action<'bump>(
.map_err(|e| match e {
DexError::ErrorCode(DexErrorCode::InsufficientFunds) => {}
DexError::ErrorCode(DexErrorCode::RequestQueueFull) => {}
DexError::ErrorCode(DexErrorCode::OrdersNotRentExempt) => {}
DexError::ErrorCode(DexErrorCode::WouldSelfTrade)
if instruction.self_trade_behavior == SelfTradeBehavior::AbortTransaction => {}
DexError::ErrorCode(DexErrorCode::WrongOrdersAccount)
if owner.closed_open_orders => {}
e => Err(e).unwrap(),
})
.ok();
Expand Down Expand Up @@ -433,7 +449,10 @@ fn run_action<'bump>(
.map_err(|e| match e {
DexError::ErrorCode(DexErrorCode::OrderNotFound) => {}
DexError::ErrorCode(DexErrorCode::RequestQueueFull) => {}
DexError::ErrorCode(DexErrorCode::RentNotProvided) => {}
DexError::ErrorCode(DexErrorCode::ClientOrderIdIsZero) if expects_zero_id => {}
DexError::ErrorCode(DexErrorCode::WrongOrdersAccount)
if owner.closed_open_orders => {}
e => Err(e).unwrap(),
})
.map(|_| {
Expand Down Expand Up @@ -476,6 +495,8 @@ fn run_action<'bump>(
DexError::ErrorCode(DexErrorCode::OrderNotFound) => {}
DexError::ErrorCode(DexErrorCode::OrderNotYours) => {}
DexError::ErrorCode(DexErrorCode::RentNotProvided) => {}
DexError::ErrorCode(DexErrorCode::WrongOrdersAccount)
if owner.closed_open_orders => {}
e => Err(e).unwrap(),
})
.ok();
Expand Down Expand Up @@ -540,7 +561,13 @@ fn run_action<'bump>(
&accounts,
&MarketInstruction::SettleFunds.pack(),
)
.unwrap();
.map_err(|e| match e {
DexError::ErrorCode(DexErrorCode::RentNotProvided) => {}
DexError::ErrorCode(DexErrorCode::WrongOrdersAccount)
if owner.closed_open_orders => {}
e => Err(e).unwrap(),
})
.ok();
}

Action::SweepFees => {
Expand All @@ -558,6 +585,33 @@ fn run_action<'bump>(
)
.unwrap();
}
Action::CloseOpenOrders { 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(),
owner.signer_account.clone(), // SOL destination.
market_accounts.market.clone(),
],
&MarketInstruction::CloseOpenOrders.pack(),
)
.map_err(|e| match e {
DexError::ErrorCode(DexErrorCode::TooManyOpenOrders) => {}
DexError::ErrorCode(DexErrorCode::RentNotProvided) => {}
DexError::ErrorCode(DexErrorCode::WrongOrdersAccount)
if owner.closed_open_orders => {}
e => Err(e).unwrap(),
})
.map(|r| {
owner.closed_open_orders = true;
r
})
.ok();
}
};

if *VERBOSE >= 2 {
Expand Down
38 changes: 31 additions & 7 deletions dex/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
sysvar::rent,
};
use std::{cmp::max, convert::TryInto};

Expand Down Expand Up @@ -432,6 +433,11 @@ pub enum MarketInstruction {
/// 3. `[writable]` OpenOrders
/// 4. `[]`
SendTake(SendTakeInstruction),
/// 0. `[writable]` OpenOrders
/// 1. `[signer]` the OpenOrders owner
/// 2. `[writable]` the destination account to send rent exemption SOL to
/// 3. `[]` market
CloseOpenOrders,
}

impl MarketInstruction {
Expand Down Expand Up @@ -523,6 +529,7 @@ impl MarketInstruction {
let data_arr = array_ref![data, 0, 46];
SendTakeInstruction::unpack(data_arr)?
}),
(14, 0) => MarketInstruction::CloseOpenOrders,
_ => return None,
})
}
Expand Down Expand Up @@ -623,7 +630,7 @@ pub fn new_order(
client_order_id: u64,
self_trade_behavior: SelfTradeBehavior,
limit: u16,
max_native_pc_qty_including_fees: NonZeroU64
max_native_pc_qty_including_fees: NonZeroU64,
) -> Result<Instruction, DexError> {
let data = MarketInstruction::NewOrderV3(NewOrderInstructionV3 {
side,
Expand All @@ -633,7 +640,7 @@ pub fn new_order(
client_order_id,
self_trade_behavior,
limit,
max_native_pc_qty_including_fees
max_native_pc_qty_including_fees,
})
.pack();
let mut accounts = vec![
Expand Down Expand Up @@ -726,11 +733,7 @@ pub fn cancel_order(
side: Side,
order_id: u128,
) -> Result<Instruction, DexError> {
let data = MarketInstruction::CancelOrderV2(CancelOrderInstructionV2 {
side,
order_id,
})
.pack();
let data = MarketInstruction::CancelOrderV2(CancelOrderInstructionV2 { side, order_id }).pack();
let accounts: Vec<AccountMeta> = vec![
AccountMeta::new_readonly(*market, false),
AccountMeta::new_readonly(*market_bids, false),
Expand Down Expand Up @@ -849,6 +852,27 @@ pub fn sweep_fees(
})
}

pub fn close_open_orders(
program_id: &Pubkey,
open_orders: &Pubkey,
owner: &Pubkey,
destination: &Pubkey,
market: &Pubkey,
) -> Result<Instruction, DexError> {
let data = MarketInstruction::CloseOpenOrders.pack();
let accounts: Vec<AccountMeta> = vec![
AccountMeta::new(*open_orders, false),
AccountMeta::new_readonly(*owner, true),
AccountMeta::new(*destination, false),
AccountMeta::new_readonly(*market, false),
];
Ok(Instruction {
program_id: *program_id,
data,
accounts,
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading

0 comments on commit 2d4a7a9

Please sign in to comment.