From 0f407ebc6359a58d8e081dbc82e5d73378a9baa0 Mon Sep 17 00:00:00 2001 From: James Li Date: Mon, 27 Mar 2023 12:10:42 -0500 Subject: [PATCH 1/6] add get seat manager info --- Cargo.toml | 1 + src/command.rs | 33 ++++++++++++----- src/lib/helpers/market_helpers.rs | 20 +++++++++++ src/lib/processor/mod.rs | 1 + .../process_get_seat_manager_info.rs | 35 +++++++++++++++++++ src/main.rs | 10 ++++-- 6 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 src/lib/processor/process_get_seat_manager_info.rs diff --git a/Cargo.toml b/Cargo.toml index fe45414..28a2b41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,3 +38,4 @@ phoenix-sdk = "0.3.6" bytemuck = "1.13.0" reqwest = "0.11.14" bincode = "1.3.3" +phoenix-seat-manager = { path = "../phoenix-seat-manager" } diff --git a/src/command.rs b/src/command.rs index 05a6a2f..51a99a6 100644 --- a/src/command.rs +++ b/src/command.rs @@ -13,11 +13,17 @@ pub enum PhoenixCLICommand { no_gpa: bool, }, /// Get detailed information on a specific market - GetMarket { market_pubkey: Pubkey }, + GetMarket { + market_pubkey: Pubkey, + }, /// Get active traders for a given market - GetTradersForMarket { market_pubkey: Pubkey }, + GetTradersForMarket { + market_pubkey: Pubkey, + }, /// Get the best bid and ask price for a given market - GetTopOfBook { market_pubkey: Pubkey }, + GetTopOfBook { + market_pubkey: Pubkey, + }, /// Get the first N levels of the order book for a given market. /// Default is 10 levels GetBookLevels { @@ -26,13 +32,17 @@ pub enum PhoenixCLICommand { levels: u64, }, /// Get the full order book for a given market - GetFullBook { market_pubkey: Pubkey }, + GetFullBook { + market_pubkey: Pubkey, + }, /// Get the market events that occured in a given transaction signature GetTransaction { signature: Signature, }, /// Get the current status of a market - GetMarketStatus { market_pubkey: Pubkey }, + GetMarketStatus { + market_pubkey: Pubkey, + }, /// Get the status and address of a seat for a given market and trader GetSeatInfo { market_pubkey: Pubkey, @@ -47,11 +57,13 @@ pub enum PhoenixCLICommand { #[clap(short, long, required = false)] trader_pubkey: Option, }, - /// Send a transaction on chain to allocate a seat for the payer on the given market. This will cost ~.0018 SOL for rent. + /// Send a transaction on chain to allocate a seat for the payer on the given market. This will cost ~.0018 SOL for rent. /// Note that the seat will have to then be approved by the market authority. - RequestSeat { market_pubkey: Pubkey }, + RequestSeat { + market_pubkey: Pubkey, + }, /// Mint tokens to a recipient for a given ticker string (for example SOL or USDC). Default amount is 100_000_000_000. - /// This is only for markets associated with the ellipsis token faucet. + /// This is only for markets associated with the ellipsis token faucet. MintTokens { /// Ticker string, example: SOL mint_ticker: String, @@ -62,7 +74,7 @@ pub enum PhoenixCLICommand { amount: u64, }, /// Mint both base and quote tokens to a recipient for a given market. Default amounts are 100_000_000_000 for base and 100_000_000 for quote. - /// This is only for markets associated with the ellipsis token faucet. + /// This is only for markets associated with the ellipsis token faucet. MintTokensForMarket { market_pubkey: Pubkey, /// Pubkey of the recipient of the tokens @@ -74,4 +86,7 @@ pub enum PhoenixCLICommand { #[clap(short, long, required = false, default_value = "100000000")] quote_amount: u64, }, + GetSeatManagerInfo { + market_pubkey: Pubkey, + }, } diff --git a/src/lib/helpers/market_helpers.rs b/src/lib/helpers/market_helpers.rs index 6e2abc1..6d79fd1 100644 --- a/src/lib/helpers/market_helpers.rs +++ b/src/lib/helpers/market_helpers.rs @@ -8,6 +8,8 @@ use phoenix::state::markets::{Ladder, Market}; use phoenix::state::OrderPacket; use phoenix_sdk::sdk_client::*; +use phoenix_seat_manager::get_seat_manager_address; +use phoenix_seat_manager::seat_manager::SeatManager; use solana_account_decoder::UiAccountEncoding; use solana_client::rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}; use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}; @@ -202,3 +204,21 @@ pub async fn get_market_header( Ok(*header) } + +pub async fn get_seat_manager_data_with_market( + client: &EllipsisClient, + market: &Pubkey, +) -> anyhow::Result { + let seat_manager_address = get_seat_manager_address(market).0; + get_seat_manager_data_with_pubkey(client, &seat_manager_address).await +} + +pub async fn get_seat_manager_data_with_pubkey( + client: &EllipsisClient, + seat_manager_pubkey: &Pubkey, +) -> anyhow::Result { + let seat_manager_account = client.get_account(seat_manager_pubkey).await?; + let seat_manager_data = SeatManager::load(&seat_manager_account.data)?; + + Ok(*seat_manager_data) +} diff --git a/src/lib/processor/mod.rs b/src/lib/processor/mod.rs index d42a4d6..876e893 100644 --- a/src/lib/processor/mod.rs +++ b/src/lib/processor/mod.rs @@ -5,6 +5,7 @@ pub mod process_get_market; pub mod process_get_market_status; pub mod process_get_open_orders; pub mod process_get_seat_info; +pub mod process_get_seat_manager_info; pub mod process_get_top_of_book; pub mod process_get_traders_for_market; pub mod process_get_transaction; diff --git a/src/lib/processor/process_get_seat_manager_info.rs b/src/lib/processor/process_get_seat_manager_info.rs new file mode 100644 index 0000000..945124c --- /dev/null +++ b/src/lib/processor/process_get_seat_manager_info.rs @@ -0,0 +1,35 @@ +use ellipsis_client::EllipsisClient; +use phoenix_seat_manager::{get_seat_manager_address, seat_manager::SeatManager}; +use solana_sdk::pubkey::Pubkey; + +use crate::helpers::market_helpers::get_seat_manager_data_with_market; + +pub async fn process_get_seat_manager_info( + client: &EllipsisClient, + market_pubkey: &Pubkey, +) -> anyhow::Result<()> { + let seat_manager_address = get_seat_manager_address(market_pubkey).0; + let seat_manager_info = get_seat_manager_data_with_market(client, market_pubkey).await?; + print_seat_manager_struct(&seat_manager_info, &seat_manager_address); + Ok(()) +} + +pub fn print_seat_manager_struct(seat_manager: &SeatManager, seat_manager_pubkey: &Pubkey) { + println!("Seat Manager Address: {}", seat_manager_pubkey); + println!("SM Market: {}", seat_manager.market); + println!("SM Authority: {}", seat_manager.authority); + println!("SM Successor: {}", seat_manager.successor); + println!( + "Number of designated market makers: {}", + seat_manager.num_makers + ); + + let dmms: Vec<&Pubkey> = seat_manager + .designated_market_makers + .iter() + .filter(|dmm| dmm != &&Pubkey::default()) + .collect(); + if !dmms.is_empty() { + println!("DMMs: {:?}", dmms); + } +} diff --git a/src/main.rs b/src/main.rs index 8ee1488..a6a5b5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,9 @@ use ellipsis_client::EllipsisClient; use phoenix_cli_processor::processor::{ process_get_all_markets::*, process_get_book_levels::*, process_get_full_book::*, process_get_market::*, process_get_market_status::*, process_get_open_orders::*, - process_get_seat_info::*, process_get_top_of_book::*, process_get_traders_for_market::*, - process_get_transaction::*, process_mint_tokens::*, process_mint_tokens_for_market::*, - process_request_seat::*, + process_get_seat_info::*, process_get_seat_manager_info::*, process_get_top_of_book::*, + process_get_traders_for_market::*, process_get_transaction::*, process_mint_tokens::*, + process_mint_tokens_for_market::*, process_request_seat::*, }; use phoenix_sdk::sdk_client::*; use solana_cli_config::{Config, ConfigInput, CONFIG_FILE}; @@ -154,6 +154,10 @@ async fn main() -> anyhow::Result<()> { ) .await? } + PhoenixCLICommand::GetSeatManagerInfo { market_pubkey } => { + sdk.add_market(&market_pubkey).await?; + process_get_seat_manager_info(&sdk.client, &market_pubkey).await?; + } } Ok(()) From d6ddccc548617c4602dd2b56c47b0dcf2e6fd332 Mon Sep 17 00:00:00 2001 From: James Li Date: Mon, 27 Mar 2023 13:24:30 -0500 Subject: [PATCH 2/6] add psm claim and evict seat --- Cargo.toml | 2 +- src/command.rs | 7 +++ src/lib/processor/mod.rs | 2 + src/lib/processor/process_claim_seat.rs | 21 ++++++++ src/lib/processor/process_evict_seat.rs | 50 +++++++++++++++++++ .../process_get_seat_manager_info.rs | 2 +- src/main.rs | 13 +++++ 7 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/lib/processor/process_claim_seat.rs create mode 100644 src/lib/processor/process_evict_seat.rs diff --git a/Cargo.toml b/Cargo.toml index 28a2b41..281925c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" spl-associated-token-account = { version = "1.1.1", features = [ "no-entrypoint" ] } phoenix-v1 = { version = "0.2.2", features = ["no-entrypoint"] } -phoenix-sdk = "0.3.6" +phoenix-sdk = {path = "../phoenix-sdk/rust/phoenix-sdk"} bytemuck = "1.13.0" reqwest = "0.11.14" bincode = "1.3.3" diff --git a/src/command.rs b/src/command.rs index 51a99a6..dc5dec4 100644 --- a/src/command.rs +++ b/src/command.rs @@ -89,4 +89,11 @@ pub enum PhoenixCLICommand { GetSeatManagerInfo { market_pubkey: Pubkey, }, + ClaimSeat { + market_pubkey: Pubkey, + }, + EvictSeat { + market_pubkey: Pubkey, + trader_to_evict: Option, + }, } diff --git a/src/lib/processor/mod.rs b/src/lib/processor/mod.rs index 876e893..c9b45f5 100644 --- a/src/lib/processor/mod.rs +++ b/src/lib/processor/mod.rs @@ -1,3 +1,5 @@ +pub mod process_claim_seat; +pub mod process_evict_seat; pub mod process_get_all_markets; pub mod process_get_book_levels; pub mod process_get_full_book; diff --git a/src/lib/processor/process_claim_seat.rs b/src/lib/processor/process_claim_seat.rs new file mode 100644 index 0000000..53b84dd --- /dev/null +++ b/src/lib/processor/process_claim_seat.rs @@ -0,0 +1,21 @@ +use ellipsis_client::EllipsisClient; +use phoenix_sdk::utils::create_claim_seat_ix_if_needed; +use solana_sdk::{pubkey::Pubkey, signer::Signer}; + +pub async fn process_claim_seat( + client: &EllipsisClient, + market_pubkey: &Pubkey, +) -> anyhow::Result<()> { + let claim_seat_ix = + create_claim_seat_ix_if_needed(client, market_pubkey, &client.payer.pubkey()).await?; + println!("Claiming seat for pubkey: {}", client.payer.pubkey()); + + if !claim_seat_ix.is_empty() { + let tx = client.sign_send_instructions(claim_seat_ix, vec![]).await?; + println!("Claim seat transaction: {}", tx); + } else { + println!("Seat already created for pubkey: {}", client.payer.pubkey()); + } + + Ok(()) +} diff --git a/src/lib/processor/process_evict_seat.rs b/src/lib/processor/process_evict_seat.rs new file mode 100644 index 0000000..c384b4c --- /dev/null +++ b/src/lib/processor/process_evict_seat.rs @@ -0,0 +1,50 @@ +use std::mem::size_of; + +use ellipsis_client::EllipsisClient; +use phoenix::program::MarketHeader; +use phoenix_sdk::utils::get_evictable_trader_ix; +use phoenix_seat_manager::instruction_builders::{ + create_evict_seat_instruction, EvictTraderAccountBackup, +}; +use solana_sdk::pubkey::Pubkey; + +pub async fn process_evict_seat( + client: &EllipsisClient, + market_pubkey: &Pubkey, + trader_to_evict: &Option, +) -> anyhow::Result<()> { + let market_bytes = client.get_account_data(market_pubkey).await?; + let (header_bytes, _market_bytes) = market_bytes.split_at(size_of::()); + let market_header = bytemuck::try_from_bytes::(header_bytes) + .map_err(|e| anyhow::anyhow!("Error deserializing market header. Error: {:?}", e))?; + + let maybe_evict_trader_ix = if let Some(trader_pubkey) = trader_to_evict { + let evict_trader_state = EvictTraderAccountBackup { + trader_pubkey: *trader_pubkey, + base_token_account_backup: None, + quote_token_account_backup: None, + }; + Some(create_evict_seat_instruction( + market_pubkey, + &market_header.base_params.mint_key, + &market_header.quote_params.mint_key, + trader_pubkey, + vec![evict_trader_state], + )) + } else { + get_evictable_trader_ix(client, market_pubkey).await? + }; + + if let Some(evict_trader_ix) = maybe_evict_trader_ix { + println!("Evicting trader: {}", evict_trader_ix.accounts[13].pubkey); + let tx = client + .sign_send_instructions(vec![evict_trader_ix], vec![]) + .await?; + println!("Evict trader tx: {}", tx); + } else { + println!("Cannot evict a trader when the market's trader state is not full."); + return Ok(()); + } + + Ok(()) +} diff --git a/src/lib/processor/process_get_seat_manager_info.rs b/src/lib/processor/process_get_seat_manager_info.rs index 945124c..a249d0b 100644 --- a/src/lib/processor/process_get_seat_manager_info.rs +++ b/src/lib/processor/process_get_seat_manager_info.rs @@ -27,7 +27,7 @@ pub fn print_seat_manager_struct(seat_manager: &SeatManager, seat_manager_pubkey let dmms: Vec<&Pubkey> = seat_manager .designated_market_makers .iter() - .filter(|dmm| dmm != &&Pubkey::default()) + .filter(|&&dmm| dmm != Pubkey::default()) .collect(); if !dmms.is_empty() { println!("DMMs: {:?}", dmms); diff --git a/src/main.rs b/src/main.rs index a6a5b5c..57f1f05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ use crate::command::PhoenixCLICommand; use anyhow::anyhow; use clap::Parser; use ellipsis_client::EllipsisClient; +use phoenix_cli_processor::processor::process_claim_seat::process_claim_seat; +use phoenix_cli_processor::processor::process_evict_seat::process_evict_seat; use phoenix_cli_processor::processor::{ process_get_all_markets::*, process_get_book_levels::*, process_get_full_book::*, process_get_market::*, process_get_market_status::*, process_get_open_orders::*, @@ -158,6 +160,17 @@ async fn main() -> anyhow::Result<()> { sdk.add_market(&market_pubkey).await?; process_get_seat_manager_info(&sdk.client, &market_pubkey).await?; } + PhoenixCLICommand::ClaimSeat { market_pubkey } => { + sdk.add_market(&market_pubkey).await?; + process_claim_seat(&sdk.client, &market_pubkey).await? + } + PhoenixCLICommand::EvictSeat { + market_pubkey, + trader_to_evict, + } => { + sdk.add_market(&market_pubkey).await?; + process_evict_seat(&sdk.client, &market_pubkey, &trader_to_evict).await? + } } Ok(()) From dd47e9b2222bf694d2e21309c704b7d2fcb32018 Mon Sep 17 00:00:00 2001 From: James Li Date: Mon, 27 Mar 2023 17:09:45 -0500 Subject: [PATCH 3/6] add doc comments to claim and evict --- src/command.rs | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/src/command.rs b/src/command.rs index dc5dec4..e5f8264 100644 --- a/src/command.rs +++ b/src/command.rs @@ -13,17 +13,11 @@ pub enum PhoenixCLICommand { no_gpa: bool, }, /// Get detailed information on a specific market - GetMarket { - market_pubkey: Pubkey, - }, + GetMarket { market_pubkey: Pubkey }, /// Get active traders for a given market - GetTradersForMarket { - market_pubkey: Pubkey, - }, + GetTradersForMarket { market_pubkey: Pubkey }, /// Get the best bid and ask price for a given market - GetTopOfBook { - market_pubkey: Pubkey, - }, + GetTopOfBook { market_pubkey: Pubkey }, /// Get the first N levels of the order book for a given market. /// Default is 10 levels GetBookLevels { @@ -32,17 +26,11 @@ pub enum PhoenixCLICommand { levels: u64, }, /// Get the full order book for a given market - GetFullBook { - market_pubkey: Pubkey, - }, + GetFullBook { market_pubkey: Pubkey }, /// Get the market events that occured in a given transaction signature - GetTransaction { - signature: Signature, - }, + GetTransaction { signature: Signature }, /// Get the current status of a market - GetMarketStatus { - market_pubkey: Pubkey, - }, + GetMarketStatus { market_pubkey: Pubkey }, /// Get the status and address of a seat for a given market and trader GetSeatInfo { market_pubkey: Pubkey, @@ -58,10 +46,9 @@ pub enum PhoenixCLICommand { trader_pubkey: Option, }, /// Send a transaction on chain to allocate a seat for the payer on the given market. This will cost ~.0018 SOL for rent. - /// Note that the seat will have to then be approved by the market authority. - RequestSeat { - market_pubkey: Pubkey, - }, + /// Note that the seat will have to then be approved by the market authority. Only relevant for permissioned markets. + /// For permissionless markets (with an automated seat manager), you can claim a seat with the claim-seat CLI command. + RequestSeat { market_pubkey: Pubkey }, /// Mint tokens to a recipient for a given ticker string (for example SOL or USDC). Default amount is 100_000_000_000. /// This is only for markets associated with the ellipsis token faucet. MintTokens { @@ -86,12 +73,14 @@ pub enum PhoenixCLICommand { #[clap(short, long, required = false, default_value = "100000000")] quote_amount: u64, }, - GetSeatManagerInfo { - market_pubkey: Pubkey, - }, - ClaimSeat { - market_pubkey: Pubkey, - }, + /// For the given market, get the seat manager data fields, including authority, successor, and designated market makers. + GetSeatManagerInfo { market_pubkey: Pubkey }, + /// On the given market, claim a maker seat for the public key of the keypair at the indicated file path. + /// Indicate a different keypair file to use by specifying the file path with flag `-k`. + ClaimSeat { market_pubkey: Pubkey }, + /// Evict a trader from the given market if that market's trader state is at capacity. + /// If no trader is given, this function will greedily find a trader to evict. + /// Note that eviction will not work if the market's trader state is not at capacity. EvictSeat { market_pubkey: Pubkey, trader_to_evict: Option, From f618695e98a882c2fba73bcffd49da513977decc Mon Sep 17 00:00:00 2001 From: James Li Date: Fri, 14 Apr 2023 11:50:13 -0400 Subject: [PATCH 4/6] update versions for SDK and PSM --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 281925c..6b286d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" spl-associated-token-account = { version = "1.1.1", features = [ "no-entrypoint" ] } phoenix-v1 = { version = "0.2.2", features = ["no-entrypoint"] } -phoenix-sdk = {path = "../phoenix-sdk/rust/phoenix-sdk"} +phoenix-sdk = "0.4.0" bytemuck = "1.13.0" reqwest = "0.11.14" bincode = "1.3.3" -phoenix-seat-manager = { path = "../phoenix-seat-manager" } +phoenix-seat-manager = "0.1.0" From ce140376b5cb914902ec81daaa4c32ffd525d8bd Mon Sep 17 00:00:00 2001 From: James Li Date: Fri, 14 Apr 2023 14:00:05 -0400 Subject: [PATCH 5/6] check for market authority managed by PSM in get seat manager info --- .../processor/process_get_seat_manager_info.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib/processor/process_get_seat_manager_info.rs b/src/lib/processor/process_get_seat_manager_info.rs index a249d0b..6026152 100644 --- a/src/lib/processor/process_get_seat_manager_info.rs +++ b/src/lib/processor/process_get_seat_manager_info.rs @@ -1,4 +1,7 @@ +use std::mem::size_of; + use ellipsis_client::EllipsisClient; +use phoenix::program::MarketHeader; use phoenix_seat_manager::{get_seat_manager_address, seat_manager::SeatManager}; use solana_sdk::pubkey::Pubkey; @@ -9,6 +12,18 @@ pub async fn process_get_seat_manager_info( market_pubkey: &Pubkey, ) -> anyhow::Result<()> { let seat_manager_address = get_seat_manager_address(market_pubkey).0; + let market_data = client.get_account_data(market_pubkey).await?; + let market_header = + bytemuck::from_bytes::(market_data.split_at(size_of::()).0); + if market_header.authority != seat_manager_address { + println!( + "Authority for Market {} is not the seat manager.", + market_pubkey + ); + println!("Market authority: {}", market_header.authority); + println!("Seat manager address: {}", seat_manager_address); + return Ok(()); + } let seat_manager_info = get_seat_manager_data_with_market(client, market_pubkey).await?; print_seat_manager_struct(&seat_manager_info, &seat_manager_address); Ok(()) From 0d9704d66c2484cf03f20710e0668de6bde15528 Mon Sep 17 00:00:00 2001 From: James Li Date: Fri, 14 Apr 2023 14:12:10 -0400 Subject: [PATCH 6/6] increment version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6b286d1..5d2d6b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "phoenix-cli" -version = "0.2.4" +version = "0.3.0" description = "CLI and associated library for interacting with the Phoenix program from the command line" edition = "2021" license = "MIT"