diff --git a/.bumpversion.toml b/.bumpversion.toml index 09d8344..be0b713 100644 --- a/.bumpversion.toml +++ b/.bumpversion.toml @@ -1,6 +1,18 @@ [bumpversion] -current_version = 0.0.4 +current_version = 0.0.5 [bumpversion:file:Cargo.toml] search = 'version = "{current_version}"' -replace = 'version = "{new_version}"' \ No newline at end of file +replace = 'version = "{new_version}"' + +[bumpversion:file:Cargo.lock] +search = 'version = "{current_version}"' +replace = 'version = "{new_version}"' + +[bumpversion:file:README.md] +search = 'openbook = "{current_version}"' +replace = 'openbook = "{new_version}"' + +[bumpversion:file:src/lib.rs] +search = '//! openbook = "{current_version}"' +replace = '//! openbook = "{new_version}"' diff --git a/Cargo.lock b/Cargo.lock index a70033e..19bcde9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2555,7 +2555,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openbook" -version = "0.0.4" +version = "0.0.5" dependencies = [ "anchor-spl", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 95bd2bc..bb455f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openbook" -version = "0.0.4" +version = "0.0.5" edition = "2021" description = "📖 A CLI and library for interacting with the OpenBook market on the Solana blockchain." license = "MIT" diff --git a/README.md b/README.md index a7be719..f3bd422 100644 --- a/README.md +++ b/README.md @@ -49,95 +49,116 @@ export KEY_PATH= ## ⌨ Usage as CLI -### Get Market Info: +### Fetch Market Info: ```sh openbook info ``` -### Place a limit bid: +### Place a limit bid order: ```sh -openbook place --bid 100 +openbook place -t 10.0 -s bid -b 0.5 -e -p 15.0 ``` -### Cancel an order: +### Place a limit ask order: ```sh -openbook cancel --bid 42 +openbook place -t 10.0 -s ask -b 0.5 -e -p 15.0 ``` -### Make match orders transaction: +### Cancel all limit orders: ```sh -openbook match --order 5 +openbook cancel -e ``` -### Make consume events instruction: +### Settle balances: ```sh -openbook consume --limit 10 +openbook settle -e ``` -### Make consume events permissioned instruction: +### Fetch all orders for current owner (bids + asks): + +```sh +openbook load +``` + +### Make match orders transaction: + +```sh +openbook match --limit 3 +``` + +### Make consume events instruction: ```sh -openbook consume-permissioned --limit 15 +openbook consume --limit 2 ``` -### Load orders for owner: +### Make consume events permissioned instruction: ```sh -openbook load --num 20 +openbook consume-permissioned --limit 2 ``` ## 💻 Usage as Dependency ```toml [dependencies] -openbook = "0.0.4" +openbook = "0.0.5" ``` ```rust use openbook::{pubkey::Pubkey, signature::Keypair, rpc_client::RpcClient}; use openbook::market::Market; use openbook::utils::read_keypair; +use openbook::matching::Side; +use openbook::commitment_config::CommitmentConfig; #[tokio::main] async fn main() -> Result<(), Box> { let rpc_url = std::env::var("RPC_URL").expect("RPC_URL is not set in .env file"); let key_path = std::env::var("KEY_PATH").expect("KEY_PATH is not set in .env file"); - - let rpc_client = RpcClient::new(rpc_url); - + + let commitment_config = CommitmentConfig::confirmed(); + let rpc_client = RpcClient::new_with_commitment(rpc_url, commitment_config); + let keypair = read_keypair(&key_path); - + let mut market = Market::new(rpc_client, 3, "usdc", keypair).await; - + println!("Initialized Market: {:?}", market); - let max_bid = 1; - let r = market.place_limit_bid(max_bid).await?; - println!("Place Order Results: {:?}", r); + let r = market + .place_limit_order( + 10.0, + Side::Bid, // or Side::Ask + 0.5, + true, + 15.0, + ) + .await?; + println!("Place Limit Order Result: {:?}", r); - let order_id_to_cancel = 2; - let c = market.cancel_order(order_id_to_cancel).await?; - println!("Cancel Order Results: {:?}", c); + let c = market.cancel_orders(true).await?; + println!("Cancel Orders Result: {:?}", c); - let s = market.settle_balance().await?; - println!("Settle Balance Results: {:?}", s); + let s = market.settle_balance(true).await?; + println!("Settle Balance Result: {:?}", s); let m = market.make_match_orders_transaction(1).await?; - println!("Match Order Results: {:?}", m); + println!("Match Order Result: {:?}", m); let open_orders_accounts = vec![Pubkey::new_from_array([0; 32])]; let limit = 10; let e = market.make_consume_events_instruction(open_orders_accounts.clone(), limit).await?; - println!("Consume Events Results: {:?}", e); + println!("Consume Events Result: {:?}", e); let p = market.make_consume_events_permissioned_instruction(open_orders_accounts.clone(), limit).await?; - println!("Consume Events Permissioned Results: {:?}", p); + println!("Consume Events Permissioned Result: {:?}", p); Ok(()) } @@ -145,16 +166,17 @@ async fn main() -> Result<(), Box> { ## 🎨 Options -| Option | Default Value | Description | -|--------------------------|---------------|----------------------------------------------------------| -| `place --bid ` | - | Place a limit bid with the specified amount. | -| `cancel --bid ` | - | Cancel an existing order with the given order ID. | -| `settle` | - | Settle balances in the OpenBook market. | -| `match --order ` | - | match orders transaction with the specified number of orders to match. | -| `consume --limit ` | - | consume events instruction with the specified limit. | -| `consume-permissioned --limit ` | - | consume events permissioned instruction with the specified limit. | -| `load --num ` | - | Load orders for a specific owner with the specified number. | -| `find-open-accounts` | - | Find open orders accounts for a specific owner. | +| Option | Default Value | Description | +|----------------------------------------|---------------|----------------------------------------------------------| +| `place -t -s -b -e -p ` | - | Place a limit order with the specified parameters. | +| `cancel -e` | - | Cancel all existing order for the current owner. | +| `settle -e` | - | Settle balances in the OpenBook market. | +| `match --limit ` | - | Match orders transaction with the specified limit. | +| `consume --limit ` | - | Consume events instruction with the specified limit. | +| `consume-permissioned --limit ` | - | Consume events permissioned instruction with the specified limit. | +| `find --future_option ` | - | Find open orders accounts for a specific owner. | +| `load` | - | Load orders for the current owner, bids + asks. | +| `info` | - | Fetch OpenBook market info. | ## 🤝 Contributing diff --git a/src/cli.rs b/src/cli.rs index e7ba8ac..0627083 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -53,14 +53,17 @@ USAGE: openbook [OPTIONS] EXAMPLES: - Place a limit bid: - openbook place --bid 100 + Place a bid limit order: + openbook place -t 10.0 -s bid -b 0.5 -e -p 15.0 - Cancel an order: - openbook cancel --order 42 + Place a ask limit order: + openbook place -t 10.0 -s ask -b 0.5 -e -p 15.0 + + Cancel all limit orders: + openbook cancel -e Settle balances: - openbook settle + openbook settle -e For more information, visit: github.com/wiseaidev/openbook "# @@ -79,7 +82,7 @@ pub struct Cli { #[cfg(feature = "cli")] #[derive(Subcommand, Debug, Clone)] pub enum Commands { - /// Place a limit bid. + /// Place a limit order. Place(Place), /// Cancel an order. Cancel(Cancel), @@ -99,31 +102,47 @@ pub enum Commands { Info(Info), } -/// Represents options for placing a limit bid in the OpenBook market. +/// Represents options for placing a limit order in the OpenBook market. #[cfg(feature = "cli")] #[derive(Args, Debug, Clone)] pub struct Place { - /// Maximum bid amount. + /// Target amount in quote currency. + #[arg(short, long)] + pub target_amount_quote: f64, + + /// Side of the order (Bid or Ask). + #[arg(short, long)] + pub side: String, + + /// Best offset in USDC. + #[arg(short, long)] + pub best_offset_usdc: f64, + + /// Flag indicating whether to execute the order immediately. + #[arg(short, long)] + pub execute: bool, + + /// Target price for the order. #[arg(short, long)] - pub bid: u64, + pub price_target: f64, } /// Represents options for cancelling an order in the OpenBook market. #[cfg(feature = "cli")] #[derive(Args, Debug, Clone)] pub struct Cancel { - /// Order ID to cancel. + /// Flag indicating whether to execute the order immediately. #[arg(short, long)] - pub order: u64, + pub execute: bool, } /// Represents options for settling balances in the OpenBook market. #[cfg(feature = "cli")] #[derive(Args, Debug, Clone)] pub struct Settle { - /// Comming Soon: future options related to settling balances. + /// Flag indicating whether to execute the order immediately. #[arg(short, long)] - pub future_option: u64, + pub execute: bool, } /// Represents options for match orders transactions in the OpenBook market. @@ -132,7 +151,7 @@ pub struct Settle { pub struct Match { /// The maximum number of orders to match. #[arg(short, long)] - pub order: u16, + pub limit: u16, } /// Represents options for consume events instructions in the OpenBook market. @@ -153,14 +172,10 @@ pub struct ConsumePermissioned { pub limit: u16, } -/// Represents options for loading orders for a specific owner in the OpenBook market. +/// Represents options for loading orders for the current owner in the OpenBook market. #[cfg(feature = "cli")] #[derive(Args, Debug, Clone)] -pub struct Load { - /// Number of orders to load. - #[arg(short, long)] - pub num: u64, -} +pub struct Load {} /// Represents options for finding open orders accounts for a specific owner in the OpenBook market. #[cfg(feature = "cli")] diff --git a/src/lib.rs b/src/lib.rs index b250042..ba6620c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ //! //! ```toml //! [dependencies] -//! openbook = "0.0.4" +//! openbook = "0.0.5" //! ``` //! //! 2. Use the `Market` struct to perform various operations in the OpenBook market: @@ -26,33 +26,42 @@ //! use openbook::{pubkey::Pubkey, signature::Keypair, rpc_client::RpcClient}; //! use openbook::market::Market; //! use openbook::utils::read_keypair; +//! use openbook::matching::Side; +//! use openbook::commitment_config::CommitmentConfig; //! //! #[tokio::main] //! async fn main() -> Result<(), Box> { //! let rpc_url = std::env::var("RPC_URL").expect("RPC_URL is not set in .env file"); //! let key_path = std::env::var("KEY_PATH").expect("KEY_PATH is not set in .env file"); -//! -//! let rpc_client = RpcClient::new(rpc_url); +//! +//! let commitment_config = CommitmentConfig::confirmed(); +//! let rpc_client = RpcClient::new_with_commitment(rpc_url, commitment_config); //! //! let keypair = read_keypair(&key_path); //! //! let mut market = Market::new(rpc_client, 3, "usdc", keypair).await; -//! +//! //! println!("Initialized Market: {:?}", market); //! -//! let max_bid = 1; -//! let r = market.place_limit_bid(max_bid).await?; -//! println!("Place Order Results: {:?}", r); +//! let r = market +//! .place_limit_order( +//! 10.0, +//! Side::Bid, // or Side::Ask +//! 0.5, +//! true, +//! 15.0, +//! ) +//! .await?; +//! println!("Place Limit Order Result: {:?}", r); //! -//! let order_id_to_cancel = 2; -//! let c = market.cancel_order(order_id_to_cancel).await?; -//! println!("Cancel Order Results: {:?}", c); +//! let c = market.cancel_orders(true).await?; +//! println!("Cancel Orders Result: {:?}", c); //! -//! let s = market.settle_balance().await?; -//! println!("Settle Balance Results: {:?}", s); +//! let s = market.settle_balance(true).await?; +//! println!("Settle Balance Result: {:?}", s); //! //! let m = market.make_match_orders_transaction(1).await?; -//! println!("Match Order Results: {:?}", m); +//! println!("Match Order Result: {:?}", m); //! //! let open_orders_accounts = vec![Pubkey::new_from_array([0; 32])]; //! let limit = 10; @@ -60,8 +69,7 @@ //! let e = market.make_consume_events_instruction(open_orders_accounts.clone(), limit).await?; //! println!("Consume Events Results: {:?}", e); //! -//! let p = -//! market.make_consume_events_permissioned_instruction(open_orders_accounts.clone(), limit).await?; +//! let p = market.make_consume_events_permissioned_instruction(open_orders_accounts.clone(), limit).await?; //! println!("Consume Events Permissioned Results: {:?}", p); //! //! Ok(()) @@ -70,16 +78,17 @@ //! //! ## Options //! -//! | Subcommand | Description | -//! |--------------------------------------|----------------------------------------------------------| -//! | `place --bid ` | Place a limit bid with the specified amount. | -//! | `cancel --order ` | Cancel an existing order with the given order ID. | -//! | `settle` | Settle balances in the OpenBook market. | -//! | `match --order ` | Match orders transaction with the specified number of orders to match. | -//! | `consume --limit ` | Consume events instruction with the specified limit. | -//! | `consume-permissioned --limit ` | Consume events permissioned instruction with the specified limit. | -//! | `load --num ` | Load orders for a specific owner with the specified number. | -//! | `find-open-accounts` | Find open orders accounts for a specific owner. | +//! | Option | Default Value | Description | +//! |----------------------------------------|---------------|----------------------------------------------------------| +//! | `place -t -s -b -e -p ` | - | Place a limit order with the specified parameters. | +//! | `cancel -e` | - | Cancel all existing order for the current owner. | +//! | `settle -e` | - | Settle balances in the OpenBook market. | +//! | `match --limit ` | - | Match orders transaction with the specified limit. | +//! | `consume --limit ` | - | Consume events instruction with the specified limit. | +//! | `consume-permissioned --limit ` | - | Consume events permissioned instruction with the specified limit. | +//! | `find --future_option ` | - | Find open orders accounts for a specific owner. | +//! | `load` | - | Load orders for the current owner, bids + asks. | +//! | `info` | - | Fetch OpenBook market info. | //! //! ## GitHub Repository //! @@ -100,11 +109,13 @@ pub mod tokens_and_markets; pub mod utils; // Re-export common func +pub use openbook_dex::matching; pub use openbook_dex::state; pub use solana_client::rpc_filter; pub use solana_program::pubkey; pub use solana_rpc_client::nonblocking::rpc_client; pub use solana_sdk::account; pub use solana_sdk::bs58; +pub use solana_sdk::commitment_config; pub use solana_sdk::signature; pub use solana_sdk::signer::keypair; diff --git a/src/main.rs b/src/main.rs index 9e1734a..ceebe28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,10 +9,11 @@ async fn main() -> Result<(), Box> { { use clap::Parser; use openbook::cli::{Cli, Commands}; + use openbook::commitment_config::CommitmentConfig; use openbook::market::Market; + use openbook::matching::Side; use openbook::rpc_client::RpcClient; use openbook::utils::read_keypair; - use solana_sdk::commitment_config::CommitmentConfig; let rpc_url = std::env::var("RPC_URL").expect("RPC_URL is not set"); let key_path = std::env::var("KEY_PATH").expect("KEY_PATH is not set"); @@ -31,19 +32,39 @@ async fn main() -> Result<(), Box> { println!("[*] Market Info: {:?}", market); } Some(Commands::Place(arg)) => { - let r = market.place_limit_bid(arg.bid).await?; - println!("[*] Transaction successful, signature: {:?}", r); + let side = match arg.side.as_str() { + "bid" => Side::Bid, + "ask" => Side::Ask, + _ => Side::Bid, + }; + + let result = market + .place_limit_order( + arg.target_amount_quote, + side, + arg.best_offset_usdc, + arg.execute, + arg.price_target, + ) + .await?; + println!( + "[*] Transaction successful, signature: {:?}", + result.unwrap() + ); } Some(Commands::Cancel(arg)) => { - let c = market.cancel_order(arg.order).await?; + let c = market.cancel_orders(arg.execute).await?; println!("[*] Transaction successful, signature: {:?}", c); } - Some(Commands::Settle(_arg)) => { - let s = market.settle_balance().await?; - println!("[*] Transaction successful, signature: {:?}", s); + Some(Commands::Settle(arg)) => { + let result = market.settle_balance(arg.execute).await?; + println!( + "[*] Transaction successful, signature: {:?}", + result.unwrap() + ); } Some(Commands::Match(arg)) => { - let m = market.make_match_orders_transaction(arg.order).await?; + let m = market.make_match_orders_transaction(arg.limit).await?; println!("[*] Transaction successful, signature: {:?}", m); } Some(Commands::Consume(arg)) => { @@ -58,8 +79,8 @@ async fn main() -> Result<(), Box> { .await?; println!("[*] Transaction successful, signature: {:?}", p); } - Some(Commands::Load(arg)) => { - let l = market.load_orders_for_owner(arg.num).await?; + Some(Commands::Load(_arg)) => { + let l = market.load_orders_for_owner().await?; println!("[*] Found Program Accounts: {:?}", l); } Some(Commands::Find(_arg)) => { diff --git a/src/market.rs b/src/market.rs index 4170a85..114d464 100644 --- a/src/market.rs +++ b/src/market.rs @@ -69,10 +69,10 @@ pub struct Market { pub pc_lot_size: u64, /// The public key of the account holding USDC tokens. - pub usdc_ata: Pubkey, + pub quote_ata: Pubkey, - /// The public key of the account holding WSOL tokens. - pub wsol_ata: Pubkey, + /// The public key of the base market token. + pub base_ata: Pubkey, /// The public key of the vault holding base currency (coin) tokens. pub coin_vault: Pubkey, @@ -158,8 +158,8 @@ impl Market { market_name: &'static str, keypair: Keypair, ) -> Self { - let usdc_ata = get_market_name("usdc").1.parse().unwrap(); - let wsol_ata = get_market_name("sol").1.parse().unwrap(); + let quote_ata = get_market_name("usdc").1.parse().unwrap(); + let base_ata = get_market_name("sol").1.parse().unwrap(); let orders_key = Default::default(); let coin_vault = Default::default(); let pc_vault = Default::default(); @@ -189,8 +189,8 @@ impl Market { pc_decimals: 6, coin_lot_size: 1_000_000, pc_lot_size: 1, - usdc_ata, - wsol_ata, + quote_ata, + base_ata, coin_vault, pc_vault, vault_signer_key, @@ -201,13 +201,8 @@ impl Market { open_orders_accounts_cache, market_info, }; - market.load().await.unwrap(); - let ata_address = market - .find_or_create_associated_token_account(&market.keypair, &market_address) - .await - .unwrap(); - let oos_key_str = std::env::var("OOS_KEY").unwrap_or(ata_address.to_string()); + let oos_key_str = std::env::var("OOS_KEY").unwrap_or("".to_string()); let orders_key = Pubkey::from_str(oos_key_str.as_str()); @@ -228,6 +223,12 @@ impl Market { market.orders_key = orders_key.unwrap(); } + market.load().await.unwrap(); + // let _ata_address = market + // .find_or_create_associated_token_account(&market.keypair, &market_address) + // .await + // .unwrap(); + market } @@ -695,17 +696,21 @@ impl Market { ) } - /// Places a limit bid order on the market. + /// Places a limit order on the market. /// /// # Arguments /// /// * `&self` - A reference to the `Market` struct. - /// * `max_bid` - The maximum bid value for the order. + /// * `target_amount_quote` - The target amount in quote currency for the order. + /// * `side` - The side of the order (buy or sell). + /// * `best_offset_usdc` - The best offset in USDC for the order. + /// * `execute` - A boolean indicating whether to execute the order immediately. + /// * `target_price` - The target price for the order. /// /// # Returns /// /// A `Result` containing the transaction signature if successful, - /// or an error if placing the limit bid fails. + /// or an error if placing the limit order fails. /// /// # Errors /// @@ -717,6 +722,7 @@ impl Market { /// use openbook::{pubkey::Pubkey, signature::Keypair, rpc_client::RpcClient}; /// use openbook::market::Market; /// use openbook::utils::read_keypair; + /// use openbook::matching::Side; /// /// #[tokio::main] /// async fn main() -> Result<(), Box> { @@ -729,19 +735,82 @@ impl Market { /// /// let market = Market::new(rpc_client, 3, "usdc", keypair).await; /// - /// let limit_bid = 2; - /// let result = market.place_limit_bid(limit_bid).await?; + /// let target_amount_quote = 10.0; + /// let side = Side::Bid; // or Side::Ask + /// let best_offset_usdc = 0.5; + /// let execute = true; + /// let target_price = 15.0; + /// + /// let result = market.place_limit_order( + /// target_amount_quote, + /// side, + /// best_offset_usdc, + /// execute, + /// target_price + /// ).await?; /// /// println!("{:?}", result); /// /// Ok(()) /// } /// ``` - pub async fn place_limit_bid(&self, max_bid: u64) -> anyhow::Result { - assert!(max_bid > 0, "Max bid must be greater than zero"); - let limit_price = NonZeroU64::new(max_bid).unwrap(); - let max_coin_qty = NonZeroU64::new(self.coin_lot_size).unwrap(); - let target_usdc_lots_w_fee = (1.0 * 1e6 * 1.1) as u64; + pub async fn place_limit_order( + &self, + target_amount_quote: f64, + side: Side, + best_offset_usdc: f64, + execute: bool, + target_price: f64, + ) -> anyhow::Result, ClientError> { + // coin: base + // pc: quote + let base_d_factor = 10u32.pow(self.coin_decimals as u32) as f64; + let quote_d_factor = 10u32.pow(self.pc_decimals as u32) as f64; + let base_lot_factor = self.coin_lot_size as f64; + let quote_lot_factor = 10u32.pow(self.pc_lot_size as u32) as f64; + + let price_factor = quote_d_factor * base_lot_factor / base_d_factor / quote_lot_factor; + + let (input_ata, price) = match side { + Side::Bid => { + let mut price = self.market_info.max_bid as f64 / price_factor - best_offset_usdc; + if !execute { + price = target_price; + } + + (&self.quote_ata, price) + } + Side::Ask => { + let mut price = self.market_info.min_ask as f64 / price_factor + best_offset_usdc; + if !execute { + price = target_price; + } + + (&self.base_ata, price) + } + }; + + let limit_price_lots = (price * price_factor) as u64; + let target_amount_base = target_amount_quote / price; + + let target_base_lots = (target_amount_base * base_d_factor / base_lot_factor) as u64; + let target_quote_lots_w_fee = + (target_base_lots as f64 * quote_lot_factor * limit_price_lots as f64) as u64; + + debug!("[*] Using limit price lots: {:?}", limit_price_lots); + debug!("[*] Using target base lots: {:?}", target_base_lots); + + if target_base_lots == 0 { + debug!( + "[*] Got zero base lots, and quote: {:?}", + target_amount_quote + ); + return Ok(None); + } + + let limit_price = NonZeroU64::new(limit_price_lots).unwrap(); + let max_coin_qty = NonZeroU64::new(target_base_lots).unwrap(); // max wsol lots + let max_native_pc_qty_including_fees = NonZeroU64::new(target_quote_lots_w_fee).unwrap(); // max usdc lots + fees let place_order_ix = openbook_dex::instruction::new_order( &self.market_address, @@ -750,7 +819,7 @@ impl Market { &self.event_queue, &self.market_info.bids_address, &self.market_info.asks_address, - &self.usdc_ata, + &input_ata, &self.keypair.pubkey(), &self.coin_vault, &self.pc_vault, @@ -765,7 +834,7 @@ impl Market { random::(), SelfTradeBehavior::AbortTransaction, u16::MAX, - NonZeroU64::new(target_usdc_lots_w_fee).unwrap(), + max_native_pc_qty_including_fees, (get_unix_secs() + 30) as i64, ) .unwrap(); @@ -782,23 +851,25 @@ impl Market { let mut config = RpcSendTransactionConfig::default(); config.skip_preflight = true; - self.rpc_client - .0 - .send_transaction_with_config(&txn, config) - .await + Ok(Some( + self.rpc_client + .0 + .send_transaction_with_config(&txn, config) + .await?, + )) } - /// Cancels an existing order in the market. + /// Cancels all limit orders in the market. /// /// # Arguments /// /// * `&self` - A reference to the `Market` struct. - /// * `order_id` - The identifier of the order to be canceled. + /// * `execute` - A boolean indicating whether to execute the order immediately. /// /// # Returns /// /// A `Result` containing the transaction signature if successful, - /// or an error if canceling the order fails. + /// or an error if canceling all orders fails. /// /// # Errors /// @@ -822,35 +893,58 @@ impl Market { /// /// let market = Market::new(rpc_client, 3, "usdc", keypair).await; /// - /// let order_id_to_cancel = 2; - /// let result = market.cancel_order(order_id_to_cancel).await?; + /// let result = market.cancel_orders(true).await?; /// /// println!("{:?}", result); /// /// Ok(()) /// } /// ``` - pub async fn cancel_order(&self, order_id: u64) -> anyhow::Result { - assert!(order_id > 0, "Order ID must be greater than zero"); + pub async fn cancel_orders( + &self, + execute: bool, + ) -> anyhow::Result, ClientError> { + let mut ixs = Vec::new(); - let ix = openbook_dex::instruction::cancel_order( - &self.program_id, - &self.market_address, - &self.market_info.bids_address, - &self.market_info.asks_address, - &self.orders_key, - &self.keypair.pubkey(), - &self.event_queue, - Side::Bid, - order_id as u128, - ) - .unwrap(); + for oid in &self.market_info.open_bids { + let ix = openbook_dex::instruction::cancel_order( + &self.program_id, + &self.market_address, + &self.market_info.bids_address, + &self.market_info.asks_address, + &self.orders_key, + &self.keypair.pubkey(), + &self.event_queue, + Side::Bid, + *oid, + ) + .unwrap(); + ixs.push(ix); + } - let instructions = vec![ix]; + for oid in &self.market_info.open_asks { + let ix = openbook_dex::instruction::cancel_order( + &self.program_id, + &self.market_address, + &self.market_info.bids_address, + &self.market_info.asks_address, + &self.orders_key, + &self.keypair.pubkey(), + &self.event_queue, + Side::Ask, + *oid, + ) + .unwrap(); + ixs.push(ix); + } + + if ixs.len() == 0 || !execute { + return Ok(None); + } let recent_hash = self.rpc_client.0.get_latest_blockhash().await?; let txn = Transaction::new_signed_with_payer( - &instructions, + &ixs, Some(&self.keypair.pubkey()), &[&self.keypair], recent_hash, @@ -858,10 +952,12 @@ impl Market { let mut config = RpcSendTransactionConfig::default(); config.skip_preflight = true; - self.rpc_client - .0 - .send_transaction_with_config(&txn, config) - .await + Ok(Some( + self.rpc_client + .0 + .send_transaction_with_config(&txn, config) + .await?, + )) } /// Settles the balance for a user in the market. @@ -869,6 +965,7 @@ impl Market { /// # Arguments /// /// * `&self` - A reference to the `Market` struct. + /// * `execute` - A boolean indicating whether to execute the order immediately. /// /// # Returns /// @@ -897,14 +994,17 @@ impl Market { /// /// let market = Market::new(rpc_client, 3, "usdc", keypair).await; /// - /// let result = market.settle_balance().await?; + /// let result = market.settle_balance(true).await?; /// /// println!("{:?}", result); /// /// Ok(()) /// } /// ``` - pub async fn settle_balance(&self) -> anyhow::Result { + pub async fn settle_balance( + &self, + execute: bool, + ) -> anyhow::Result, ClientError> { let ix = openbook_dex::instruction::settle_funds( &self.program_id, &self.market_address, @@ -912,9 +1012,9 @@ impl Market { &self.orders_key, &self.keypair.pubkey(), &self.coin_vault, - &self.wsol_ata, + &self.base_ata, &self.pc_vault, - &self.usdc_ata, + &self.quote_ata, None, &self.vault_signer_key, ) @@ -922,6 +1022,10 @@ impl Market { let instructions = vec![ix]; + if !execute { + return Ok(None); + } + let recent_hash = self.rpc_client.0.get_latest_blockhash().await?; let txn = Transaction::new_signed_with_payer( &instructions, @@ -932,10 +1036,12 @@ impl Market { let mut config = RpcSendTransactionConfig::default(); config.skip_preflight = true; - self.rpc_client - .0 - .send_transaction_with_config(&txn, config) - .await + Ok(Some( + self.rpc_client + .0 + .send_transaction_with_config(&txn, config) + .await?, + )) } /// Creates a new transaction to match orders in the market. @@ -1039,8 +1145,8 @@ impl Market { /// Ok(()) /// } /// ``` - pub fn load_bids(&mut self) -> Result { - self.load_orders(self.market_info.bids_address) + pub fn load_bids(&mut self) -> Result, ProgramError> { + Ok(self.market_info.open_bids.clone()) } /// Loads the asks from the market. @@ -1078,26 +1184,8 @@ impl Market { /// Ok(()) /// } /// ``` - pub fn load_asks(&mut self) -> Result { - self.load_orders(self.market_info.asks_address) - } - - /// Loads orders from the specified address. - /// - /// # Arguments - /// - /// * `address` - The address from which to load orders. - /// - /// # Returns - /// - /// The orders stored at the specified address. - /// - /// # Errors - /// - /// Returns an error if there is an issue with loading the orders. - pub fn load_orders(&mut self, _address: Pubkey) -> anyhow::Result { - // let account_info: Vec = self.rpc_client.0.get_account_data(&address).unwrap(); - Ok(self.market_info.clone()) + pub fn load_asks(&mut self) -> Result, ProgramError> { + Ok(self.market_info.open_asks.clone()) } /// Consumes events from the market for specified open orders accounts. @@ -1239,7 +1327,6 @@ impl Market { /// # Arguments /// /// * `&mut self` - A mutable reference to the `Market` struct. - /// * `cache_duration_ms` - The duration in milliseconds for which to cache open orders accounts. /// /// # Returns /// @@ -1263,24 +1350,25 @@ impl Market { /// /// let mut market = Market::new(rpc_client, 3, "usdc", keypair).await; /// - /// let result = market.load_orders_for_owner(5000).await?; + /// let result = market.load_orders_for_owner().await?; /// /// println!("{:?}", result); /// /// Ok(()) /// } /// ``` - pub async fn load_orders_for_owner( - &mut self, - cache_duration_ms: u64, - ) -> Result, Box> { - let _bids = self.load_bids()?; - let _asks = self.load_asks()?; - let open_orders_accounts = self - .find_open_orders_accounts_for_owner(&self.keypair.pubkey(), cache_duration_ms) - .await?; - - Ok(open_orders_accounts) + pub async fn load_orders_for_owner(&mut self) -> Result, Box> { + let mut bids = self.load_bids()?; + let asks = self.load_asks()?; + bids.extend(asks); + // let open_orders_accounts = self + // .find_open_orders_accounts_for_owner( + // &self.orders_key.clone(), + // 5000, + // ) + // .await?; + + Ok(bids) } /// Filters open orders accounts based on bids and asks. diff --git a/src/orders.rs b/src/orders.rs index 6e4a724..bc3a4b2 100644 --- a/src/orders.rs +++ b/src/orders.rs @@ -9,6 +9,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use log::debug; use memoffset::offset_of; use solana_client::{ + rpc_config::RpcSendTransactionConfig, rpc_filter::MemcmpEncodedBytes, rpc_filter::{Memcmp, MemcmpEncodedBytes::Base58, MemcmpEncoding, RpcFilterType}, }; @@ -415,7 +416,10 @@ impl OpenOrders { recent_hash, ); - let result = connection.send_transaction(&txn).await; + let mut config = RpcSendTransactionConfig::default(); + config.skip_preflight = true; + + let result = connection.send_transaction_with_config(&txn, config).await; match result { Ok(sig) => debug!("Transaction successful, signature: {:?}", sig), diff --git a/tests/market.rs b/tests/market.rs index 3c54b7a..06ed3ae 100644 --- a/tests/market.rs +++ b/tests/market.rs @@ -38,11 +38,11 @@ async fn test_market_state_info() { "EaXdHx7x3mdGA38j5RSmKYSXMzAFzzUXCLNBEDXDn1d5" ); assert_eq!( - &market.usdc_ata.to_string(), + &market.quote_ata.to_string(), "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" ); assert_eq!( - &market.wsol_ata.to_string(), + &market.base_ata.to_string(), "So11111111111111111111111111111111111111112" );