diff --git a/Anchor.toml b/Anchor.toml index dbaa023761..7b173fa84a 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -2,9 +2,6 @@ cluster = "localnet" wallet = "~/.config/solana/id.json" -[workspace] -exclude = ["programs/openbook_v2"] - [scripts] # to run local validator tests, use "./test-scripts/run-ts-mocha" in "test" test = "echo" # need to call anchor test to update metadata field in idl before running tests, so just do a noop diff --git a/Cargo.lock b/Cargo.lock index ff6fb03c43..083c775d68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,12 +71,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alloc-traits" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2d54853319fd101b8dd81de382bcbf3e03410a64d8928bbee85a3e7dcde483" - [[package]] name = "anchor-attribute-access-control" version = "0.29.0" @@ -288,7 +282,7 @@ dependencies = [ "num-bigint", "num-traits", "paste", - "rustc_version 0.4.0", + "rustc_version", "zeroize", ] @@ -930,7 +924,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.0", + "rustc_version", "syn 2.0.48", ] @@ -971,14 +965,12 @@ dependencies = [ "hex", "num-integer", "num-traits", - "openbook-v2-light", "phoenix-v1", "pyth", "pyth-client", "pyth-lazer-solana-contract", "pyth-solana-receiver-sdk", "pythnet-sdk", - "serum_dex", "solana-program", "solana-security-txt", "static_assertions", @@ -1099,16 +1091,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" -[[package]] -name = "field-offset" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" -dependencies = [ - "memoffset 0.6.5", - "rustc_version 0.3.3", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1276,15 +1258,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.10.5" @@ -1651,15 +1624,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openbook-v2-light" -version = "0.1.0" -dependencies = [ - "anchor-lang", - "borsh 0.10.3", - "bytemuck", -] - [[package]] name = "parking_lot" version = "0.12.1" @@ -1713,15 +1677,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "phoenix-v1" version = "0.2.4" @@ -1896,7 +1851,7 @@ dependencies = [ "fast-math", "hex", "proc-macro2", - "rustc_version 0.4.0", + "rustc_version", "serde", "sha3 0.10.4", "slow_primes", @@ -2117,22 +2072,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.18", + "semver", ] [[package]] @@ -2147,12 +2093,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -[[package]] -name = "safe-transmute" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a01dab6acf992653be49205bdd549f32f17cb2803e8eacf1560bf97259aae8" - [[package]] name = "scopeguard" version = "1.1.0" @@ -2165,30 +2105,12 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - [[package]] name = "serde" version = "1.0.209" @@ -2251,29 +2173,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "serum_dex" -version = "0.5.6" -source = "git+https://github.com/project-serum/serum-dex?rev=85b4f14#85b4f1499017f22da4b355781bdb5973b3b2646f" -dependencies = [ - "arrayref", - "bincode", - "bytemuck", - "byteorder", - "enumflags2", - "field-offset", - "itertools 0.9.0", - "num-traits", - "num_enum 0.5.9", - "safe-transmute", - "serde", - "solana-program", - "spl-token 3.5.0", - "static_assertions", - "thiserror", - "without-alloc", -] - [[package]] name = "sha2" version = "0.9.9" @@ -2425,7 +2324,7 @@ dependencies = [ "memmap2", "once_cell", "rand_core 0.6.4", - "rustc_version 0.4.0", + "rustc_version", "serde", "serde_bytes", "serde_derive", @@ -2444,7 +2343,7 @@ checksum = "f516f992211a2ab70de5c367190575c97e02d156f9f1d8b76886d673f30e88a2" dependencies = [ "proc-macro2", "quote", - "rustc_version 0.4.0", + "rustc_version", "syn 2.0.48", ] @@ -2497,7 +2396,7 @@ dependencies = [ "parking_lot", "rand 0.7.3", "rand_chacha 0.2.2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "serde", "serde_bytes", @@ -2548,7 +2447,7 @@ dependencies = [ "qstring", "rand 0.7.3", "rand_chacha 0.2.2", - "rustc_version 0.4.0", + "rustc_version", "rustversion", "serde", "serde_bytes", @@ -2985,12 +2884,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "ucd-trie" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" - [[package]] name = "uint" version = "0.9.1" @@ -3212,15 +3105,6 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" -[[package]] -name = "without-alloc" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e34736feff52a0b3e5680927e947a4d8fac1f0b80dc8120b080dd8de24d75e2" -dependencies = [ - "alloc-traits", -] - [[package]] name = "wyz" version = "0.5.1" diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index a677d4b74c..22b43a754d 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -33,14 +33,12 @@ uint = { version = "0.9.1", default-features = false } num-integer = "0.1.44" arrayref = "0.3.6" base64 = "0.13.0" -serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "85b4f14", version = "0.5.6", features = ["no-entrypoint"] } enumflags2 = "0.6.4" phoenix-v1 = { git = "https://github.com/drift-labs/phoenix-v1", rev = "7703c5", version = "0.2.4", features = ["no-entrypoint"] } solana-security-txt = "1.1.0" static_assertions = "1.1.0" drift-macros = { git = "https://github.com/drift-labs/drift-macros.git", rev = "c57d87" } switchboard = { path = "../switchboard", features = ["no-entrypoint"] } -openbook-v2-light = { path = "../openbook_v2", features = ["no-entrypoint"] } switchboard-on-demand = { path = "../switchboard-on-demand", features = ["no-entrypoint"] } byteorder = "1.4.3" diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 607eaeced7..2a4db30caa 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -5,7 +5,6 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use phoenix::quantities::WrapperU64; use pyth_solana_receiver_sdk::cpi::accounts::InitPriceUpdate; use pyth_solana_receiver_sdk::program::PythSolanaReceiver; -use serum_dex::state::ToAlignedBytes; use std::convert::{identity, TryInto}; use std::mem::size_of; @@ -42,13 +41,8 @@ use crate::state::amm_cache::{AmmCache, CacheInfo, AMM_POSITIONS_CACHE}; use crate::state::events::{ CurveRecord, DepositDirection, DepositExplanation, DepositRecord, SpotMarketVaultDepositRecord, }; -use crate::state::fulfillment_params::openbook_v2::{ - OpenbookV2Context, OpenbookV2FulfillmentConfig, -}; use crate::state::fulfillment_params::phoenix::PhoenixMarketContext; use crate::state::fulfillment_params::phoenix::PhoenixV1FulfillmentConfig; -use crate::state::fulfillment_params::serum::SerumContext; -use crate::state::fulfillment_params::serum::SerumV3FulfillmentConfig; use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use crate::state::if_rebalance_config::{IfRebalanceConfig, IfRebalanceConfigParams}; use crate::state::insurance_fund_stake::InsuranceFundStake; @@ -395,221 +389,6 @@ pub fn handle_update_spot_market_pool_id( Ok(()) } -pub fn handle_initialize_serum_fulfillment_config( - ctx: Context, - market_index: u16, -) -> Result<()> { - validate!( - market_index != QUOTE_SPOT_MARKET_INDEX, - ErrorCode::InvalidSpotMarketAccount, - "Cant add serum market to quote asset" - )?; - - let base_spot_market = load!(&ctx.accounts.base_spot_market)?; - let quote_spot_market = load!(&ctx.accounts.quote_spot_market)?; - - let serum_program_id = crate::ids::serum_program::id(); - validate!( - ctx.accounts.serum_program.key() == serum_program_id, - ErrorCode::InvalidSerumProgram - )?; - - let serum_market_key = ctx.accounts.serum_market.key(); - - let serum_context = SerumContext { - serum_program: &ctx.accounts.serum_program, - serum_market: &ctx.accounts.serum_market, - serum_open_orders: &ctx.accounts.serum_open_orders, - }; - - let market_state = serum_context.load_serum_market()?; - - validate!( - identity(market_state.coin_mint) == base_spot_market.mint.to_aligned_bytes(), - ErrorCode::InvalidSerumMarket, - "Invalid base mint" - )?; - - validate!( - identity(market_state.pc_mint) == quote_spot_market.mint.to_aligned_bytes(), - ErrorCode::InvalidSerumMarket, - "Invalid quote mint" - )?; - - let market_step_size = market_state.coin_lot_size; - let valid_step_size = base_spot_market.order_step_size >= market_step_size - && base_spot_market - .order_step_size - .rem_euclid(market_step_size) - == 0; - - validate!( - valid_step_size, - ErrorCode::InvalidSerumMarket, - "base market step size ({}) not a multiple of serum step size ({})", - base_spot_market.order_step_size, - market_step_size - )?; - - let market_tick_size = market_state.pc_lot_size; - let valid_tick_size = base_spot_market.order_tick_size >= market_tick_size - && base_spot_market - .order_tick_size - .rem_euclid(market_tick_size) - == 0; - - validate!( - valid_tick_size, - ErrorCode::InvalidSerumMarket, - "base market tick size ({}) not a multiple of serum tick size ({})", - base_spot_market.order_tick_size, - market_tick_size - )?; - - drop(market_state); - - let open_orders_seeds: &[&[u8]] = &[b"serum_open_orders", serum_market_key.as_ref()]; - controller::pda::seed_and_create_pda( - ctx.program_id, - &ctx.accounts.admin.to_account_info(), - &Rent::get()?, - size_of::() + 12, - &serum_program_id, - &ctx.accounts.system_program.to_account_info(), - &ctx.accounts.serum_open_orders, - open_orders_seeds, - )?; - - let open_orders = serum_context.load_open_orders()?; - validate!( - open_orders.account_flags == 0, - ErrorCode::InvalidSerumOpenOrders, - "Serum open orders already initialized" - )?; - drop(open_orders); - - serum_context.invoke_init_open_orders( - &ctx.accounts.drift_signer, - &ctx.accounts.rent, - ctx.accounts.state.signer_nonce, - )?; - - let serum_fulfillment_config_key = ctx.accounts.serum_fulfillment_config.key(); - let mut serum_fulfillment_config = ctx.accounts.serum_fulfillment_config.load_init()?; - *serum_fulfillment_config = serum_context - .to_serum_v3_fulfillment_config(&serum_fulfillment_config_key, market_index)?; - - Ok(()) -} - -pub fn handle_update_serum_fulfillment_config_status( - ctx: Context, - status: SpotFulfillmentConfigStatus, -) -> Result<()> { - let mut config = load_mut!(ctx.accounts.serum_fulfillment_config)?; - msg!("config.status {:?} -> {:?}", config.status, status); - config.status = status; - Ok(()) -} - -pub fn handle_update_serum_vault(ctx: Context) -> Result<()> { - let vault = &ctx.accounts.srm_vault; - validate!( - vault.mint == crate::ids::srm_mint::id() || vault.mint == crate::ids::msrm_mint::id(), - ErrorCode::InvalidSrmVault, - "vault did not hav srm or msrm mint" - )?; - - validate!( - vault.owner == ctx.accounts.state.signer, - ErrorCode::InvalidVaultOwner, - "vault owner was not program signer" - )?; - - let state = &mut ctx.accounts.state; - - msg!("state.srm_vault {:?} -> {:?}", state.srm_vault, vault.key()); - state.srm_vault = vault.key(); - - Ok(()) -} - -pub fn handle_initialize_openbook_v2_fulfillment_config( - ctx: Context, - market_index: u16, -) -> Result<()> { - validate!( - market_index != QUOTE_SPOT_MARKET_INDEX, - ErrorCode::InvalidSpotMarketAccount, - "Cannot add openbook v2 market to quote asset" - )?; - - let base_spot_market = load!(&ctx.accounts.base_spot_market)?; - let quote_spot_market = load!(&ctx.accounts.quote_spot_market)?; - - let openbook_v2_program_id = openbook_v2_light::id(); - - validate!( - ctx.accounts.openbook_v2_program.key() == openbook_v2_program_id, - ErrorCode::InvalidOpenbookV2Program - )?; - - let openbook_v2_market_context = OpenbookV2Context { - openbook_v2_program: &ctx.accounts.openbook_v2_program, - openbook_v2_market: &ctx.accounts.openbook_v2_market, - }; - let market = openbook_v2_market_context.load_openbook_v2_market()?; - validate!( - market.base_mint == base_spot_market.mint, - ErrorCode::InvalidOpenbookV2Market, - "Invalid base mint" - )?; - - validate!( - market.quote_mint == quote_spot_market.mint, - ErrorCode::InvalidOpenbookV2Market, - "Invalid quote mint" - )?; - - validate!( - market.taker_fee == 0, - ErrorCode::InvalidOpenbookV2Market, - "Fee must be 0" - )?; - - let market_step_size = market.base_lot_size as u64; - let valid_step_size = base_spot_market.order_step_size >= market_step_size - && base_spot_market - .order_step_size - .rem_euclid(market_step_size) - == 0; - - validate!( - valid_step_size, - ErrorCode::InvalidOpenbookV2Market, - "base market step size ({}) not a multiple of Openbook V2 base lot size ({})", - base_spot_market.order_step_size, - market_step_size - )?; - - let openbook_v2_fulfillment_config_key = ctx.accounts.openbook_v2_fulfillment_config.key(); - let mut openbook_v2_fulfillment_config = - ctx.accounts.openbook_v2_fulfillment_config.load_init()?; - *openbook_v2_fulfillment_config = openbook_v2_market_context - .to_openbook_v2_fulfillment_config(&openbook_v2_fulfillment_config_key, market_index)?; - Ok(()) -} - -pub fn handle_update_openbook_v2_fulfillment_config_status( - ctx: Context, - status: SpotFulfillmentConfigStatus, -) -> Result<()> { - let mut config = load_mut!(ctx.accounts.openbook_v2_fulfillment_config)?; - msg!("config.status {:?} -> {:?}", config.status, status); - config.status = status; - Ok(()) -} - pub fn handle_initialize_phoenix_fulfillment_config( ctx: Context, market_index: u16, @@ -5320,66 +5099,6 @@ pub struct DeleteInitializedSpotMarket<'info> { pub token_program: Interface<'info, TokenInterface>, } -#[derive(Accounts)] -#[instruction(market_index: u16)] -pub struct InitializeSerumFulfillmentConfig<'info> { - #[account( - seeds = [b"spot_market", market_index.to_le_bytes().as_ref()], - bump, - )] - pub base_spot_market: AccountLoader<'info, SpotMarket>, - #[account( - seeds = [b"spot_market", 0_u16.to_le_bytes().as_ref()], - bump, - )] - pub quote_spot_market: AccountLoader<'info, SpotMarket>, - #[account( - mut, - has_one = admin - )] - pub state: Box>, - /// CHECK: checked in ix - pub serum_program: AccountInfo<'info>, - /// CHECK: checked in ix - pub serum_market: AccountInfo<'info>, - #[account( - mut, - seeds = [b"serum_open_orders".as_ref(), serum_market.key.as_ref()], - bump, - )] - /// CHECK: checked in ix - pub serum_open_orders: AccountInfo<'info>, - #[account( - constraint = state.signer.eq(&drift_signer.key()) - )] - /// CHECK: program signer - pub drift_signer: AccountInfo<'info>, - #[account( - init, - seeds = [b"serum_fulfillment_config".as_ref(), serum_market.key.as_ref()], - space = SerumV3FulfillmentConfig::SIZE, - bump, - payer = admin, - )] - pub serum_fulfillment_config: AccountLoader<'info, SerumV3FulfillmentConfig>, - #[account(mut)] - pub admin: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct UpdateSerumFulfillmentConfig<'info> { - #[account( - has_one = admin - )] - pub state: Box>, - #[account(mut)] - pub serum_fulfillment_config: AccountLoader<'info, SerumV3FulfillmentConfig>, - #[account(mut)] - pub admin: Signer<'info>, -} - #[derive(Accounts)] #[instruction(market_index: u16)] pub struct InitializePhoenixFulfillmentConfig<'info> { @@ -5906,59 +5625,6 @@ pub struct DeletePrelaunchOracle<'info> { pub state: Box>, } -#[derive(Accounts)] -#[instruction(market_index: u16)] -pub struct InitializeOpenbookV2FulfillmentConfig<'info> { - #[account( - seeds = [b"spot_market", market_index.to_le_bytes().as_ref()], - bump, - )] - pub base_spot_market: AccountLoader<'info, SpotMarket>, - #[account( - seeds = [b"spot_market", 0_u16.to_le_bytes().as_ref()], - bump, - )] - pub quote_spot_market: AccountLoader<'info, SpotMarket>, - #[account( - mut, - has_one = admin - )] - pub state: Box>, - /// CHECK: checked in ix - pub openbook_v2_program: AccountInfo<'info>, - /// CHECK: checked in ix - pub openbook_v2_market: AccountInfo<'info>, - #[account( - constraint = state.signer.eq(&drift_signer.key()) - )] - /// CHECK: program signer - pub drift_signer: AccountInfo<'info>, - #[account( - init, - seeds = [b"openbook_v2_fulfillment_config".as_ref(), openbook_v2_market.key.as_ref()], - space = OpenbookV2FulfillmentConfig::SIZE, - bump, - payer = admin, - )] - pub openbook_v2_fulfillment_config: AccountLoader<'info, OpenbookV2FulfillmentConfig>, - #[account(mut)] - pub admin: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct UpdateOpenbookV2FulfillmentConfig<'info> { - #[account( - has_one = admin - )] - pub state: Box>, - #[account(mut)] - pub openbook_v2_fulfillment_config: AccountLoader<'info, OpenbookV2FulfillmentConfig>, - #[account(mut)] - pub admin: Signer<'info>, -} - #[derive(Accounts)] #[instruction(feed_id : [u8; 32])] pub struct InitPythPullPriceFeed<'info> { diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 6019a45199..19c7614b8d 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -52,9 +52,7 @@ use crate::state::events::LPSettleRecord; use crate::state::events::{DeleteUserRecord, OrderActionExplanation, SignedMsgOrderRecord}; use crate::state::fill_mode::FillMode; use crate::state::fulfillment_params::drift::MatchFulfillmentParams; -use crate::state::fulfillment_params::openbook_v2::OpenbookV2FulfillmentParams; use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams; -use crate::state::fulfillment_params::serum::SerumFulfillmentParams; use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use crate::state::insurance_fund_stake::InsuranceFundStake; use crate::state::lp_pool::Constituent; @@ -226,10 +224,8 @@ pub fn handle_revert_fill<'info>(ctx: Context) -> Result<()> { #[derive(Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Eq, Default)] pub enum SpotFulfillmentType { #[default] - SerumV3, Match, PhoenixV1, - OpenbookV2, } #[access_control( @@ -295,17 +291,6 @@ fn fill_spot_order<'c: 'info, 'info>( }; let mut fulfillment_params: Box = match fulfillment_type { - SpotFulfillmentType::SerumV3 => { - let base_market = spot_market_map.get_ref(&market_index)?; - let quote_market = spot_market_map.get_quote_spot_market()?; - Box::new(SerumFulfillmentParams::new( - remaining_accounts_iter, - &ctx.accounts.state, - &base_market, - "e_market, - clock.unix_timestamp, - )?) - } SpotFulfillmentType::PhoenixV1 => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; @@ -316,17 +301,6 @@ fn fill_spot_order<'c: 'info, 'info>( "e_market, )?) } - SpotFulfillmentType::OpenbookV2 => { - let base_market = spot_market_map.get_ref(&market_index)?; - let quote_market = spot_market_map.get_quote_spot_market()?; - Box::new(OpenbookV2FulfillmentParams::new( - remaining_accounts_iter, - &ctx.accounts.state, - &base_market, - "e_market, - clock.unix_timestamp, - )?) - } SpotFulfillmentType::Match => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 1883e5af7e..1e9fdc567c 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -70,9 +70,7 @@ use crate::state::events::{ }; use crate::state::fill_mode::FillMode; use crate::state::fulfillment_params::drift::MatchFulfillmentParams; -use crate::state::fulfillment_params::openbook_v2::OpenbookV2FulfillmentParams; use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams; -use crate::state::fulfillment_params::serum::SerumFulfillmentParams; use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use crate::state::margin_calculation::MarginContext; use crate::state::oracle::StrictOraclePrice; @@ -3128,17 +3126,6 @@ pub fn handle_place_and_take_spot_order<'c: 'info, 'info>( let is_immediate_or_cancel = params.is_immediate_or_cancel(); let mut fulfillment_params: Box = match fulfillment_type { - SpotFulfillmentType::SerumV3 => { - let base_market = spot_market_map.get_ref(&market_index)?; - let quote_market = spot_market_map.get_quote_spot_market()?; - Box::new(SerumFulfillmentParams::new( - remaining_accounts_iter, - &ctx.accounts.state, - &base_market, - "e_market, - clock.unix_timestamp, - )?) - } SpotFulfillmentType::PhoenixV1 => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; @@ -3149,17 +3136,6 @@ pub fn handle_place_and_take_spot_order<'c: 'info, 'info>( "e_market, )?) } - SpotFulfillmentType::OpenbookV2 => { - let base_market = spot_market_map.get_ref(&market_index)?; - let quote_market = spot_market_map.get_quote_spot_market()?; - Box::new(OpenbookV2FulfillmentParams::new( - remaining_accounts_iter, - &ctx.accounts.state, - &base_market, - "e_market, - clock.unix_timestamp, - )?) - } SpotFulfillmentType::Match => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; @@ -3276,17 +3252,6 @@ pub fn handle_place_and_make_spot_order<'c: 'info, 'info>( let market_index = params.market_index; let mut fulfillment_params: Box = match fulfillment_type { - SpotFulfillmentType::SerumV3 => { - let base_market = spot_market_map.get_ref(&market_index)?; - let quote_market = spot_market_map.get_quote_spot_market()?; - Box::new(SerumFulfillmentParams::new( - remaining_accounts_iter, - &ctx.accounts.state, - &base_market, - "e_market, - clock.unix_timestamp, - )?) - } SpotFulfillmentType::PhoenixV1 => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; @@ -3297,17 +3262,6 @@ pub fn handle_place_and_make_spot_order<'c: 'info, 'info>( "e_market, )?) } - SpotFulfillmentType::OpenbookV2 => { - let base_market = spot_market_map.get_ref(&market_index)?; - let quote_market = spot_market_map.get_quote_spot_market()?; - Box::new(OpenbookV2FulfillmentParams::new( - remaining_accounts_iter, - &ctx.accounts.state, - &base_market, - "e_market, - clock.unix_timestamp, - )?) - } SpotFulfillmentType::Match => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index df9d1431d2..33f058bb16 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -951,33 +951,6 @@ pub mod drift { handle_delete_initialized_spot_market(ctx, market_index) } - pub fn initialize_serum_fulfillment_config( - ctx: Context, - market_index: u16, - ) -> Result<()> { - handle_initialize_serum_fulfillment_config(ctx, market_index) - } - - pub fn update_serum_fulfillment_config_status( - ctx: Context, - status: SpotFulfillmentConfigStatus, - ) -> Result<()> { - handle_update_serum_fulfillment_config_status(ctx, status) - } - - pub fn initialize_openbook_v2_fulfillment_config( - ctx: Context, - market_index: u16, - ) -> Result<()> { - handle_initialize_openbook_v2_fulfillment_config(ctx, market_index) - } - - pub fn openbook_v2_fulfillment_config_status( - ctx: Context, - status: SpotFulfillmentConfigStatus, - ) -> Result<()> { - handle_update_openbook_v2_fulfillment_config_status(ctx, status) - } pub fn initialize_phoenix_fulfillment_config( ctx: Context, market_index: u16, diff --git a/programs/drift/src/state/amm_cache.rs b/programs/drift/src/state/amm_cache.rs index 1e485b2289..f5140fd175 100644 --- a/programs/drift/src/state/amm_cache.rs +++ b/programs/drift/src/state/amm_cache.rs @@ -56,7 +56,8 @@ pub struct CacheInfo { pub oracle_validity: u8, pub lp_status_for_perp_market: u8, pub amm_position_scalar: u8, - pub _padding: [u8; 34], + pub _padding: [u8; 32], + pub _padding2: [u8; 2], } impl Size for CacheInfo { @@ -86,7 +87,8 @@ impl Default for CacheInfo { lp_status_for_perp_market: 0u8, amm_position_scalar: 0u8, market_index: 0u16, - _padding: [0u8; 34], + _padding: [0u8; 32], + _padding2: [0u8; 2], } } } diff --git a/programs/drift/src/state/fulfillment_params/mod.rs b/programs/drift/src/state/fulfillment_params/mod.rs index dc1fae2416..9795e42ecc 100644 --- a/programs/drift/src/state/fulfillment_params/mod.rs +++ b/programs/drift/src/state/fulfillment_params/mod.rs @@ -1,4 +1,2 @@ pub mod drift; -pub mod openbook_v2; pub mod phoenix; -pub mod serum; diff --git a/programs/drift/src/state/fulfillment_params/openbook_v2.rs b/programs/drift/src/state/fulfillment_params/openbook_v2.rs deleted file mode 100644 index 869602ac16..0000000000 --- a/programs/drift/src/state/fulfillment_params/openbook_v2.rs +++ /dev/null @@ -1,508 +0,0 @@ -#![allow(unused)] // unused when target_os is not solana -use crate::controller::position::PositionDirection; -use crate::error::{DriftResult, ErrorCode}; -use crate::instructions::SpotFulfillmentType; -use crate::math::safe_math::SafeMath; -use crate::math::serum::{ - calculate_price_from_serum_limit_price, calculate_serum_limit_price, - calculate_serum_max_coin_qty, -}; -use crate::math::spot_withdraw::validate_spot_market_vault_amount; -use crate::signer::get_signer_seeds; -use crate::state::events::OrderActionExplanation; -use crate::state::load_ref::load_ref; -use crate::state::spot_fulfillment_params::{ExternalSpotFill, SpotFulfillmentParams}; -use crate::state::spot_market::{SpotBalanceType, SpotFulfillmentConfigStatus, SpotMarket}; -use crate::state::state::State; -use crate::state::traits::Size; -use crate::{load, validate}; -use anchor_lang::prelude::*; -use anchor_lang::prelude::{Account, Program, System}; -use anchor_lang::{account, InstructionData, Key}; -use anchor_spl::token::{Token, TokenAccount}; -use arrayref::array_ref; -use openbook_v2_light::instruction::PlaceTakeOrder; -use openbook_v2_light::{ - BookSide, Market, PlaceOrderType, Side, OPEN_ORDERS_ACCOUNT_DISCRIMINATOR, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::{AccountMeta, Instruction}; -use solana_program::program::invoke_signed_unchecked; -use solana_program::pubkey::Pubkey; -use std::cell::Ref; -use std::convert::TryFrom; - -#[account(zero_copy(unsafe))] -#[derive(Default, PartialEq, Eq, Debug)] -#[repr(C)] -pub struct OpenbookV2FulfillmentConfig { - pub pubkey: Pubkey, //32 - pub openbook_v2_program_id: Pubkey, // 64 - pub openbook_v2_market: Pubkey, // 96 - pub openbook_v2_market_authority: Pubkey, // 128 - pub openbook_v2_event_heap: Pubkey, // 160 - pub openbook_v2_bids: Pubkey, // 192 - pub openbook_v2_asks: Pubkey, // 224 - pub openbook_v2_base_vault: Pubkey, // 256 - pub openbook_v2_quote_vault: Pubkey, // 288 - pub market_index: u16, // 290 - pub fulfillment_type: SpotFulfillmentType, // 291 - pub status: SpotFulfillmentConfigStatus, // 292 - pub padding: [u8; 4], // 296 -} - -impl Size for OpenbookV2FulfillmentConfig { - const SIZE: usize = 304; -} - -pub struct OpenbookV2Context<'a, 'b> { - pub openbook_v2_program: &'a AccountInfo<'b>, - pub openbook_v2_market: &'a AccountInfo<'b>, -} - -impl<'a, 'b> OpenbookV2Context<'a, 'b> { - pub fn load_openbook_v2_market(&self) -> DriftResult { - let market = - load_ref(self.openbook_v2_market).map_err(|_| ErrorCode::FailedOpenbookV2CPI)?; - Ok(*market) - } - - pub fn to_openbook_v2_fulfillment_config( - &self, - openbook_v2_fulfillment_config_key: &Pubkey, - market_index: u16, - ) -> DriftResult { - let market = self - .load_openbook_v2_market() - .map_err(|_| ErrorCode::FailedOpenbookV2CPI)?; - Ok(OpenbookV2FulfillmentConfig { - pubkey: *openbook_v2_fulfillment_config_key, - openbook_v2_program_id: *self.openbook_v2_program.key, - openbook_v2_market: *self.openbook_v2_market.key, - openbook_v2_market_authority: market.market_authority, - openbook_v2_event_heap: market.event_heap, - openbook_v2_bids: market.bids, - openbook_v2_asks: market.asks, - openbook_v2_base_vault: market.market_base_vault, - openbook_v2_quote_vault: market.market_quote_vault, - market_index, - fulfillment_type: SpotFulfillmentType::OpenbookV2, - status: SpotFulfillmentConfigStatus::Enabled, - padding: [0; 4], - }) - } -} - -pub struct OpenbookV2FulfillmentParams<'a, 'b> { - pub drift_signer: &'a AccountInfo<'b>, // same as penalty payer - pub openbook_v2_context: OpenbookV2Context<'a, 'b>, - pub openbook_v2_market_authority: &'a AccountInfo<'b>, - pub openbook_v2_event_heap: &'a AccountInfo<'b>, - pub openbook_v2_bids: &'a AccountInfo<'b>, - pub openbook_v2_asks: &'a AccountInfo<'b>, - pub openbook_v2_base_vault: &'a AccountInfo<'b>, - pub openbook_v2_quote_vault: &'a AccountInfo<'b>, - pub base_market_vault: Box>, - pub quote_market_vault: Box>, - pub token_program: Program<'b, Token>, - pub system_program: Program<'b, System>, - pub signer_nonce: u8, - pub now: i64, - pub remaining_ooa_accounts: Vec>, -} - -impl<'a, 'b> OpenbookV2FulfillmentParams<'a, 'b> { - #[allow(clippy::type_complexity)] - pub fn new<'c: 'b>( - account_info_iter: &'a mut std::iter::Peekable>>, - state: &State, - base_market: &SpotMarket, - quote_market: &SpotMarket, - now: i64, - ) -> DriftResult { - let account_info_vec = account_info_iter.collect::>(); - let mut remaining_ooa_accounts = account_info_vec - .iter() - .skip(14) - .filter(|acc| { - acc.data - .borrow() - .starts_with(&OPEN_ORDERS_ACCOUNT_DISCRIMINATOR) - }) - .map(|acc| UncheckedAccount::try_from(*acc)) - .collect::>(); - remaining_ooa_accounts.truncate(3); - let account_infos = array_ref![account_info_vec, 0, 14]; - let [openbook_v2_fulfillment_config, drift_signer, openbook_v2_program, openbook_v2_market, openbook_v2_market_authority, openbook_v2_event_heap, openbook_v2_bids, openbook_v2_asks, openbook_v2_base_vault, openbook_v2_quote_vault, base_market_vault, quote_market_vault, token_program, system_program] = - account_infos; - let openbook_v2_fulfillment_config_loader: AccountLoader = - AccountLoader::try_from(openbook_v2_fulfillment_config).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?; - let openbook_v2_fulfillment_config = load!(openbook_v2_fulfillment_config_loader)?; - - validate!( - openbook_v2_fulfillment_config.status == SpotFulfillmentConfigStatus::Enabled, - ErrorCode::SpotFulfillmentConfigDisabled - )?; - - validate!( - &state.signer == drift_signer.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - openbook_v2_fulfillment_config.market_index == base_market.market_index, - ErrorCode::InvalidFulfillmentConfig, - "config market index {} does not equal base asset index {}", - openbook_v2_fulfillment_config.market_index, - base_market.market_index - )?; - - validate!( - &base_market.vault == base_market_vault.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - "e_market.vault == quote_market_vault.key, - ErrorCode::InvalidFulfillmentConfig - )?; - validate!( - &openbook_v2_fulfillment_config.openbook_v2_program_id == openbook_v2_program.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - &openbook_v2_fulfillment_config.openbook_v2_market_authority - == openbook_v2_market_authority.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - &openbook_v2_fulfillment_config.openbook_v2_event_heap == openbook_v2_event_heap.key, - ErrorCode::InvalidFulfillmentConfig, - "Openbook V2 eventheap key does not match" - )?; - validate!( - &openbook_v2_fulfillment_config.openbook_v2_bids == openbook_v2_bids.key, - ErrorCode::InvalidFulfillmentConfig, - "Openbook V2 bids key does not match" - )?; - validate!( - &openbook_v2_fulfillment_config.openbook_v2_asks == openbook_v2_asks.key, - ErrorCode::InvalidFulfillmentConfig, - "Openbook V2 asks key does not match" - )?; - validate!( - &openbook_v2_fulfillment_config.openbook_v2_base_vault == openbook_v2_base_vault.key, - ErrorCode::InvalidFulfillmentConfig, - "OpenbookV2 quote vault key does not match" - )?; - - validate!( - &openbook_v2_fulfillment_config.openbook_v2_quote_vault == openbook_v2_quote_vault.key, - ErrorCode::InvalidFulfillmentConfig, - "OpenbookV2 quote vault key does not match" - )?; - - validate!( - &openbook_v2_fulfillment_config.openbook_v2_market == openbook_v2_market.key, - ErrorCode::InvalidFulfillmentConfig - )?; - let base_market_vault: Box> = - Box::new(Account::try_from(base_market_vault).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?); - let quote_market_vault: Box> = - Box::new(Account::try_from(quote_market_vault).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?); - let token_program: Program = Program::try_from(*token_program).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?; - let system_program: Program = Program::try_from(*system_program).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?; - Ok(OpenbookV2FulfillmentParams { - drift_signer, - openbook_v2_context: OpenbookV2Context { - openbook_v2_program, - openbook_v2_market, - }, - openbook_v2_market_authority, - openbook_v2_event_heap, - openbook_v2_bids, - openbook_v2_asks, - openbook_v2_base_vault, - openbook_v2_quote_vault, - base_market_vault, - quote_market_vault, - token_program, - system_program, - signer_nonce: state.signer_nonce, - now, - remaining_ooa_accounts, - }) - } -} - -impl<'a, 'b> OpenbookV2FulfillmentParams<'a, 'b> { - pub fn invoke_new_order(&self, data: Vec) -> DriftResult { - let mut accounts = vec![ - AccountMeta::new(*self.drift_signer.key, true), - AccountMeta::new(*self.drift_signer.key, true), - AccountMeta::new(*self.openbook_v2_context.openbook_v2_market.key, false), - AccountMeta::new_readonly(*self.openbook_v2_market_authority.key, false), - AccountMeta::new(*self.openbook_v2_bids.key, false), - AccountMeta::new(*self.openbook_v2_asks.key, false), - AccountMeta::new(*self.openbook_v2_base_vault.key, false), - AccountMeta::new(*self.openbook_v2_quote_vault.key, false), - AccountMeta::new(*self.openbook_v2_event_heap.key, false), - AccountMeta::new(self.base_market_vault.key(), false), - AccountMeta::new(self.quote_market_vault.key(), false), - AccountMeta::new_readonly(*self.openbook_v2_context.openbook_v2_program.key, false), - AccountMeta::new_readonly(*self.openbook_v2_context.openbook_v2_program.key, false), - AccountMeta::new_readonly(*self.token_program.key, false), - AccountMeta::new_readonly(*self.system_program.key, false), - AccountMeta::new_readonly(*self.openbook_v2_context.openbook_v2_program.key, false), - ]; - let mut account_infos = vec![ - self.openbook_v2_context.openbook_v2_program.clone(), - self.drift_signer.clone(), - self.drift_signer.clone(), - self.openbook_v2_context.openbook_v2_market.clone(), - self.openbook_v2_market_authority.clone(), - self.openbook_v2_bids.clone(), - self.openbook_v2_asks.clone(), - self.openbook_v2_base_vault.clone(), - self.openbook_v2_quote_vault.clone(), - self.openbook_v2_event_heap.clone(), - self.base_market_vault.to_account_info(), - self.quote_market_vault.to_account_info(), - self.openbook_v2_context.openbook_v2_program.clone(), - self.openbook_v2_context.openbook_v2_program.clone(), - self.token_program.to_account_info(), - self.system_program.to_account_info(), - self.openbook_v2_context.openbook_v2_program.clone(), - ]; - for unchecked_account in self.remaining_ooa_accounts.iter() { - accounts.push(AccountMeta::new(*unchecked_account.key, false)); - account_infos.push(unchecked_account.to_account_info()); - } - let new_place_take_order_instruction = Instruction { - program_id: *self.openbook_v2_context.openbook_v2_program.key, - accounts, - data, - }; - let signer_seeds = get_signer_seeds(&self.signer_nonce); - let signers_seeds = &[&signer_seeds[..]]; - - invoke_signed_unchecked( - &new_place_take_order_instruction, - &account_infos, - signers_seeds, - ) - .map_err(|e| { - msg!("{:?}", e); - ErrorCode::FailedOpenbookV2CPI - })?; - - Ok(()) - } -} - -impl<'a, 'b> SpotFulfillmentParams for OpenbookV2FulfillmentParams<'a, 'b> { - fn is_external(&self) -> bool { - true - } - fn fulfill_order( - &mut self, - taker_direction: PositionDirection, - taker_price: u64, - taker_base_asset_amount: u64, - taker_max_quote_asset_amount: u64, - ) -> DriftResult { - // load openbook v2 market - let market = self.openbook_v2_context.load_openbook_v2_market()?; - // coin - base - // pc - quote - - let serum_max_coin_qty = - calculate_serum_max_coin_qty(taker_base_asset_amount, market.base_lot_size as u64)?; - - let price_lots = calculate_serum_limit_price( - taker_price, - market.quote_lot_size as u64, - market.base_decimals as u32, - market.base_lot_size as u64, - taker_direction, - )?; - - let max_quote_lots_including_fees = (market.quote_lot_size as u64) - .safe_mul(price_lots)? - .safe_mul(serum_max_coin_qty)? - .min(taker_max_quote_asset_amount) as i64; - let max_base_lots = taker_base_asset_amount as i64 / market.base_lot_size; - // let max_quote_lots_including_fees = if taker_max_quote_asset_amount == u64::MAX { (price_lots as i64 * max_base_lots)/market.quote_lot_size } else {taker_max_quote_asset_amount as i64/market.quote_lot_size}; - - let openbook_v2_order_side = match taker_direction { - PositionDirection::Long => Side::Bid, - PositionDirection::Short => Side::Ask, - }; - // the openbook v2 will take care of what is better if the price_lots or max_base or max_quote - let args = PlaceTakeOrder { - side: openbook_v2_order_side, - price_lots: price_lots as i64, // i64::MAX, // 8 - // price_lots: i64::MAX, - max_base_lots, // 8 - max_quote_lots_including_fees, // 8 - order_type: PlaceOrderType::Market, // 1 - limit: 20, // why 50? - // total - 27 - }; - let data = args.data(); - let base_before = self.base_market_vault.amount; - let quote_before = self.quote_market_vault.amount; - - self.invoke_new_order(data)?; - - self.base_market_vault.reload().map_err(|_e| { - msg!("Failed to reload base_market_vault"); - ErrorCode::FailedOpenbookV2CPI - })?; - self.quote_market_vault.reload().map_err(|_e| { - msg!("Failed to reload quote_market_vault"); - ErrorCode::FailedOpenbookV2CPI - })?; - - let base_after = self.base_market_vault.amount; - let quote_after = self.quote_market_vault.amount; - - let (base_update_direction, base_asset_amount_filled) = if base_after > base_before { - (SpotBalanceType::Deposit, base_after.safe_sub(base_before)?) - } else { - (SpotBalanceType::Borrow, base_before.safe_sub(base_after)?) - }; - - if base_asset_amount_filled == 0 { - msg!("No base filled on openbook v2"); - return Ok(ExternalSpotFill::empty()); - } - - let (quote_update_direction, quote_asset_amount_filled) = - if base_update_direction == SpotBalanceType::Borrow { - let quote_asset_amount_delta = quote_after.safe_sub(quote_before)?; - (SpotBalanceType::Deposit, quote_asset_amount_delta) - } else { - let quote_asset_amount_delta = quote_before.safe_sub(quote_after)?; - (SpotBalanceType::Borrow, quote_asset_amount_delta) - }; - Ok(ExternalSpotFill { - base_asset_amount_filled, - quote_asset_amount_filled, - base_update_direction, - quote_update_direction, - fee: 0, - unsettled_referrer_rebate: 0, - settled_referrer_rebate: 0, - }) - } - fn get_best_bid_and_ask(&self) -> DriftResult<(Option, Option)> { - let market = self.openbook_v2_context.load_openbook_v2_market()?; - let bid_data = self.openbook_v2_bids.data.borrow(); - let bid = bytemuck::try_from_bytes::(&bid_data[8..]).map_err(|_| { - msg!("Failed to parse OpenbookV2 bids"); - ErrorCode::FailedOpenbookV2CPI - })?; - let ask_data = self.openbook_v2_asks.data.borrow(); - let ask = bytemuck::try_from_bytes::(&ask_data[8..]).map_err(|_| { - msg!("Failed to parse OpenbookV2 asks"); - ErrorCode::FailedOpenbookV2CPI - })?; - let bid_price: Option = match bid.find_max() { - Some(bid) => { - let bid_price = calculate_price_from_serum_limit_price( - bid, - market.quote_lot_size as u64, - market.base_decimals as u32, - market.base_lot_size as u64, - )?; - Some(bid_price) - } - None => None, - }; - let ask_price: Option = match ask.find_min() { - Some(ask) => { - let ask_price = calculate_price_from_serum_limit_price( - ask, - market.quote_lot_size as u64, - market.base_decimals as u32, - market.base_lot_size as u64, - )?; - Some(ask_price) - } - None => None, - }; - Ok((bid_price, ask_price)) - } - - fn get_order_action_explanation(&self) -> DriftResult { - Ok(OrderActionExplanation::OrderFilledWithOpenbookV2) - } - - fn validate_vault_amounts( - &self, - base_market: &Ref, - quote_market: &Ref, - ) -> DriftResult { - validate_spot_market_vault_amount(base_market, self.base_market_vault.amount)?; - validate_spot_market_vault_amount(quote_market, self.quote_market_vault.amount)?; - Ok(()) - } - - fn validate_markets( - &self, - base_market: &SpotMarket, - quote_market: &SpotMarket, - ) -> DriftResult<()> { - validate!( - self.base_market_vault.mint == base_market.mint, - ErrorCode::DefaultError, - "base mints dont match" - )?; - - validate!( - self.quote_market_vault.mint == quote_market.mint, - ErrorCode::DefaultError, - "base mints dont match" - )?; - - Ok(()) - } -} - -#[cfg(test)] -mod openbook_v2_test { - use crate::math::serum::calculate_price_from_serum_limit_price; - - #[test] - fn test_calculate_price_from_serum_limit_price() { - // price +- 6.6.2024 170.0 - let openbook_v2_price = 170_000; - // values from https://solscan.io/account/CFSMrBssNG8Ud1edW59jNLnq2cwrQ9uY5cM3wXmqRJj3#anchorData - let price = calculate_price_from_serum_limit_price( - openbook_v2_price, - 1, // quote_lot_size - 9, // base decimals - 1000000, // base_lot_size - ) - .unwrap(); - assert_eq!(170_000_000, price); - } -} diff --git a/programs/drift/src/state/fulfillment_params/serum.rs b/programs/drift/src/state/fulfillment_params/serum.rs deleted file mode 100644 index fa30f65eac..0000000000 --- a/programs/drift/src/state/fulfillment_params/serum.rs +++ /dev/null @@ -1,715 +0,0 @@ -use crate::controller::position::PositionDirection; -use crate::error::{DriftResult, ErrorCode}; -use crate::instructions::SpotFulfillmentType; -use crate::math::safe_math::SafeMath; -use crate::math::safe_unwrap::SafeUnwrap; -use crate::math::serum::{ - calculate_price_from_serum_limit_price, calculate_serum_limit_price, - calculate_serum_max_coin_qty, calculate_serum_max_native_pc_quantity, -}; -use crate::math::spot_withdraw::validate_spot_market_vault_amount; -use crate::msg; -use crate::signer::get_signer_seeds; -use crate::state::events::OrderActionExplanation; -use crate::state::spot_fulfillment_params::{ExternalSpotFill, SpotFulfillmentParams}; -use crate::state::spot_market::{SpotBalanceType, SpotFulfillmentConfigStatus, SpotMarket}; -use crate::state::state::State; -use crate::state::traits::Size; -use crate::{load, validate}; -use anchor_lang::accounts::account_loader::AccountLoader; -use anchor_lang::prelude::*; -use anchor_lang::{Key, ToAccountInfo}; -use anchor_spl::token::{Token, TokenAccount}; -use arrayref::array_ref; -use bytemuck::{cast_slice, from_bytes}; -use serum_dex::critbit::SlabView; -use serum_dex::instruction::{NewOrderInstructionV3, SelfTradeBehavior}; -use serum_dex::matching::{OrderBookState, Side}; -use serum_dex::state::Market; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::Instruction; -use std::cell::Ref; -use std::convert::TryFrom; -use std::num::NonZeroU64; -use std::ops::{Deref, DerefMut}; - -#[account(zero_copy(unsafe))] -#[derive(Default, PartialEq, Eq, Debug)] -#[repr(C)] -pub struct SerumV3FulfillmentConfig { - pub pubkey: Pubkey, - pub serum_program_id: Pubkey, - pub serum_market: Pubkey, - pub serum_request_queue: Pubkey, - pub serum_event_queue: Pubkey, - pub serum_bids: Pubkey, - pub serum_asks: Pubkey, - pub serum_base_vault: Pubkey, - pub serum_quote_vault: Pubkey, - pub serum_open_orders: Pubkey, - pub serum_signer_nonce: u64, - pub market_index: u16, - pub fulfillment_type: SpotFulfillmentType, - pub status: SpotFulfillmentConfigStatus, - pub padding: [u8; 4], -} - -impl Size for SerumV3FulfillmentConfig { - const SIZE: usize = 344; -} - -pub struct SerumContext<'a, 'b> { - pub serum_program: &'a AccountInfo<'b>, - pub serum_market: &'a AccountInfo<'b>, - pub serum_open_orders: &'a AccountInfo<'b>, -} - -impl<'a, 'b> SerumContext<'a, 'b> { - pub fn load_serum_market(&self) -> DriftResult { - Market::load(self.serum_market, self.serum_program.key, false).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidSerumMarket - }) - } - - pub fn load_open_orders(&self) -> DriftResult> { - validate!( - self.serum_open_orders.data_len() >= 12, - ErrorCode::InvalidSerumOpenOrders - )?; - let unpadded_data: Ref<[u8]> = Ref::map( - self.serum_open_orders - .try_borrow_data() - .map_err(|_e| ErrorCode::InvalidSerumOpenOrders)?, - |data| { - let data_len = data.len() - 12; - let (_, rest) = data.split_at(5); - let (mid, _) = rest.split_at(data_len); - mid - }, - ); - Ok(Ref::map(unpadded_data, from_bytes)) - } - - pub fn invoke_init_open_orders( - &self, - authority: &'a AccountInfo<'b>, - rent: &Sysvar<'b, Rent>, - nonce: u8, - ) -> DriftResult { - let signer_seeds = get_signer_seeds(&nonce); - let signers_seeds = &[&signer_seeds[..]]; - - let data = serum_dex::instruction::MarketInstruction::InitOpenOrders.pack(); - let instruction = Instruction { - program_id: *self.serum_program.key, - data, - accounts: vec![ - AccountMeta::new(*self.serum_open_orders.key, false), - AccountMeta::new_readonly(*authority.key, true), - AccountMeta::new_readonly(*self.serum_market.key, false), - AccountMeta::new_readonly(*rent.to_account_info().key, false), - ], - }; - - let account_infos = [ - self.serum_program.clone(), - self.serum_open_orders.clone(), - authority.clone(), - self.serum_market.clone(), - rent.to_account_info(), - ]; - solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds).map_err( - |e| { - msg!("{:?}", e); - ErrorCode::FailedSerumCPI - }, - ) - } - - pub fn to_serum_v3_fulfillment_config( - &self, - serum_fulfillment_config_key: &Pubkey, - market_index: u16, - ) -> DriftResult { - let market_state = self.load_serum_market()?; - let market_state_event_queue = market_state.event_q; - let serum_event_queue = - Pubkey::try_from_slice(cast_slice::(&market_state_event_queue)) - .map_err(|_| ErrorCode::InvalidSerumMarket)?; - - let market_state_request_queue = market_state.req_q; - let serum_request_queue = - Pubkey::try_from_slice(cast_slice::(&market_state_request_queue)) - .map_err(|_| ErrorCode::InvalidSerumMarket)?; - - let market_state_bids = market_state.bids; - let serum_bids = Pubkey::try_from_slice(cast_slice::(&market_state_bids)) - .map_err(|_| ErrorCode::InvalidSerumMarket)?; - - let market_state_asks = market_state.asks; - let serum_asks = Pubkey::try_from_slice(cast_slice::(&market_state_asks)) - .map_err(|_| ErrorCode::InvalidSerumMarket)?; - - let market_state_coin_vault = market_state.coin_vault; - let serum_base_vault = - Pubkey::try_from_slice(cast_slice::(&market_state_coin_vault)) - .map_err(|_| ErrorCode::InvalidSerumMarket)?; - - let market_state_pc_vault = market_state.pc_vault; - let serum_quote_vault = - Pubkey::try_from_slice(cast_slice::(&market_state_pc_vault)) - .map_err(|_| ErrorCode::InvalidSerumMarket)?; - let serum_signer_nonce = market_state.vault_signer_nonce; - - Ok(SerumV3FulfillmentConfig { - pubkey: *serum_fulfillment_config_key, - serum_program_id: *self.serum_program.key, - serum_market: *self.serum_market.key, - serum_request_queue, - serum_event_queue, - serum_bids, - serum_asks, - serum_base_vault, - serum_quote_vault, - serum_open_orders: *self.serum_open_orders.key, - serum_signer_nonce, - market_index, - fulfillment_type: SpotFulfillmentType::SerumV3, - status: SpotFulfillmentConfigStatus::Enabled, - padding: [0; 4], - }) - } -} - -pub struct SerumFulfillmentParams<'a, 'b> { - pub drift_signer: &'a AccountInfo<'b>, - pub serum_context: SerumContext<'a, 'b>, - pub serum_request_queue: &'a AccountInfo<'b>, - pub serum_event_queue: &'a AccountInfo<'b>, - pub serum_bids: &'a AccountInfo<'b>, - pub serum_asks: &'a AccountInfo<'b>, - pub serum_base_vault: &'a AccountInfo<'b>, - pub serum_quote_vault: &'a AccountInfo<'b>, - pub token_program: Program<'b, Token>, - pub base_market_vault: Box>, - pub quote_market_vault: Box>, - pub srm_vault: &'a AccountInfo<'b>, - pub serum_signer: &'a AccountInfo<'b>, - pub signer_nonce: u8, - pub base_mint_decimals: u32, - pub now: i64, -} - -impl<'a, 'b> Deref for SerumFulfillmentParams<'a, 'b> { - type Target = SerumContext<'a, 'b>; - - fn deref(&self) -> &Self::Target { - &self.serum_context - } -} - -/// Constructor for SerumFulfillmentParams -impl<'a, 'b> SerumFulfillmentParams<'a, 'b> { - #[allow(clippy::type_complexity)] - pub fn new<'c: 'b>( - account_info_iter: &'a mut std::iter::Peekable>>, - state: &State, - base_market: &SpotMarket, - quote_market: &SpotMarket, - now: i64, - ) -> DriftResult { - let account_info_vec = account_info_iter.collect::>(); - let account_infos = array_ref![account_info_vec, 0, 16]; - let [serum_fulfillment_config, serum_program, serum_market, serum_request_queue, serum_event_queue, serum_bids, serum_asks, serum_base_vault, serum_quote_vault, serum_open_orders, serum_signer, drift_signer, token_program, base_market_vault, quote_market_vault, srm_vault] = - account_infos; - - let serum_fulfillment_config_loader: AccountLoader = - AccountLoader::try_from(serum_fulfillment_config).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?; - let serum_fulfillment_config = load!(serum_fulfillment_config_loader)?; - - validate!( - serum_fulfillment_config.status == SpotFulfillmentConfigStatus::Enabled, - ErrorCode::SpotFulfillmentConfigDisabled - )?; - - validate!( - &state.signer == drift_signer.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - serum_fulfillment_config.market_index == base_market.market_index, - ErrorCode::InvalidFulfillmentConfig, - "config market index {} does not equal base asset index {}", - serum_fulfillment_config.market_index, - base_market.market_index - )?; - - validate!( - &base_market.vault == base_market_vault.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - "e_market.vault == quote_market_vault.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - &serum_fulfillment_config.serum_program_id == serum_program.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - &serum_fulfillment_config.serum_market == serum_market.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - validate!( - &serum_fulfillment_config.serum_open_orders == serum_open_orders.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - let base_market_vault: Box> = - Box::new(Account::try_from(base_market_vault).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?); - let quote_market_vault: Box> = - Box::new(Account::try_from(quote_market_vault).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?); - - let token_program: Program = Program::try_from(*token_program).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidFulfillmentConfig - })?; - - validate!( - &state.srm_vault == srm_vault.key, - ErrorCode::InvalidFulfillmentConfig - )?; - - Ok(SerumFulfillmentParams { - drift_signer, - serum_context: SerumContext { - serum_program, - serum_market, - serum_open_orders, - }, - serum_request_queue, - serum_event_queue, - serum_bids, - serum_asks, - serum_base_vault, - serum_quote_vault, - token_program, - base_market_vault, - quote_market_vault, - serum_signer, - srm_vault, - signer_nonce: state.signer_nonce, - base_mint_decimals: base_market.decimals, - now, - }) - } -} - -/// CPI Wrappers into Serum/Openbook -impl<'a, 'b> SerumFulfillmentParams<'a, 'b> { - pub fn invoke_new_order( - &self, - taker_direction: PositionDirection, - order: NewOrderInstructionV3, - ) -> DriftResult { - let drift_vault = match taker_direction { - PositionDirection::Long => self.quote_market_vault.to_account_info(), - PositionDirection::Short => self.base_market_vault.to_account_info(), - }; - - let data = serum_dex::instruction::MarketInstruction::NewOrderV3(order).pack(); - let mut instruction = Instruction { - program_id: *self.serum_program.key, - data, - accounts: vec![ - AccountMeta::new(*self.serum_market.key, false), - AccountMeta::new(*self.serum_open_orders.key, false), - AccountMeta::new(*self.serum_request_queue.key, false), - AccountMeta::new(*self.serum_event_queue.key, false), - AccountMeta::new(*self.serum_bids.key, false), - AccountMeta::new(*self.serum_asks.key, false), - AccountMeta::new(*drift_vault.key, false), - AccountMeta::new_readonly(*self.drift_signer.key, true), - AccountMeta::new(*self.serum_base_vault.key, false), - AccountMeta::new(*self.serum_quote_vault.key, false), - AccountMeta::new_readonly(*self.token_program.key, false), - AccountMeta::new_readonly(*self.drift_signer.key, false), - ], - }; - - if self.srm_vault.key != &Pubkey::default() { - instruction - .accounts - .push(AccountMeta::new_readonly(*self.srm_vault.key, false)); - - let account_infos = [ - self.serum_program.clone(), // Have to add account of the program id - self.serum_market.clone(), - self.serum_open_orders.clone(), - self.serum_request_queue.clone(), - self.serum_event_queue.clone(), - self.serum_bids.clone(), - self.serum_asks.clone(), - drift_vault.clone(), - self.drift_signer.clone(), - self.serum_base_vault.clone(), - self.serum_quote_vault.clone(), - self.token_program.to_account_info(), - self.srm_vault.clone(), - ]; - - let signer_seeds = get_signer_seeds(&self.signer_nonce); - let signers_seeds = &[&signer_seeds[..]]; - - solana_program::program::invoke_signed_unchecked( - &instruction, - &account_infos, - signers_seeds, - ) - .map_err(|e| { - msg!("{:?}", e); - ErrorCode::FailedSerumCPI - }) - } else { - let account_infos = [ - self.serum_program.clone(), // Have to add account of the program id - self.serum_market.clone(), - self.serum_open_orders.clone(), - self.serum_request_queue.clone(), - self.serum_event_queue.clone(), - self.serum_bids.clone(), - self.serum_asks.clone(), - drift_vault.clone(), - self.drift_signer.clone(), - self.serum_base_vault.clone(), - self.serum_quote_vault.clone(), - self.token_program.to_account_info(), - ]; - - let signer_seeds = get_signer_seeds(&self.signer_nonce); - let signers_seeds = &[&signer_seeds[..]]; - - solana_program::program::invoke_signed_unchecked( - &instruction, - &account_infos, - signers_seeds, - ) - .map_err(|e| { - msg!("{:?}", e); - ErrorCode::FailedSerumCPI - }) - } - } - - pub fn invoke_settle_funds(&self) -> DriftResult { - let data = serum_dex::instruction::MarketInstruction::SettleFunds.pack(); - let instruction = Instruction { - program_id: *self.serum_program.key, - data, - accounts: vec![ - AccountMeta::new(*self.serum_market.key, false), - AccountMeta::new(*self.serum_open_orders.key, false), - AccountMeta::new_readonly(*self.drift_signer.key, true), - AccountMeta::new(*self.serum_base_vault.key, false), - AccountMeta::new(*self.serum_quote_vault.key, false), - AccountMeta::new(self.base_market_vault.key(), false), - AccountMeta::new(self.quote_market_vault.key(), false), - AccountMeta::new_readonly(*self.serum_signer.key, false), - AccountMeta::new_readonly(*self.token_program.key, false), - AccountMeta::new(self.quote_market_vault.key(), false), - ], - }; - - let account_infos = [ - self.serum_program.clone(), - self.serum_market.clone(), - self.serum_open_orders.clone(), - self.drift_signer.clone(), - self.serum_base_vault.clone(), - self.serum_quote_vault.clone(), - self.base_market_vault.to_account_info(), - self.quote_market_vault.to_account_info(), - self.serum_signer.clone(), - self.token_program.to_account_info(), - ]; - - let signer_seeds = get_signer_seeds(&self.signer_nonce); - let signers_seeds = &[&signer_seeds[..]]; - - solana_program::program::invoke_signed_unchecked( - &instruction, - &account_infos, - signers_seeds, - ) - .map_err(|e| { - msg!("{:?}", e); - ErrorCode::FailedSerumCPI - }) - } -} - -impl<'a, 'b> SpotFulfillmentParams for SerumFulfillmentParams<'a, 'b> { - fn is_external(&self) -> bool { - true - } - - fn get_best_bid_and_ask(&self) -> DriftResult<(Option, Option)> { - let mut market = self.load_serum_market()?; - - let mut bids = market.load_bids_mut(self.serum_bids).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidSerumBids - })?; - - let mut asks = market.load_asks_mut(self.serum_asks).map_err(|e| { - msg!("{:?}", e); - ErrorCode::InvalidSerumAsks - })?; - - let order_book_state = OrderBookState { - bids: bids.deref_mut(), - asks: asks.deref_mut(), - market_state: market.deref_mut(), - }; - - let best_bid = match order_book_state.bids.find_max() { - Some(best_bid_h) => { - let best_bid_ref = order_book_state - .bids - .get(best_bid_h) - .safe_unwrap()? - .as_leaf() - .safe_unwrap()?; - - let price = calculate_price_from_serum_limit_price( - best_bid_ref.price().get(), - order_book_state.market_state.pc_lot_size, - self.base_mint_decimals, - order_book_state.market_state.coin_lot_size, - )?; - - Some(price) - } - None => None, - }; - - let best_ask = match order_book_state.asks.find_min() { - Some(best_ask_h) => { - let best_ask_ref = order_book_state - .asks - .get(best_ask_h) - .safe_unwrap()? - .as_leaf() - .safe_unwrap()?; - - let price = calculate_price_from_serum_limit_price( - best_ask_ref.price().get(), - order_book_state.market_state.pc_lot_size, - self.base_mint_decimals, - order_book_state.market_state.coin_lot_size, - )?; - - Some(price) - } - None => None, - }; - - Ok((best_bid, best_ask)) - } - - fn fulfill_order( - &mut self, - taker_direction: PositionDirection, - taker_price: u64, - taker_base_asset_amount: u64, - taker_max_quote_asset_amount: u64, - ) -> DriftResult { - let market_state_before = self.load_serum_market()?; - - let serum_order_side = match taker_direction { - PositionDirection::Long => Side::Bid, - PositionDirection::Short => Side::Ask, - }; - - let serum_max_coin_qty = calculate_serum_max_coin_qty( - taker_base_asset_amount, - market_state_before.coin_lot_size, - )?; - - let serum_limit_price = calculate_serum_limit_price( - taker_price, - market_state_before.pc_lot_size, - self.base_mint_decimals, - market_state_before.coin_lot_size, - taker_direction, - )?; - - let serum_max_native_pc_qty = calculate_serum_max_native_pc_quantity( - serum_limit_price, - serum_max_coin_qty, - market_state_before.pc_lot_size, - )? - .min(taker_max_quote_asset_amount); - - if serum_max_coin_qty == 0 || serum_max_native_pc_qty == 0 { - return Ok(ExternalSpotFill::empty()); - } - - let serum_order = NewOrderInstructionV3 { - side: serum_order_side, - limit_price: NonZeroU64::new(serum_limit_price).safe_unwrap()?, - max_coin_qty: NonZeroU64::new(serum_max_coin_qty).safe_unwrap()?, // max base to deposit into serum - max_native_pc_qty_including_fees: NonZeroU64::new(serum_max_native_pc_qty) - .safe_unwrap()?, // max quote to deposit into serum - self_trade_behavior: SelfTradeBehavior::AbortTransaction, - order_type: serum_dex::matching::OrderType::ImmediateOrCancel, - client_order_id: 0, - limit: 10, - max_ts: self.now, - }; - - let _market_fees_accrued_before = market_state_before.pc_fees_accrued; - let base_before = self.base_market_vault.amount; - let quote_before = self.quote_market_vault.amount; - let market_rebates_accrued_before = market_state_before.referrer_rebates_accrued; - - drop(market_state_before); - - self.invoke_new_order(taker_direction, serum_order)?; - - let market_state_after = self.load_serum_market()?; - - let _market_fees_accrued_after = market_state_after.pc_fees_accrued; - let market_rebates_accrued_after = market_state_after.referrer_rebates_accrued; - - drop(market_state_after); - - let open_orders_before = self.load_open_orders()?; - let unsettled_referrer_rebate_before = open_orders_before.referrer_rebates_accrued; - - drop(open_orders_before); - - self.invoke_settle_funds()?; - - self.base_market_vault.reload().map_err(|_e| { - msg!("Failed to reload base_market_vault"); - ErrorCode::FailedSerumCPI - })?; - self.quote_market_vault.reload().map_err(|_e| { - msg!("Failed to reload quote_market_vault"); - ErrorCode::FailedSerumCPI - })?; - - let base_after = self.base_market_vault.amount; - let quote_after = self.quote_market_vault.amount; - - let open_orders_after = self.load_open_orders()?; - let unsettled_referrer_rebate_after = open_orders_after.referrer_rebates_accrued; - - drop(open_orders_after); - - let settled_referred_rebate = - unsettled_referrer_rebate_before.safe_sub(unsettled_referrer_rebate_after)?; - - let (base_update_direction, base_asset_amount_filled) = if base_after > base_before { - (SpotBalanceType::Deposit, base_after.safe_sub(base_before)?) - } else { - (SpotBalanceType::Borrow, base_before.safe_sub(base_after)?) - }; - - if base_asset_amount_filled == 0 { - msg!("No base filled on serum"); - return Ok(ExternalSpotFill::empty()); - } - - let serum_referrer_rebate = - market_rebates_accrued_after.safe_sub(market_rebates_accrued_before)?; - - // rebate is half of taker fee - let serum_fee = serum_referrer_rebate; - - let (quote_update_direction, quote_asset_amount_filled) = - if base_update_direction == SpotBalanceType::Borrow { - let quote_asset_amount_delta = quote_after - .safe_sub(quote_before)? - .safe_sub(settled_referred_rebate)?; - - ( - SpotBalanceType::Deposit, - quote_asset_amount_delta - .safe_add(serum_fee)? - .safe_add(serum_referrer_rebate)?, - ) - } else { - let quote_asset_amount_delta = quote_before - .safe_add(settled_referred_rebate)? - .safe_sub(quote_after)?; - - ( - SpotBalanceType::Borrow, - quote_asset_amount_delta - .safe_sub(serum_fee)? - .safe_sub(serum_referrer_rebate)?, - ) - }; - - Ok(ExternalSpotFill { - base_asset_amount_filled, - quote_asset_amount_filled, - base_update_direction, - quote_update_direction, - fee: serum_fee, - unsettled_referrer_rebate: serum_referrer_rebate, - settled_referrer_rebate: settled_referred_rebate, - }) - } - - fn get_order_action_explanation(&self) -> DriftResult { - Ok(OrderActionExplanation::OrderFillWithSerum) - } - - fn validate_vault_amounts( - &self, - base_market: &Ref, - quote_market: &Ref, - ) -> DriftResult { - validate_spot_market_vault_amount(base_market, self.base_market_vault.amount)?; - validate_spot_market_vault_amount(quote_market, self.quote_market_vault.amount)?; - Ok(()) - } - - fn validate_markets( - &self, - base_market: &SpotMarket, - quote_market: &SpotMarket, - ) -> DriftResult<()> { - validate!( - self.base_market_vault.mint == base_market.mint, - ErrorCode::DefaultError, - "base mints dont match" - )?; - - validate!( - self.quote_market_vault.mint == quote_market.mint, - ErrorCode::DefaultError, - "base mints dont match" - )?; - - Ok(()) - } -} diff --git a/programs/drift/src/state/traits/tests.rs b/programs/drift/src/state/traits/tests.rs index 260546181b..b5333ed437 100644 --- a/programs/drift/src/state/traits/tests.rs +++ b/programs/drift/src/state/traits/tests.rs @@ -1,6 +1,5 @@ mod size { use crate::state::events::OrderActionRecord; - use crate::state::fulfillment_params::serum::SerumV3FulfillmentConfig; use crate::state::insurance_fund_stake::InsuranceFundStake; use crate::state::perp_market::PerpMarket; use crate::state::spot_market::SpotMarket; @@ -29,13 +28,6 @@ mod size { assert_eq!(actual_size, expected_size); } - #[test] - fn serum_config() { - let expected_size = std::mem::size_of::() + 8; - let actual_size = SerumV3FulfillmentConfig::SIZE; - assert_eq!(actual_size, expected_size); - } - #[test] fn state() { let expected_size = std::mem::size_of::() + 8; diff --git a/programs/openbook_v2/Cargo.toml b/programs/openbook_v2/Cargo.toml deleted file mode 100644 index 7a7360a4f3..0000000000 --- a/programs/openbook_v2/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "openbook-v2-light" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -crate-type = ["cdylib", "lib"] -name = "openbook_v2_light" -path = "src/lib.rs" - -[features] -default = ["cpi"] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] - - -[dependencies] -anchor-lang = { version = "0.29.0", features = ["derive"] } -borsh = {version = "0.10.3", features = ["const-generics", "default"]} -bytemuck = { version = "1.4.0" , features = ["derive", "min_const_generics"]} \ No newline at end of file diff --git a/programs/openbook_v2/README.md b/programs/openbook_v2/README.md deleted file mode 100644 index cf36b57b3a..0000000000 --- a/programs/openbook_v2/README.md +++ /dev/null @@ -1,10 +0,0 @@ - -Openbook V2 integration to add ability to fulfill via openbook v2 liquidity - -to run integration test - integration.rs: -``` -anchor build -cargo test-sbf --package openbook-v2-light --test integration -``` - -#### Please note integration test will not run on Apple M x chips - will throw error during deserializing of SpotMarket data \ No newline at end of file diff --git a/programs/openbook_v2/src/account.rs b/programs/openbook_v2/src/account.rs deleted file mode 100644 index edf6550f9b..0000000000 --- a/programs/openbook_v2/src/account.rs +++ /dev/null @@ -1,418 +0,0 @@ -#![allow(dead_code)] - -use crate::*; -use bytemuck::Zeroable; - -#[zero_copy] -#[derive(AnchorDeserialize, AnchorSerialize, Debug)] -pub struct OracleConfig { - pub conf_filter: f64, - pub max_staleness_slots: i64, - pub reserved: [u8; 72], -} - -#[derive( - Debug, - Copy, - Clone, - // bytemuck::Pod, - // bytemuck::Zeroable, - AnchorSerialize, - AnchorDeserialize, -)] -pub struct OracleConfigParams { - pub conf_filter: f32, - pub max_staleness_slots: Option, -} - -#[account(zero_copy)] -pub struct EventHeap { - pub header: EventHeapHeader, - pub nodes: [EventNode; 600], - pub reserved: [u8; 64], -} - -#[zero_copy] -pub struct EventHeapHeader { - free_head: u16, - used_head: u16, - count: u16, - _padd: u16, - pub seq_num: u64, -} - -#[zero_copy] -#[derive(Debug)] -pub struct EventNode { - next: u16, - prev: u16, - _pad: [u8; 4], - pub event: AnyEvent, -} - -#[zero_copy] -#[derive(Debug)] -pub struct AnyEvent { - pub event_type: u8, - pub padding: [u8; 143], -} - -#[account(zero_copy(unsafe))] -#[repr(packed)] -pub struct Market { - /// PDA bump - pub bump: u8, - - /// Number of decimals used for the base token. - /// - /// Used to convert the oracle's price into a native/native price. - pub base_decimals: u8, - pub quote_decimals: u8, - - pub padding1: [u8; 5], - - // Pda for signing vault txs - pub market_authority: Pubkey, - - /// No expiry = 0. Market will expire and no trading allowed after time_expiry - pub time_expiry: i64, - - /// Admin who can collect fees from the market - pub collect_fee_admin: Pubkey, - /// Admin who must sign off on all order creations - pub open_orders_admin: NonZeroPubkeyOption, - /// Admin who must sign off on all event consumptions - pub consume_events_admin: NonZeroPubkeyOption, - /// Admin who can set market expired, prune orders and close the market - pub close_market_admin: NonZeroPubkeyOption, - - /// Name. Trailing zero bytes are ignored. - pub name: [u8; 16], - - /// Address of the BookSide account for bids - pub bids: Pubkey, - /// Address of the BookSide account for asks - pub asks: Pubkey, - /// Address of the EventHeap account - pub event_heap: Pubkey, - - /// Oracles account address - pub oracle_a: NonZeroPubkeyOption, - pub oracle_b: NonZeroPubkeyOption, - /// Oracle configuration - pub oracle_config: OracleConfig, - - /// Number of quote native in a quote lot. Must be a power of 10. - /// - /// Primarily useful for increasing the tick size on the market: A lot price - /// of 1 becomes a native price of quote_lot_size/base_lot_size becomes a - /// ui price of quote_lot_size*base_decimals/base_lot_size/quote_decimals. - pub quote_lot_size: i64, - - /// Number of base native in a base lot. Must be a power of 10. - /// - /// Example: If base decimals for the underlying asset is 6, base lot size - /// is 100 and and base position lots is 10_000 then base position native is - /// 1_000_000 and base position ui is 1. - pub base_lot_size: i64, - - /// Total number of orders seen - pub seq_num: u64, - - /// Timestamp in seconds that the market was registered at. - pub registration_time: i64, - - /// Fees - /// - /// Fee (in 10^-6) when matching maker orders. - /// maker_fee < 0 it means some of the taker_fees goes to the maker - /// maker_fee > 0, it means no taker_fee to the maker, and maker fee goes to the referral - pub maker_fee: i64, - /// Fee (in 10^-6) for taker orders, always >= 0. - pub taker_fee: i64, - - /// Total fees accrued in native quote - pub fees_accrued: u128, - /// Total fees settled in native quote - pub fees_to_referrers: u128, - - /// Referrer rebates to be distributed - pub referrer_rebates_accrued: u64, - - /// Fees generated and available to withdraw via sweep_fees - pub fees_available: u64, - - /// Cumulative maker volume (same as taker volume) in quote native units - pub maker_volume: u128, - - /// Cumulative taker volume in quote native units due to place take orders - pub taker_volume_wo_oo: u128, - - pub base_mint: Pubkey, - pub quote_mint: Pubkey, - - pub market_base_vault: Pubkey, - pub base_deposit_total: u64, - - pub market_quote_vault: Pubkey, - pub quote_deposit_total: u64, - - pub reserved: [u8; 128], -} - -pub const MAX_ORDERTREE_NODES: usize = 1024; - -#[account(zero_copy)] -pub struct BookSide { - pub roots: [OrderTreeRoot; 2], - pub reserved_roots: [OrderTreeRoot; 4], - pub reserved: [u8; 256], - pub nodes: OrderTreeNodes, -} - -impl BookSide { - #[allow(clippy::unwrap_used)] - pub fn find_min(&self) -> Option { - let mut p = 0_u64; - for node in self.nodes.nodes.iter() { - if node.tag == LEAF_NODE_TAG { - let price = (u128::try_from_slice(&node.data[7..23]).unwrap() >> 64) as u64; - if price < p || p == 0 { - p = price; - } - } - } - if p > 0 { - return Some(p); - } - None - } - - #[allow(clippy::unwrap_used)] - pub fn find_max(&self) -> Option { - let mut p = 0_u64; - for node in self.nodes.nodes.iter() { - if node.tag == LEAF_NODE_TAG { - let price = (u128::try_from_slice(&node.data[7..23]).unwrap() >> 64) as u64; - if price > p { - p = price; - } - } - } - if p > 0 { - return Some(p); - } - None - } -} - -#[zero_copy] -#[derive(AnchorSerialize)] -pub struct AnyNode { - pub tag: u8, - pub data: [u8; 79], - // essential to make AnyNode alignment the same as other node types - pub force_align: u64, -} - -#[zero_copy] -pub struct OrderTreeNodes { - pub order_tree_type: u8, // OrderTreeType, but that's not POD - pub padding: [u8; 3], - pub bump_index: u32, - pub free_list_len: u32, - pub free_list_head: NodeHandle, - pub reserved: [u8; 512], - pub nodes: [AnyNode; MAX_ORDERTREE_NODES], -} - -pub type NodeHandle = u32; - -#[zero_copy] -#[derive(Debug)] -pub struct OrderTreeRoot { - pub maybe_node: NodeHandle, - pub leaf_count: u32, -} - -/// LeafNodes represent an order in the binary tree -#[derive( - Debug, - Copy, - Clone, - PartialEq, - Eq, - // bytemuck::Pod, - // bytemuck::Zeroable, - AnchorSerialize, - AnchorDeserialize, -)] -// #[repr(C)] -pub struct LeafNode { - /// NodeTag - pub tag: u8, - - /// Index into the owning OpenOrdersAccount's OpenOrders - pub owner_slot: u8, - - /// Time in seconds after `timestamp` at which the order expires. - /// A value of 0 means no expiry. - pub time_in_force: u16, - - pub padding: [u8; 4], - - /// The binary tree key, see new_node_key() - pub key: u128, - - /// Address of the owning OpenOrdersAccount - pub owner: Pubkey, - - /// Number of base lots to buy or sell, always >=1 - pub quantity: i64, - - /// The time the order was placed - pub timestamp: u64, - - /// If the effective price of an oracle pegged order exceeds this limit, - /// it will be considered invalid and may be removed. - /// - /// Only applicable in the oracle_pegged OrderTree - pub peg_limit: i64, - - /// User defined id for this order, used in FillEvents - pub client_order_id: u64, -} - -impl LeafNode { - #[allow(clippy::too_many_arguments)] - pub fn new( - owner_slot: u8, - key: u128, - owner: Pubkey, - quantity: i64, - timestamp: u64, - time_in_force: u16, - peg_limit: i64, - client_order_id: u64, - ) -> Self { - Self { - tag: 2, - owner_slot, - time_in_force, - padding: Default::default(), - key, - owner, - quantity, - timestamp, - peg_limit, - client_order_id, - } - } - - /// The order's price_data as stored in the key - /// - /// Needs to be unpacked differently for fixed and oracle pegged orders. - #[inline(always)] - pub fn price_data(&self) -> u64 { - (self.key >> 64) as u64 - } - - /// Time at which this order will expire, u64::MAX if never - #[inline(always)] - pub fn expiry(&self) -> u64 { - if self.time_in_force == 0 { - u64::MAX - } else { - self.timestamp + self.time_in_force as u64 - } - } - - /// Returns if the order is expired at `now_ts` - #[inline(always)] - pub fn is_expired(&self, now_ts: u64) -> bool { - self.time_in_force > 0 && now_ts >= self.timestamp + self.time_in_force as u64 - } -} - -#[zero_copy] -#[derive(AnchorSerialize, AnchorDeserialize, Debug, Default, PartialEq)] -pub struct NonZeroPubkeyOption { - key: Pubkey, -} - -pub trait NonZeroKey { - fn non_zero_key(&self) -> NonZeroPubkeyOption; -} - -impl NonZeroKey for Option -where - T: Key, -{ - fn non_zero_key(&self) -> NonZeroPubkeyOption { - self.as_ref().map(|this| this.key()).into() - } -} - -impl PartialEq for Pubkey { - fn eq(&self, other: &NonZeroPubkeyOption) -> bool { - other.is_some() && *self == other.key - } -} - -impl PartialEq for NonZeroPubkeyOption { - fn eq(&self, other: &Pubkey) -> bool { - self.is_some() && self.key == *other - } -} - -// impl From for Option { -// fn from(NonZeroPubkeyOption) -> Self { -// if pubkey_option.is_some() { -// Some(pubkey_option.key) -// } else { -// None -// } -// } -// } - -impl From> for NonZeroPubkeyOption { - fn from(normal_option: Option) -> Self { - match normal_option { - Some(key) => Self { key }, - None => Self::zeroed(), - } - } -} - -impl NonZeroPubkeyOption { - pub fn is_some(&self) -> bool { - *self != Self::zeroed() - } - - pub fn is_none(&self) -> bool { - *self == Self::zeroed() - } -} - -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub enum Side { - Bid, - Ask, -} - -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub enum SelfTradeBehavior { - DecrementTake, - CancelProvide, - AbortTransaction, -} - -#[derive(BorshSerialize, BorshDeserialize, Debug)] -pub enum PlaceOrderType { - Limit, - ImmediateOrCancel, - PostOnly, - Market, - PostOnlySlide, - FillOrKill, -} diff --git a/programs/openbook_v2/src/constants.rs b/programs/openbook_v2/src/constants.rs deleted file mode 100644 index 79997d4dc7..0000000000 --- a/programs/openbook_v2/src/constants.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub const LEAF_NODE_TAG: u8 = 2; -pub const OPEN_ORDERS_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 194, 78, 123, 16, 105, 208, 165]; diff --git a/programs/openbook_v2/src/context.rs b/programs/openbook_v2/src/context.rs deleted file mode 100644 index f14d5c34c3..0000000000 --- a/programs/openbook_v2/src/context.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::*; - -#[derive(Accounts)] -pub struct PlaceTakeOrder<'info> { - pub dummy_authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CreateMarket<'info> { - pub dummy_authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CreateOpenOrdersIndexer<'info> { - pub dummy_authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CreateOpenOrdersAccount<'info> { - pub dummy_authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct PlaceOrder<'info> { - pub dummy_authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct ConsumeEvents<'info> { - pub dummy_authority: Signer<'info>, -} diff --git a/programs/openbook_v2/src/lib.rs b/programs/openbook_v2/src/lib.rs deleted file mode 100644 index d6ea487c61..0000000000 --- a/programs/openbook_v2/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -#![allow(clippy::too_many_arguments)] - -use anchor_lang::prelude::{ - borsh::{BorshDeserialize, BorshSerialize}, - *, -}; - -declare_id!("opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb"); - -pub mod account; -// pub mod order; -pub mod constants; -pub mod context; - -pub use crate::account::*; -pub use crate::constants::*; -pub use crate::context::*; - -#[program] -mod openbook_v2 { - #![allow(dead_code)] - #![allow(unused_variables)] - - use super::*; - - pub(crate) fn place_take_order( - ctx: Context, - side: Side, - price_lots: i64, - max_base_lots: i64, - max_quote_lots_including_fees: i64, - order_type: PlaceOrderType, - limit: u8, - ) -> Result<()> { - Ok(()) - } - - pub(crate) fn create_market( - ctx: Context, - name: String, - oracle_config: OracleConfigParams, - quote_lot_size: i64, - base_lot_size: i64, - maker_fee: i64, - taker_fee: i64, - time_expiry: i64, - ) -> Result<()> { - Ok(()) - } - - pub(crate) fn create_open_orders_indexer(ctx: Context) -> Result<()> { - Ok(()) - } - - pub(crate) fn create_open_orders_account( - ctx: Context, - name: String, - ) -> Result<()> { - Ok(()) - } - - pub(crate) fn place_order( - ctx: Context, - side: Side, - price_lots: i64, - max_base_lots: i64, - max_quote_lots_including_fees: i64, - client_order_id: u64, - order_type: PlaceOrderType, - expiry_timestamp: u64, - self_trade_behavior: SelfTradeBehavior, - limit: u8, - ) -> Result<()> { - Ok(()) - } - - pub(crate) fn consume_events(ctx: Context, limit: u64) -> Result<()> { - Ok(()) - } -} diff --git a/sdk/package.json b/sdk/package.json index bd399899b1..8b27d48530 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -47,8 +47,6 @@ "@ellipsis-labs/phoenix-sdk": "1.4.5", "@grpc/grpc-js": "1.14.0", "@msgpack/msgpack": "^3.1.2", - "@openbook-dex/openbook-v2": "0.2.10", - "@project-serum/serum": "0.13.65", "@pythnetwork/client": "2.5.3", "@pythnetwork/price-service-sdk": "1.7.1", "@pythnetwork/pyth-solana-receiver": "0.7.0", @@ -155,4 +153,4 @@ "@grpc/grpc-js": false, "zstddec": false } -} +} \ No newline at end of file diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index e6533d5a41..2098178622 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -329,61 +329,6 @@ export class AdminClient extends DriftClient { ); } - public async initializeSerumFulfillmentConfig( - marketIndex: number, - serumMarket: PublicKey, - serumProgram: PublicKey - ): Promise { - const initializeIx = await this.getInitializeSerumFulfillmentConfigIx( - marketIndex, - serumMarket, - serumProgram - ); - - const tx = await this.buildTransaction(initializeIx); - - const { txSig } = await this.sendTransaction(tx, [], this.opts); - - return txSig; - } - - public async getInitializeSerumFulfillmentConfigIx( - marketIndex: number, - serumMarket: PublicKey, - serumProgram: PublicKey - ): Promise { - const serumOpenOrders = getSerumOpenOrdersPublicKey( - this.program.programId, - serumMarket - ); - - const serumFulfillmentConfig = getSerumFulfillmentConfigPublicKey( - this.program.programId, - serumMarket - ); - - return await this.program.instruction.initializeSerumFulfillmentConfig( - marketIndex, - { - accounts: { - admin: this.isSubscribed - ? this.getStateAccount().admin - : this.wallet.publicKey, - state: await this.getStatePublicKey(), - baseSpotMarket: this.getSpotMarketAccount(marketIndex).pubkey, - quoteSpotMarket: this.getQuoteSpotMarketAccount().pubkey, - driftSigner: this.getSignerPublicKey(), - serumProgram, - serumMarket, - serumOpenOrders, - rent: SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - serumFulfillmentConfig, - }, - } - ); - } - public async initializePhoenixFulfillmentConfig( marketIndex: number, phoenixMarket: PublicKey @@ -430,52 +375,6 @@ export class AdminClient extends DriftClient { ); } - public async initializeOpenbookV2FulfillmentConfig( - marketIndex: number, - openbookMarket: PublicKey - ): Promise { - const initializeIx = await this.getInitializeOpenbookV2FulfillmentConfigIx( - marketIndex, - openbookMarket - ); - - const tx = await this.buildTransaction(initializeIx); - - const { txSig } = await this.sendTransaction(tx, [], this.opts); - - return txSig; - } - - public async getInitializeOpenbookV2FulfillmentConfigIx( - marketIndex: number, - openbookMarket: PublicKey - ): Promise { - const openbookFulfillmentConfig = getOpenbookV2FulfillmentConfigPublicKey( - this.program.programId, - openbookMarket - ); - - return this.program.instruction.initializeOpenbookV2FulfillmentConfig( - marketIndex, - { - accounts: { - baseSpotMarket: this.getSpotMarketAccount(marketIndex).pubkey, - quoteSpotMarket: this.getQuoteSpotMarketAccount().pubkey, - state: await this.getStatePublicKey(), - openbookV2Program: OPENBOOK_PROGRAM_ID, - openbookV2Market: openbookMarket, - driftSigner: this.getSignerPublicKey(), - openbookV2FulfillmentConfig: openbookFulfillmentConfig, - admin: this.isSubscribed - ? this.getStateAccount().admin - : this.wallet.publicKey, - rent: SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - }, - } - ); - } - public async initializePerpMarket( marketIndex: number, priceOracle: PublicKey, diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 6779c3e2fe..d4d083af2c 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -34,7 +34,6 @@ import { MarketType, ModifyOrderParams, ModifyOrderPolicy, - OpenbookV2FulfillmentConfigAccount, OptionalOrderParams, OracleSource, Order, @@ -48,7 +47,6 @@ import { PositionDirection, ReferrerInfo, ReferrerNameAccount, - SerumV3FulfillmentConfigAccount, SettlePnlMode, SignedTxData, SpotBalanceType, @@ -686,27 +684,6 @@ export class DriftClient { ); } - public async getSerumV3FulfillmentConfig( - serumMarket: PublicKey - ): Promise { - const address = await getSerumFulfillmentConfigPublicKey( - this.program.programId, - serumMarket - ); - return (await this.program.account.serumV3FulfillmentConfig.fetch( - address - )) as SerumV3FulfillmentConfigAccount; - } - - public async getSerumV3FulfillmentConfigs(): Promise< - SerumV3FulfillmentConfigAccount[] - > { - const accounts = await this.program.account.serumV3FulfillmentConfig.all(); - return accounts.map( - (account) => account.account - ) as SerumV3FulfillmentConfigAccount[]; - } - public async getPhoenixV1FulfillmentConfig( phoenixMarket: PublicKey ): Promise { @@ -729,28 +706,6 @@ export class DriftClient { ) as PhoenixV1FulfillmentConfigAccount[]; } - public async getOpenbookV2FulfillmentConfig( - openbookMarket: PublicKey - ): Promise { - const address = getOpenbookV2FulfillmentConfigPublicKey( - this.program.programId, - openbookMarket - ); - return (await this.program.account.openbookV2FulfillmentConfig.fetch( - address - )) as OpenbookV2FulfillmentConfigAccount; - } - - public async getOpenbookV2FulfillmentConfigs(): Promise< - OpenbookV2FulfillmentConfigAccount[] - > { - const accounts = - await this.program.account.openbookV2FulfillmentConfig.all(); - return accounts.map( - (account) => account.account - ) as OpenbookV2FulfillmentConfigAccount[]; - } - /** @deprecated use fetchAllLookupTableAccounts() */ public async fetchMarketLookupTableAccount(): Promise { if (this.lookupTableAccount) return this.lookupTableAccount; @@ -5593,10 +5548,7 @@ export class DriftClient { userAccountPublicKey: PublicKey, user: UserAccount, order?: Pick, - fulfillmentConfig?: - | SerumV3FulfillmentConfigAccount - | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams @@ -5623,10 +5575,7 @@ export class DriftClient { userAccountPublicKey: PublicKey, userAccount: UserAccount, order?: Pick, - fulfillmentConfig?: - | SerumV3FulfillmentConfigAccount - | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, fillerPublicKey?: PublicKey @@ -5702,30 +5651,15 @@ export class DriftClient { addSpotFulfillmentAccounts( marketIndex: number, remainingAccounts: AccountMeta[], - fulfillmentConfig?: - | SerumV3FulfillmentConfigAccount - | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount ): void { if (fulfillmentConfig) { - if ('serumProgramId' in fulfillmentConfig) { - this.addSerumRemainingAccounts( - marketIndex, - remainingAccounts, - fulfillmentConfig - ); - } else if ('phoenixProgramId' in fulfillmentConfig) { + if ('phoenixProgramId' in fulfillmentConfig) { this.addPhoenixRemainingAccounts( marketIndex, remainingAccounts, fulfillmentConfig ); - } else if ('openbookV2ProgramId' in fulfillmentConfig) { - this.addOpenbookRemainingAccounts( - marketIndex, - remainingAccounts, - fulfillmentConfig - ); } else { throw Error('Invalid fulfillment config type'); } @@ -5743,97 +5677,6 @@ export class DriftClient { } } - addSerumRemainingAccounts( - marketIndex: number, - remainingAccounts: AccountMeta[], - fulfillmentConfig: SerumV3FulfillmentConfigAccount - ): void { - remainingAccounts.push({ - pubkey: fulfillmentConfig.pubkey, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumProgramId, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumMarket, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumRequestQueue, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumEventQueue, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumBids, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumAsks, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumBaseVault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumQuoteVault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.serumOpenOrders, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: getSerumSignerPublicKey( - fulfillmentConfig.serumProgramId, - fulfillmentConfig.serumMarket, - fulfillmentConfig.serumSignerNonce - ), - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getSignerPublicKey(), - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: TOKEN_PROGRAM_ID, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getSpotMarketAccount(marketIndex).vault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getQuoteSpotMarketAccount().vault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getStateAccount().srmVault, - isWritable: false, - isSigner: false, - }); - } - addPhoenixRemainingAccounts( marketIndex: number, remainingAccounts: AccountMeta[], @@ -5891,103 +5734,6 @@ export class DriftClient { }); } - addOpenbookRemainingAccounts( - marketIndex: number, - remainingAccounts: AccountMeta[], - fulfillmentConfig: OpenbookV2FulfillmentConfigAccount - ): void { - remainingAccounts.push({ - pubkey: fulfillmentConfig.pubkey, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getSignerPublicKey(), - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2ProgramId, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2Market, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2MarketAuthority, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2EventHeap, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2Bids, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2Asks, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2BaseVault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: fulfillmentConfig.openbookV2QuoteVault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getSpotMarketAccount(marketIndex).vault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getQuoteSpotMarketAccount().vault, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: TOKEN_PROGRAM_ID, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: SystemProgram.programId, - isWritable: false, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getSpotMarketAccount(marketIndex).pubkey, - isWritable: true, - isSigner: false, - }); - remainingAccounts.push({ - pubkey: this.getQuoteSpotMarketAccount().pubkey, - isWritable: true, - isSigner: false, - }); - - if (fulfillmentConfig.remainingAccounts) { - for (const remainingAccount of fulfillmentConfig.remainingAccounts) { - remainingAccounts.push({ - pubkey: remainingAccount, - isWritable: true, - isSigner: false, - }); - } - } - } - /** * Swap tokens in drift account using titan or jupiter * @param swapClient swap client to find routes and instructions (Titan or Jupiter) @@ -7866,7 +7612,7 @@ export class DriftClient { public async preparePlaceAndTakeSpotOrder( orderParams: OptionalOrderParams, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams, @@ -7890,7 +7636,7 @@ export class DriftClient { public async placeAndTakeSpotOrder( orderParams: OptionalOrderParams, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, txParams?: TxParams, @@ -7918,7 +7664,7 @@ export class DriftClient { public async getPlaceAndTakeSpotOrderIx( orderParams: OptionalOrderParams, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo, subAccountId?: number @@ -7993,7 +7739,7 @@ export class DriftClient { public async placeAndMakeSpotOrder( orderParams: OptionalOrderParams, takerInfo: TakerInfo, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, referrerInfo?: ReferrerInfo, txParams?: TxParams, subAccountId?: number @@ -8020,7 +7766,7 @@ export class DriftClient { public async getPlaceAndMakeSpotOrderIx( orderParams: OptionalOrderParams, takerInfo: TakerInfo, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + fulfillmentConfig?: PhoenixV1FulfillmentConfigAccount, referrerInfo?: ReferrerInfo, subAccountId?: number ): Promise { diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 6c73a0de63..eb867e9cf2 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -85,13 +85,9 @@ export * from './types'; export * from './math/utils'; export * from './math/fuel'; export * from './config'; -export * from './serum/serumSubscriber'; -export * from './serum/serumFulfillmentConfigMap'; export * from './phoenix/phoenixSubscriber'; export * from './priorityFee'; export * from './phoenix/phoenixFulfillmentConfigMap'; -export * from './openbook/openbookV2Subscriber'; -export * from './openbook/openbookV2FulfillmentConfigMap'; export * from './oracles/pythClient'; export * from './oracles/pythPullClient'; export * from './oracles/pythLazerClient'; diff --git a/sdk/src/math/trade.ts b/sdk/src/math/trade.ts index 2b0dec0f58..7c8fb75c6e 100644 --- a/sdk/src/math/trade.ts +++ b/sdk/src/math/trade.ts @@ -34,7 +34,6 @@ import { isVariant } from '../types'; import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { DLOB } from '../dlob/DLOB'; import { PublicKey } from '@solana/web3.js'; -import { Orderbook } from '@project-serum/serum'; import { L2OrderBook } from '../dlob/orderBookLevels'; const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1] @@ -697,215 +696,6 @@ export function calculateEstimatedPerpEntryPrice( }; } -/** - * Calculates the estimated entry price and price impact of order, in base or quote - * Price impact is based on the difference between the entry price and the best bid/ask price (whether it's dlob or serum) - * - * @param assetType - * @param amount - * @param direction - * @param market - * @param oraclePriceData - * @param dlob - * @param serumBids - * @param serumAsks - * @param slot - * @param usersToSkip - */ -export function calculateEstimatedSpotEntryPrice( - assetType: AssetType, - amount: BN, - direction: PositionDirection, - market: SpotMarketAccount, - oraclePriceData: OraclePriceData, - dlob: DLOB, - serumBids: Orderbook, - serumAsks: Orderbook, - slot: number, - usersToSkip = new Map() -): { - entryPrice: BN; - priceImpact: BN; - bestPrice: BN; - worstPrice: BN; - baseFilled: BN; - quoteFilled: BN; -} { - if (amount.eq(ZERO)) { - return { - entryPrice: ZERO, - priceImpact: ZERO, - bestPrice: ZERO, - worstPrice: ZERO, - baseFilled: ZERO, - quoteFilled: ZERO, - }; - } - - const basePrecision = new BN(Math.pow(10, market.decimals)); - - const takerIsLong = isVariant(direction, 'long'); - const dlobLimitOrders = dlob[ - takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids' - ](market.marketIndex, slot, MarketType.SPOT, oraclePriceData); - const serumLimitOrders = takerIsLong - ? serumAsks.getL2(100) - : serumBids.getL2(100); - - let cumulativeBaseFilled = ZERO; - let cumulativeQuoteFilled = ZERO; - - let dlobLimitOrder = dlobLimitOrders.next().value; - let serumLimitOrder = serumLimitOrders.shift(); - - const dlobLimitOrderPrice = dlobLimitOrder?.getPrice(oraclePriceData, slot); - const serumLimitOrderPrice = serumLimitOrder - ? new BN(serumLimitOrder[0] * PRICE_PRECISION.toNumber()) - : undefined; - - const bestPrice = takerIsLong - ? BN.min(serumLimitOrderPrice || BN_MAX, dlobLimitOrderPrice || BN_MAX) - : BN.max(serumLimitOrderPrice || ZERO, dlobLimitOrderPrice || ZERO); - let worstPrice = bestPrice; - - if (assetType === 'base') { - while ( - !cumulativeBaseFilled.eq(amount) && - (dlobLimitOrder || serumLimitOrder) - ) { - const dlobLimitOrderPrice = dlobLimitOrder?.getPrice( - oraclePriceData, - slot - ); - const serumLimitOrderPrice = serumLimitOrder - ? new BN(serumLimitOrder[0] * PRICE_PRECISION.toNumber()) - : undefined; - - const useSerum = takerIsLong - ? (serumLimitOrderPrice || BN_MAX).lt(dlobLimitOrderPrice || BN_MAX) - : (serumLimitOrderPrice || ZERO).gt(dlobLimitOrderPrice || ZERO); - - if (!useSerum) { - if (dlobLimitOrder && usersToSkip.has(dlobLimitOrder.userAccount)) { - continue; - } - - const baseFilled = BN.min( - dlobLimitOrder.order.baseAssetAmount.sub( - dlobLimitOrder.order.baseAssetAmountFilled - ), - amount.sub(cumulativeBaseFilled) - ); - const quoteFilled = baseFilled - .mul(dlobLimitOrderPrice) - .div(basePrecision); - - cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); - cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); - - worstPrice = dlobLimitOrderPrice; - - dlobLimitOrder = dlobLimitOrders.next().value; - } else { - const baseFilled = BN.min( - new BN(serumLimitOrder[1] * basePrecision.toNumber()), - amount.sub(cumulativeBaseFilled) - ); - const quoteFilled = baseFilled - .mul(serumLimitOrderPrice) - .div(basePrecision); - - cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); - cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); - - worstPrice = serumLimitOrderPrice; - - serumLimitOrder = serumLimitOrders.shift(); - } - } - } else { - while ( - !cumulativeQuoteFilled.eq(amount) && - (dlobLimitOrder || serumLimitOrder) - ) { - const dlobLimitOrderPrice = dlobLimitOrder?.getPrice( - oraclePriceData, - slot - ); - const serumLimitOrderPrice = serumLimitOrder - ? new BN(serumLimitOrder[0] * PRICE_PRECISION.toNumber()) - : undefined; - - const useSerum = takerIsLong - ? (serumLimitOrderPrice || BN_MAX).lt(dlobLimitOrderPrice || BN_MAX) - : (serumLimitOrderPrice || ZERO).gt(dlobLimitOrderPrice || ZERO); - - if (!useSerum) { - if (dlobLimitOrder && usersToSkip.has(dlobLimitOrder.userAccount)) { - continue; - } - - const quoteFilled = BN.min( - dlobLimitOrder.order.baseAssetAmount - .sub(dlobLimitOrder.order.baseAssetAmountFilled) - .mul(dlobLimitOrderPrice) - .div(basePrecision), - amount.sub(cumulativeQuoteFilled) - ); - - const baseFilled = quoteFilled - .mul(basePrecision) - .div(dlobLimitOrderPrice); - - cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); - cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); - - worstPrice = dlobLimitOrderPrice; - - dlobLimitOrder = dlobLimitOrders.next().value; - } else { - const serumOrderBaseAmount = new BN( - serumLimitOrder[1] * basePrecision.toNumber() - ); - const quoteFilled = BN.min( - serumOrderBaseAmount.mul(serumLimitOrderPrice).div(basePrecision), - amount.sub(cumulativeQuoteFilled) - ); - - const baseFilled = quoteFilled - .mul(basePrecision) - .div(serumLimitOrderPrice); - - cumulativeBaseFilled = cumulativeBaseFilled.add(baseFilled); - cumulativeQuoteFilled = cumulativeQuoteFilled.add(quoteFilled); - - worstPrice = serumLimitOrderPrice; - - serumLimitOrder = serumLimitOrders.shift(); - } - } - } - - const entryPrice = - cumulativeBaseFilled && cumulativeBaseFilled.gt(ZERO) - ? cumulativeQuoteFilled.mul(basePrecision).div(cumulativeBaseFilled) - : ZERO; - - const priceImpact = - bestPrice && bestPrice.gt(ZERO) - ? entryPrice.sub(bestPrice).mul(PRICE_PRECISION).div(bestPrice).abs() - : ZERO; - - return { - entryPrice, - priceImpact, - bestPrice, - worstPrice, - baseFilled: cumulativeBaseFilled, - quoteFilled: cumulativeQuoteFilled, - }; -} - export function calculateEstimatedEntryPriceWithL2( assetType: AssetType, amount: BN, diff --git a/sdk/src/openbook/openbookV2FulfillmentConfigMap.ts b/sdk/src/openbook/openbookV2FulfillmentConfigMap.ts deleted file mode 100644 index 9c1b756477..0000000000 --- a/sdk/src/openbook/openbookV2FulfillmentConfigMap.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { PublicKey } from '@solana/web3.js'; -import { OpenbookV2FulfillmentConfigAccount } from '../types'; -import { DriftClient } from '../driftClient'; - -export class OpenbookV2FulfillmentConfigMap { - driftClient: DriftClient; - map = new Map(); - - public constructor(driftClient: DriftClient) { - this.driftClient = driftClient; - } - - public async add( - marketIndex: number, - openbookV2MarketAddress: PublicKey - ): Promise { - const account = await this.driftClient.getOpenbookV2FulfillmentConfig( - openbookV2MarketAddress - ); - - this.map.set(marketIndex, account); - } - - public get( - marketIndex: number - ): OpenbookV2FulfillmentConfigAccount | undefined { - return this.map.get(marketIndex); - } -} diff --git a/sdk/src/openbook/openbookV2Subscriber.ts b/sdk/src/openbook/openbookV2Subscriber.ts deleted file mode 100644 index 39348252ca..0000000000 --- a/sdk/src/openbook/openbookV2Subscriber.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; -import { PRICE_PRECISION } from '../constants/numericConstants'; -import { AnchorProvider, BN, Idl, Program, Wallet } from '@coral-xyz/anchor'; -import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels'; -import { Market, OpenBookV2Client } from '@openbook-dex/openbook-v2'; -import openbookV2Idl from '../idl/openbook.json'; - -export type OpenbookV2SubscriberConfig = { - connection: Connection; - programId: PublicKey; - marketAddress: PublicKey; - accountSubscription: - | { - // enables use to add web sockets in the future - type: 'polling'; - accountLoader: BulkAccountLoader; - } - | { - type: 'websocket'; - }; -}; - -export class OpenbookV2Subscriber implements L2OrderBookGenerator { - connection: Connection; - programId: PublicKey; - marketAddress: PublicKey; - subscriptionType: 'polling' | 'websocket'; - accountLoader: BulkAccountLoader | undefined; - subscribed: boolean; - market: Market; - marketCallbackId: string | number; - client: OpenBookV2Client; - - public constructor(config: OpenbookV2SubscriberConfig) { - this.connection = config.connection; - this.programId = config.programId; - this.marketAddress = config.marketAddress; - this.subscribed = false; - if (config.accountSubscription.type === 'polling') { - this.subscriptionType = 'polling'; - this.accountLoader = config.accountSubscription.accountLoader; - } else { - this.subscriptionType = 'websocket'; - } - } - - public async subscribe(): Promise { - if (this.subscribed === true) { - return; - } - - const anchorProvider = new AnchorProvider( - this.connection, - new Wallet(Keypair.generate()), - {} - ); - const openbookV2Program = new Program( - openbookV2Idl as Idl, - this.programId, - anchorProvider - ); - this.client = new OpenBookV2Client(anchorProvider); - const market = await Market.load(this.client, this.marketAddress); - this.market = await market.loadOrderBook(); - - if (this.subscriptionType === 'websocket') { - this.marketCallbackId = this.connection.onAccountChange( - this.marketAddress, - async (accountInfo, _) => { - const marketRaw = openbookV2Program.coder.accounts.decode( - 'Market', - accountInfo.data - ); - const market = new Market(this.client, this.marketAddress, marketRaw); - await market.loadOrderBook(); - this.market = market; - } - ); - } else { - this.marketCallbackId = await this.accountLoader.addAccount( - this.marketAddress, - async (buffer, _) => { - const marketRaw = openbookV2Program.coder.accounts.decode( - 'Market', - buffer - ); - const market = new Market(this.client, this.marketAddress, marketRaw); - await market.loadOrderBook(); - this.market = market; - } - ); - } - - this.subscribed = true; - } - - public getBestBid(): BN | undefined { - const bestBid = this.market.bids?.best(); - - if (bestBid === undefined) { - return undefined; - } - - return this.convertPriceInLotsToPricePrecision(bestBid.priceLots); - } - - public getBestAsk(): BN | undefined { - const bestAsk = this.market.asks?.best(); - - if (bestAsk === undefined) { - return undefined; - } - - return this.convertPriceInLotsToPricePrecision(bestAsk.priceLots); - } - - public getL2Bids(): Generator { - return this.getL2Levels('bids'); - } - - public getL2Asks(): Generator { - return this.getL2Levels('asks'); - } - - public convertSizeInBaseLotsToMarketPrecision(sizeInLots: BN): BN { - return sizeInLots.mul(this.market.account.baseLotSize); - } - - public convertPriceInLotsToPricePrecision(priceInLots: BN): BN { - const adjPrice = priceInLots - .mul(PRICE_PRECISION) - .muln( - 10 ** - (this.market.account.baseDecimals - this.market.account.quoteDecimals) - ) - .mul(this.market.account.quoteLotSize) - .div(this.market.account.baseLotSize); - return adjPrice; - } - - *getL2Levels(side: 'bids' | 'asks'): Generator { - const levels = side === 'bids' ? this.market.bids : this.market.asks; - for (const order of levels?.items() ?? []) { - const size = this.convertSizeInBaseLotsToMarketPrecision(order.sizeLots); - const price = this.convertPriceInLotsToPricePrecision(order.priceLots); - yield { - price, - size, - sources: { - openbook: size, - }, - }; - } - } - - public async unsubscribe(): Promise { - if (!this.subscribed) { - return; - } - - if (this.subscriptionType === 'websocket') { - await this.connection.removeAccountChangeListener( - this.marketCallbackId as number - ); - } else { - this.accountLoader.removeAccount( - this.marketAddress, - this.marketCallbackId as string - ); - } - - this.subscribed = false; - } -} diff --git a/sdk/src/serum/serumFulfillmentConfigMap.ts b/sdk/src/serum/serumFulfillmentConfigMap.ts deleted file mode 100644 index 4d57f40a25..0000000000 --- a/sdk/src/serum/serumFulfillmentConfigMap.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { PublicKey } from '@solana/web3.js'; -import { SerumV3FulfillmentConfigAccount } from '../types'; -import { DriftClient } from '../driftClient'; - -export class SerumFulfillmentConfigMap { - driftClient: DriftClient; - map = new Map(); - - public constructor(driftClient: DriftClient) { - this.driftClient = driftClient; - } - - public async add( - marketIndex: number, - serumMarketAddress: PublicKey - ): Promise { - const account = await this.driftClient.getSerumV3FulfillmentConfig( - serumMarketAddress - ); - this.map.set(marketIndex, account); - } - - public get(marketIndex: number): SerumV3FulfillmentConfigAccount { - return this.map.get(marketIndex); - } -} diff --git a/sdk/src/serum/serumSubscriber.ts b/sdk/src/serum/serumSubscriber.ts deleted file mode 100644 index 9806afea9b..0000000000 --- a/sdk/src/serum/serumSubscriber.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { Connection, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; -import { Market, Orderbook } from '@project-serum/serum'; -import { SerumMarketSubscriberConfig } from './types'; -import { BN } from '@coral-xyz/anchor'; -import { PRICE_PRECISION } from '../constants/numericConstants'; -import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels'; - -export class SerumSubscriber implements L2OrderBookGenerator { - connection: Connection; - programId: PublicKey; - marketAddress: PublicKey; - subscriptionType: 'polling' | 'websocket'; - accountLoader: BulkAccountLoader | undefined; - market: Market; - - subscribed: boolean; - - asksAddress: PublicKey; - asks: Orderbook; - asksCallbackId: string | number; - lastAsksSlot: number; - - bidsAddress: PublicKey; - bids: Orderbook; - bidsCallbackId: string | number; - lastBidsSlot: number; - - public constructor(config: SerumMarketSubscriberConfig) { - this.connection = config.connection; - this.programId = config.programId; - this.marketAddress = config.marketAddress; - if (config.accountSubscription.type === 'polling') { - this.subscriptionType = 'polling'; - this.accountLoader = config.accountSubscription.accountLoader; - } else { - this.subscriptionType = 'websocket'; - } - } - - public async subscribe(): Promise { - if (this.subscribed) { - return; - } - - this.market = await Market.load( - this.connection, - this.marketAddress, - undefined, - this.programId - ); - - this.asksAddress = this.market.asksAddress; - this.asks = await this.market.loadAsks(this.connection); - - if (this.subscriptionType === 'websocket') { - this.asksCallbackId = this.connection.onAccountChange( - this.asksAddress, - (accountInfo, ctx) => { - this.lastAsksSlot = ctx.slot; - this.asks = Orderbook.decode(this.market, accountInfo.data); - } - ); - } else { - this.asksCallbackId = await this.accountLoader.addAccount( - this.asksAddress, - (buffer, slot) => { - this.lastAsksSlot = slot; - this.asks = Orderbook.decode(this.market, buffer); - } - ); - } - - this.bidsAddress = this.market.bidsAddress; - this.bids = await this.market.loadBids(this.connection); - - if (this.subscriptionType === 'websocket') { - this.bidsCallbackId = this.connection.onAccountChange( - this.bidsAddress, - (accountInfo, ctx) => { - this.lastBidsSlot = ctx.slot; - this.bids = Orderbook.decode(this.market, accountInfo.data); - } - ); - } else { - this.bidsCallbackId = await this.accountLoader.addAccount( - this.bidsAddress, - (buffer, slot) => { - this.lastBidsSlot = slot; - this.bids = Orderbook.decode(this.market, buffer); - } - ); - } - - this.subscribed = true; - } - - public getBestBid(): BN | undefined { - const bestBid = this.bids.getL2(1)[0]; - if (!bestBid) { - return undefined; - } - - return new BN(bestBid[0] * PRICE_PRECISION.toNumber()); - } - - public getBestAsk(): BN | undefined { - const bestAsk = this.asks.getL2(1)[0]; - if (!bestAsk) { - return undefined; - } - - return new BN(bestAsk[0] * PRICE_PRECISION.toNumber()); - } - - public getL2Bids(): Generator { - return this.getL2Levels('bids'); - } - - public getL2Asks(): Generator { - return this.getL2Levels('asks'); - } - - *getL2Levels(side: 'bids' | 'asks'): Generator { - // @ts-ignore - const basePrecision = Math.pow(10, this.market._baseSplTokenDecimals); - const pricePrecision = PRICE_PRECISION.toNumber(); - for (const { price: priceNum, size: sizeNum } of this[side].items( - side === 'bids' - )) { - const price = new BN(priceNum * pricePrecision); - const size = new BN(sizeNum * basePrecision); - yield { - price, - size, - sources: { - serum: size, - }, - }; - } - } - - public async unsubscribe(): Promise { - if (!this.subscribed) { - return; - } - - // remove listeners - if (this.subscriptionType === 'websocket') { - await this.connection.removeAccountChangeListener( - this.asksCallbackId as number - ); - await this.connection.removeAccountChangeListener( - this.bidsCallbackId as number - ); - } else { - this.accountLoader.removeAccount( - this.asksAddress, - this.asksCallbackId as string - ); - this.accountLoader.removeAccount( - this.bidsAddress, - this.bidsCallbackId as string - ); - } - - this.subscribed = false; - } -} diff --git a/sdk/src/serum/types.ts b/sdk/src/serum/types.ts deleted file mode 100644 index d42b37b0c6..0000000000 --- a/sdk/src/serum/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Connection, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; - -export type SerumMarketSubscriberConfig = { - connection: Connection; - programId: PublicKey; - marketAddress: PublicKey; - accountSubscription: - | { - // enables use to add web sockets in the future - type: 'polling'; - accountLoader: BulkAccountLoader; - } - | { - type: 'websocket'; - }; -}; diff --git a/sdk/src/types.ts b/sdk/src/types.ts index ea111ce0c5..4353ec49e3 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1527,23 +1527,6 @@ export type InsuranceFundStake = { lastWithdrawRequestTs: BN; }; -export type SerumV3FulfillmentConfigAccount = { - fulfillmentType: SpotFulfillmentType; - status: SpotFulfillmentStatus; - pubkey: PublicKey; - marketIndex: number; - serumProgramId: PublicKey; - serumMarket: PublicKey; - serumRequestQueue: PublicKey; - serumEventQueue: PublicKey; - serumBids: PublicKey; - serumAsks: PublicKey; - serumBaseVault: PublicKey; - serumQuoteVault: PublicKey; - serumOpenOrders: PublicKey; - serumSignerNonce: BN; -}; - export type PhoenixV1FulfillmentConfigAccount = { pubkey: PublicKey; phoenixProgramId: PublicKey; @@ -1556,23 +1539,6 @@ export type PhoenixV1FulfillmentConfigAccount = { status: SpotFulfillmentStatus; }; -export type OpenbookV2FulfillmentConfigAccount = { - pubkey: PublicKey; - openbookV2ProgramId: PublicKey; - openbookV2Market: PublicKey; - openbookV2MarketAuthority: PublicKey; - openbookV2EventHeap: PublicKey; - openbookV2Bids: PublicKey; - openbookV2Asks: PublicKey; - openbookV2BaseVault: PublicKey; - openbookV2QuoteVault: PublicKey; - marketIndex: number; - fulfillmentType: SpotFulfillmentType; - status: SpotFulfillmentStatus; - // not actually on the account, just used to pass around remaining accounts in ts - remainingAccounts?: PublicKey[]; -}; - export type ReferrerNameAccount = { name: number[]; user: PublicKey; diff --git a/sdk/yarn.lock b/sdk/yarn.lock index 26a6f824c0..1dfbe782c4 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -28,7 +28,7 @@ dependencies: "@babel/types" "^7.28.0" -"@babel/runtime@^7.10.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.25.0": +"@babel/runtime@^7.17.2", "@babel/runtime@^7.25.0": version "7.27.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.27.6.tgz#ec4070a04d76bae8ddbb10770ba55714a417b7c6" integrity sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q== @@ -501,55 +501,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@openbook-dex/openbook-v2@0.2.10": - version "0.2.10" - resolved "https://registry.yarnpkg.com/@openbook-dex/openbook-v2/-/openbook-v2-0.2.10.tgz#a5cfcd30ce827ecd446b76429a5e41baa23a318c" - integrity sha512-JOroVQHeia+RbghpluDJB5psUIhdhYRPLu0zWoG0h5vgDU4SjXwlcC+LJiIa2HVPasvZjWuCtlKWFyrOS75lOA== - dependencies: - "@coral-xyz/anchor" "^0.29.0" - "@solana/spl-token" "^0.4.0" - "@solana/web3.js" "^1.77.3" - big.js "^6.2.1" - -"@project-serum/anchor@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" - integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== - dependencies: - "@project-serum/borsh" "^0.2.2" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.0" - camelcase "^5.3.1" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/borsh@^0.2.2": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" - integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@project-serum/serum@0.13.65": - version "0.13.65" - resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0" - integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA== - dependencies: - "@project-serum/anchor" "^0.11.1" - "@solana/spl-token" "^0.1.6" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.2" - buffer-layout "^1.2.0" - "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -1111,7 +1062,7 @@ dependencies: "@solana/codecs" "2.0.0-rc.1" -"@solana/spl-token@0.4.13", "@solana/spl-token@^0.4.0": +"@solana/spl-token@0.4.13": version "0.4.13" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.4.13.tgz#8f65c3c2b315e1a00a91b8d0f60922c6eb71de62" integrity sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w== @@ -1122,18 +1073,6 @@ "@solana/spl-token-metadata" "^0.1.6" buffer "^6.0.3" -"@solana/spl-token@^0.1.6": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" - integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== - dependencies: - "@babel/runtime" "^7.10.5" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.0" - buffer "6.0.3" - buffer-layout "^1.2.0" - dotenv "10.0.0" - "@solana/spl-token@^0.3.7": version "0.3.11" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.11.tgz#cdc10f9472b29b39c8983c92592cadd06627fb9a" @@ -1210,7 +1149,7 @@ "@solana/rpc-types" "2.3.0" "@solana/transaction-messages" "2.3.0" -"@solana/web3.js@1.98.0", "@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0", "@solana/web3.js@^1.30.2", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.77.3", "@solana/web3.js@^1.90.0", "@solana/web3.js@^1.98.0", "@solana/web3.js@^1.98.2", "@solana/web3.js@~1.77.3": +"@solana/web3.js@1.98.0", "@solana/web3.js@^1.30.2", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.90.0", "@solana/web3.js@^1.98.0", "@solana/web3.js@^1.98.2", "@solana/web3.js@~1.77.3": version "1.98.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.98.0.tgz#21ecfe8198c10831df6f0cfde7f68370d0405917" integrity sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA== @@ -1837,12 +1776,12 @@ base-x@^5.0.0: resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.1.tgz#16bf35254be1df8aca15e36b7c1dda74b2aa6b03" integrity sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg== -base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -big.js@^6.2.1, big.js@^6.2.2: +big.js@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.2.tgz#be3bb9ac834558b53b099deef2a1d06ac6368e1a" integrity sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ== @@ -2071,11 +2010,6 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - camelcase@^6.0.0, camelcase@^6.2.1, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" @@ -2525,11 +2459,6 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dotenv@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - dotenv@^16.0.3: version "16.6.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" @@ -2933,13 +2862,6 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" - integrity sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw== - dependencies: - traverse-chain "~0.1.0" - flat-cache@^3.0.4: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" @@ -4781,11 +4703,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -traverse-chain@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" - integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg== - ts-api-utils@^1.0.1: version "1.4.3" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 7b2fceedf2..99196c9ec8 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -55,7 +55,6 @@ test_files=( modifyOrder.ts multipleMakerOrders.ts multipleSpotMakerOrders.ts - openbookTest.ts oracleDiffSources.ts oracleFillPriceGuardrails.ts oracleOffsetOrders.ts @@ -74,7 +73,6 @@ test_files=( pythLazerBankrun.ts referrer.ts roundInFavorBaseAsset.ts - serumTest.ts settlePNLInvariant.ts spotDepositWithdraw.ts spotDepositWithdraw22.ts diff --git a/tests/ifRebalance.ts b/tests/ifRebalance.ts index 94e2440c7b..1b1e09845f 100644 --- a/tests/ifRebalance.ts +++ b/tests/ifRebalance.ts @@ -244,8 +244,6 @@ describe('spot swap', () => { console.log('\n\n\n\n\n here \n\n\n\n\n'); - console.log('\n\n\n\n\n here \n\n\n\n\n'); - const market = await Market.load( bankrunContextWrapper.connection.toConnection(), serumMarketPublicKey, @@ -372,7 +370,7 @@ describe('spot swap', () => { market.programId, market.publicKey, // @ts-ignore - market._decoded.vaultSignerNonce, + market._decoded.vaultSignerNonce ), programId: market.programId, }); diff --git a/tests/liquidateSpotWithSwap.ts b/tests/liquidateSpotWithSwap.ts index f6966324ee..896ddc8b5b 100644 --- a/tests/liquidateSpotWithSwap.ts +++ b/tests/liquidateSpotWithSwap.ts @@ -230,14 +230,6 @@ describe('spot swap', () => { console.log('\n\n\n\n\n here \n\n\n\n\n'); - await makerDriftClient.initializeSerumFulfillmentConfig( - solSpotMarketIndex, - serumMarketPublicKey, - SERUM - ); - - console.log('\n\n\n\n\n here \n\n\n\n\n'); - const market = await Market.load( bankrunContextWrapper.connection.toConnection(), serumMarketPublicKey, @@ -332,23 +324,21 @@ describe('spot swap', () => { } ); - const serumConfig = await takerDriftClient.getSerumV3FulfillmentConfig( - market.publicKey - ); const settleFundsIx = DexInstructions.settleFunds({ market: market.publicKey, openOrders: takerOpenOrders, owner: takerDriftClient.wallet.publicKey, // @ts-ignore - baseVault: serumConfig.serumBaseVault, + baseVault: market._decoded.baseVault, // @ts-ignore - quoteVault: serumConfig.serumQuoteVault, + quoteVault: market._decoded.quoteVault, baseWallet: takerWSOL, quoteWallet: takerUSDC, vaultSigner: getSerumSignerPublicKey( market.programId, market.publicKey, - serumConfig.serumSignerNonce + // @ts-ignore + market._decoded.vaultSignerNonce ), programId: market.programId, }); diff --git a/tests/lpPoolSwap.ts b/tests/lpPoolSwap.ts index 866436c136..4996c96462 100644 --- a/tests/lpPoolSwap.ts +++ b/tests/lpPoolSwap.ts @@ -732,12 +732,6 @@ describe('LP Pool', () => { SERUM ); - await adminClient.initializeSerumFulfillmentConfig( - 2, - serumMarketPublicKey, - SERUM - ); - serumMarket = await Market.load( bankrunContextWrapper.connection.toConnection(), serumMarketPublicKey, @@ -875,24 +869,23 @@ describe('LP Pool', () => { } ); - const serumConfig = await adminClient.getSerumV3FulfillmentConfig( - serumMarket.publicKey - ); const settleFundsIx = DexInstructions.settleFunds({ market: serumMarket.publicKey, openOrders: openOrdersAccount, owner: adminClient.wallet.publicKey, // @ts-ignore - baseVault: serumConfig.serumBaseVault, + baseVault: serumMarket._decoded.baseVault, // @ts-ignore - quoteVault: serumConfig.serumQuoteVault, + quoteVault: serumMarket._decoded.quoteVault, baseWallet: adminSolAccount, quoteWallet: userUSDCAccount.publicKey, vaultSigner: getSerumSignerPublicKey( serumMarket.programId, serumMarket.publicKey, - serumConfig.serumSignerNonce + // @ts-ignore + serumMarket._decoded.vaultSignerNonce ), + // @ts-ignore programId: serumMarket.programId, }); diff --git a/tests/openbookTest.ts b/tests/openbookTest.ts deleted file mode 100644 index 3d57b25d99..0000000000 --- a/tests/openbookTest.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { assert } from 'chai'; -import * as anchor from '@coral-xyz/anchor'; - -import { Program, Idl, BN } from '@coral-xyz/anchor'; - -import { - OracleSource, - OrderType, - PositionDirection, - PublicKey, - TestClient, -} from '../sdk/src'; -import openbookIDL from '../sdk/src/idl/openbook.json'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; -import { - createOpenOrdersAccount, - createOpenOrdersAccountV2, - OPENBOOK, - OrderType as ObOrderType, - placeOrder, - SelfTradeBehavior, - Side, -} from './openbookHelpers'; -import { - createWSolTokenAccountForUser, - initializeQuoteSpotMarket, - initializeSolSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, -} from './testHelpers'; -import { createBidsAsksEventHeap, createMarket } from './openbookHelpers'; -import { Keypair } from '@solana/web3.js'; -import { LAMPORTS_PRECISION, PRICE_PRECISION } from '../sdk/src'; -import { WRAPPED_SOL_MINT } from '../sdk/src'; -import { ZERO } from '../sdk'; - -describe('openbook v2', () => { - const chProgram = anchor.workspace.Drift as Program; - const openbookProgram = new Program(openbookIDL as Idl, OPENBOOK); - - let driftClient: TestClient; - - let fillerDriftClient: TestClient; - const fillerKeypair = Keypair.generate(); - - let bulkAccountLoader: TestBulkAccountLoader; - - let bankrunContextWrapper: BankrunContextWrapper; - - const solSpotMarketIndex = 1; - - const bids = Keypair.generate(); - const asks = Keypair.generate(); - const eventHeap = Keypair.generate(); - const market = Keypair.generate(); - let usdcMint: Keypair; - - const usdcAmount = new anchor.BN(200 * 1_000 * 10 ** 6); - const solAmount = new anchor.BN(200 * 1_000 * 10 ** 9); - - let userUsdcAccount: Keypair; - let userWSolAccount: PublicKey; - - let _marketAuthority: PublicKey; - let marketBaseVault: PublicKey; - let marketQuoteVault: PublicKey; - - let openOrdersAccount: PublicKey; - let openOrdersIndexer: PublicKey; - const openOrdersAccounts: PublicKey[] = []; - - before(async () => { - const context = await startAnchor( - '', - [ - { - name: 'openbook', - programId: OPENBOOK, - }, - ], - [] - ); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - const solOracle = await mockOracleNoProgram(bankrunContextWrapper, 100); - usdcMint = await mockUSDCMint(bankrunContextWrapper); - - userUsdcAccount = await mockUserUSDCAccount( - usdcMint, - // @ts-ignore - usdcAmount.muln(2), - bankrunContextWrapper - ); - - userWSolAccount = await createWSolTokenAccountForUser( - bankrunContextWrapper, - // @ts-ignore - bankrunContextWrapper.provider.wallet, - solAmount - ); - - driftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: bankrunContextWrapper.provider.wallet, - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0], - spotMarketIndexes: [0, 1], - subAccountIds: [], - oracleInfos: [ - { - publicKey: solOracle, - source: OracleSource.PYTH, - }, - ], - userStats: true, - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - await createBidsAsksEventHeap(bankrunContextWrapper, bids, asks, eventHeap); - - const quoteSizeLot = new BN(1); - const baseSizeLot = new BN(100000); - [_marketAuthority, marketBaseVault, marketQuoteVault] = await createMarket( - bankrunContextWrapper, - openbookProgram, - market, - WRAPPED_SOL_MINT, - usdcMint.publicKey, - bids.publicKey, - asks.publicKey, - eventHeap.publicKey, - quoteSizeLot, - baseSizeLot - ); - - [openOrdersIndexer, openOrdersAccount] = await createOpenOrdersAccount( - bankrunContextWrapper, - openbookProgram, - market.publicKey - ); - const names = [ - 'Bob', - 'Marley', - 'Nicky', - 'Minaj', - 'Bad', - 'Bunny', - 'Luis', - 'Miguel', - 'Anita', - 'Soul', - 'Bronco', - 'Marilina', - 'Rytmus', - 'Separ', - 'Meiko', - 'Kaji', - 'Karol', - 'G', - 'Ricky', - 'Martin', - ]; - let index = 1; - for (const name of names) { - index += 1; - openOrdersAccounts.push( - await createOpenOrdersAccountV2( - bankrunContextWrapper, - openbookProgram, - market.publicKey, - openOrdersIndexer, - name, - index - ) - ); - } - console.log(`OpenOrdersAccounts: ${openOrdersAccounts}`); - - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - await initializeSolSpotMarket(driftClient, solOracle); - - await driftClient.updateSpotMarketStepSizeAndTickSize( - 1, - baseSizeLot, - quoteSizeLot - ); - - await driftClient.updateSpotMarketOrdersEnabled(1, true); - - await driftClient.initializeUserAccountAndDepositCollateral( - // @ts-ignore - usdcAmount, - userUsdcAccount.publicKey - ); - - await driftClient.addUser(0); - // @ts-ignore - // await driftClient.deposit(solAmount, 1, userWSolAccount.publicKey); - - fillerDriftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: new anchor.Wallet(fillerKeypair), - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0], - spotMarketIndexes: [0, 1], - subAccountIds: [], - oracleInfos: [ - { - publicKey: solOracle, - source: OracleSource.PYTH, - }, - ], - userStats: true, - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - await fillerDriftClient.subscribe(); - - await bankrunContextWrapper.fundKeypair(fillerKeypair, 10 * 10 ** 9); - - await fillerDriftClient.initializeUserAccount(); - - await fillerDriftClient.addUser(0); - }); - - after(async () => { - await driftClient.unsubscribe(); - await fillerDriftClient.unsubscribe(); - }); - - it('add market', async () => { - await driftClient.initializeOpenbookV2FulfillmentConfig( - solSpotMarketIndex, - market.publicKey - ); - }); - - it('fill ask and bids', async () => { - for (let i = 0; i < 10; i++) { - const ooa = openOrdersAccounts[i]; - for (let j = 0; j < 15; j++) { - await placeOrder( - bankrunContextWrapper, - openbookProgram, - ooa, - openOrdersIndexer, - market.publicKey, - bids.publicKey, - asks.publicKey, - eventHeap.publicKey, - marketBaseVault, - userWSolAccount, - { - side: Side.ASK, - priceLots: new anchor.BN(10000 + (j + 1) * 300), - maxBaseLots: new anchor.BN(1_000_000_000), - maxQuoteLotsIncludingFees: new anchor.BN( - 100_000_000 + (j + 1) * 3_000_000 - ), - clientOrderId: new anchor.BN(0), - orderType: ObOrderType.LIMIT, - expiryTimestamp: new anchor.BN(0), - selfTradeBehavior: SelfTradeBehavior.DECREMENT_TAKE, - limit: new anchor.BN(20), - } - ); - } - } - for (let i = 10; i < 20; i++) { - const ooa = openOrdersAccounts[i]; - for (let j = 0; j < 15; j++) { - console.log('BIDING'); - await placeOrder( - bankrunContextWrapper, - openbookProgram, - ooa, - openOrdersIndexer, - market.publicKey, - bids.publicKey, - asks.publicKey, - eventHeap.publicKey, - marketQuoteVault, - userUsdcAccount.publicKey, - { - side: Side.BID, - priceLots: new anchor.BN(10000 - (j + 1) * 300), - maxBaseLots: new anchor.BN(1_000_000_000), - maxQuoteLotsIncludingFees: new anchor.BN( - 100_000_000 - (j + 1) * 3_000_000 - ), - clientOrderId: new anchor.BN(0), - orderType: ObOrderType.LIMIT, - expiryTimestamp: new anchor.BN(0), - selfTradeBehavior: SelfTradeBehavior.DECREMENT_TAKE, - limit: new anchor.BN(20), - } - ); - } - } - }); - - it('fill long', async () => { - const quoteTokenAmountBefore = driftClient.getTokenAmount(0); - const baseTokenAmountBefore = driftClient.getTokenAmount(1); - - console.log(`quoteTokenAmountBefore ${quoteTokenAmountBefore.toString()}`); - console.log(`baseTokenAmountBefore ${baseTokenAmountBefore.toString()}`); - await placeOrder( - bankrunContextWrapper, - openbookProgram, - openOrdersAccount, - openOrdersIndexer, - market.publicKey, - bids.publicKey, - asks.publicKey, - eventHeap.publicKey, - marketBaseVault, - userWSolAccount, - { - side: Side.ASK, - priceLots: new anchor.BN(10000), - maxBaseLots: new anchor.BN(1_000_000_000), - maxQuoteLotsIncludingFees: new anchor.BN(100_000_000), - clientOrderId: new anchor.BN(0), - orderType: ObOrderType.LIMIT, - expiryTimestamp: new anchor.BN(0), - selfTradeBehavior: SelfTradeBehavior.DECREMENT_TAKE, - limit: new anchor.BN(10), - } - ); - - await driftClient.placeSpotOrder({ - orderType: OrderType.LIMIT, - marketIndex: 1, - // @ts-ignore - baseAssetAmount: driftClient.convertToSpotPrecision(1, 1), - direction: PositionDirection.LONG, - price: PRICE_PRECISION.muln(100), - }); - - const fulfillmentConfig = await driftClient.getOpenbookV2FulfillmentConfig( - market.publicKey - ); - fulfillmentConfig.remainingAccounts = [ - openOrdersAccount, - openOrdersAccounts[2], - openOrdersAccounts[13], - ]; - - const userAccount = driftClient.getUserAccount(); - const order = userAccount.orders.filter( - (order) => order.marketIndex == 1 - )[0]; - await fillerDriftClient.fillSpotOrder( - await driftClient.getUserAccountPublicKey(), - driftClient.getUserAccount(), - order, - fulfillmentConfig - ); - - await driftClient.fetchAccounts(); - - const quoteTokenAmountAfter = driftClient.getTokenAmount(0); - const baseTokenAmountAfter = driftClient.getTokenAmount(1); - - const openOrdersAccountInfo = - await bankrunContextWrapper.connection.getAccountInfo(openOrdersAccount); - const openOrdersAccountParsedData = - await openbookProgram.account.openOrdersAccount.coder.accounts.decode( - 'OpenOrdersAccount', - openOrdersAccountInfo.data - ); - assert( - openOrdersAccountParsedData.position.quoteFreeNative.eq(new BN(100000000)) - ); - - console.log(`quoteTokenAmountAfter ${quoteTokenAmountAfter.toString()}`); - console.log(`baseTokenAmountAfter ${baseTokenAmountAfter.toString()}`); - - assert(baseTokenAmountAfter.eq(LAMPORTS_PRECISION)); - assert(quoteTokenAmountAfter.eq(new BN('199899899999'))); - }); - - it('fill short', async () => { - await placeOrder( - bankrunContextWrapper, - openbookProgram, - openOrdersAccount, - openOrdersIndexer, - market.publicKey, - bids.publicKey, - asks.publicKey, - eventHeap.publicKey, - marketQuoteVault, - userUsdcAccount.publicKey, - { - side: Side.BID, - priceLots: new anchor.BN(10000), - maxBaseLots: new anchor.BN(1_000_000_000), - maxQuoteLotsIncludingFees: new anchor.BN(100_000_000), - clientOrderId: new anchor.BN(0), - orderType: ObOrderType.LIMIT, - expiryTimestamp: new anchor.BN(0), - selfTradeBehavior: SelfTradeBehavior.DECREMENT_TAKE, - limit: new anchor.BN(10), - } - ); - - await driftClient.placeSpotOrder({ - orderType: OrderType.LIMIT, - marketIndex: 1, - // @ts-ignore - baseAssetAmount: driftClient.convertToSpotPrecision(1, 1), - direction: PositionDirection.SHORT, - price: PRICE_PRECISION.muln(100), - }); - - const fulfillmentConfig = await driftClient.getOpenbookV2FulfillmentConfig( - market.publicKey - ); - fulfillmentConfig.remainingAccounts = [ - openOrdersAccount, - openOrdersAccounts[1], - openOrdersAccounts[12], - ]; - - const userAccount = driftClient.getUserAccount(); - const order = userAccount.orders.filter( - (order) => order.marketIndex == 1 - )[0]; - await fillerDriftClient.fillSpotOrder( - await driftClient.getUserAccountPublicKey(), - driftClient.getUserAccount(), - order, - fulfillmentConfig - ); - - await driftClient.fetchAccounts(); - - const quoteTokenAmountAfter = driftClient.getTokenAmount(0); - const baseTokenAmountAfter = driftClient.getTokenAmount(1); - - console.log(`quoteTokenAmountAfter ${quoteTokenAmountAfter.toString()}`); - console.log(`baseTokenAmountAfter ${baseTokenAmountAfter.toString()}`); - - assert(baseTokenAmountAfter.eq(ZERO)); - assert(quoteTokenAmountAfter.eq(new BN('199999799999'))); - - const openOrdersAccountInfo = - await bankrunContextWrapper.connection.getAccountInfo(openOrdersAccount); - const openOrdersAccountParsedData = - await openbookProgram.account.openOrdersAccount.coder.accounts.decode( - 'OpenOrdersAccount', - openOrdersAccountInfo.data - ); - assert(openOrdersAccountParsedData.position.baseFreeNative.eq(new BN(1e9))); - }); -}); diff --git a/tests/serumTest.ts b/tests/serumTest.ts deleted file mode 100644 index 7ede64540d..0000000000 --- a/tests/serumTest.ts +++ /dev/null @@ -1,707 +0,0 @@ -import * as anchor from '@coral-xyz/anchor'; -import { assert } from 'chai'; - -import { Program } from '@coral-xyz/anchor'; - -import { Keypair, PublicKey, Transaction } from '@solana/web3.js'; -import { listMarket, makePlaceOrderTransaction, SERUM } from './serumHelper'; -import { - BN, - TestClient, - EventSubscriber, - OracleSource, - OracleInfo, - PositionDirection, - castNumberToSpotPrecision, - getLimitOrderParams, - getTokenAmount, - isVariant, - PRICE_PRECISION, - SpotBalanceType, -} from '../sdk/src'; - -import { - createUserWithUSDCAndWSOLAccount, - createWSolTokenAccountForUser, - initializeQuoteSpotMarket, - initializeSolSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, -} from './testHelpers'; -import { NATIVE_MINT } from '@solana/spl-token'; -import { Market } from '@project-serum/serum'; -import { getMarketOrderParams, ZERO } from '../sdk'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -describe('serum spot market', () => { - const chProgram = anchor.workspace.Drift as Program; - - let makerDriftClient: TestClient; - let makerWSOL: PublicKey; - - let eventSubscriber: EventSubscriber; - - let bulkAccountLoader: TestBulkAccountLoader; - - let bankrunContextWrapper: BankrunContextWrapper; - - let solOracle: PublicKey; - - let serumMarketPublicKey: PublicKey; - - let usdcMint; - let makerUSDC; - - let takerDriftClient: TestClient; - let _takerWSOL: PublicKey; - let takerUSDC: PublicKey; - - const usdcAmount = new BN(200 * 10 ** 6); - const solAmount = new BN(2 * 10 ** 9); - - let marketIndexes: number[]; - let spotMarketIndexes: number[]; - let oracleInfos: OracleInfo[]; - - const solSpotMarketIndex = 1; - - let openOrdersAccount: PublicKey; - - before(async () => { - const context = await startAnchor( - '', - [ - { - name: 'serum_dex', - programId: new PublicKey( - 'srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX' - ), - }, - ], - [] - ); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - eventSubscriber = new EventSubscriber( - bankrunContextWrapper.connection.toConnection(), - chProgram - ); - - await eventSubscriber.subscribe(); - - usdcMint = await mockUSDCMint(bankrunContextWrapper); - makerUSDC = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - bankrunContextWrapper - ); - makerWSOL = await createWSolTokenAccountForUser( - bankrunContextWrapper, - // @ts-ignore - bankrunContextWrapper.provider.wallet, - solAmount - ); - - solOracle = await mockOracleNoProgram(bankrunContextWrapper, 100); - - marketIndexes = []; - spotMarketIndexes = [0, 1]; - oracleInfos = [{ publicKey: solOracle, source: OracleSource.PYTH }]; - - makerDriftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: bankrunContextWrapper.provider.wallet, - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: marketIndexes, - spotMarketIndexes: spotMarketIndexes, - subAccountIds: [], - oracleInfos, - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - await makerDriftClient.initialize(usdcMint.publicKey, true); - await makerDriftClient.subscribe(); - await makerDriftClient.initializeUserAccount(); - - await initializeQuoteSpotMarket(makerDriftClient, usdcMint.publicKey); - await initializeSolSpotMarket(makerDriftClient, solOracle); - await makerDriftClient.updateSpotMarketStepSizeAndTickSize( - 1, - new BN(100000000), - new BN(100) - ); - await makerDriftClient.updateSpotAuctionDuration(0); - - [takerDriftClient, _takerWSOL, takerUSDC] = - await createUserWithUSDCAndWSOLAccount( - bankrunContextWrapper, - usdcMint, - chProgram, - solAmount, - usdcAmount, - [], - [0, 1], - [ - { - publicKey: solOracle, - source: OracleSource.PYTH, - }, - ], - bulkAccountLoader - ); - - await takerDriftClient.deposit(usdcAmount, 0, takerUSDC); - }); - - after(async () => { - await takerDriftClient.unsubscribe(); - await makerDriftClient.unsubscribe(); - await eventSubscriber.unsubscribe(); - }); - - it('Add Serum Market', async () => { - serumMarketPublicKey = await listMarket({ - context: bankrunContextWrapper, - wallet: bankrunContextWrapper.provider.wallet, - baseMint: NATIVE_MINT, - quoteMint: usdcMint.publicKey, - baseLotSize: 100000000, - quoteLotSize: 100, - dexProgramId: SERUM, - feeRateBps: 0, - }); - - await Market.load( - bankrunContextWrapper.connection.toConnection(), - serumMarketPublicKey, - { commitment: 'confirmed' }, - SERUM - ); - - await makerDriftClient.initializeSerumFulfillmentConfig( - solSpotMarketIndex, - serumMarketPublicKey, - SERUM - ); - }); - - const crankMarkets = async () => { - const openOrdersAccounts = []; - - const market = await Market.load( - bankrunContextWrapper.connection.toConnection(), - serumMarketPublicKey, - { commitment: 'processed' }, - SERUM - ); - - openOrdersAccounts.push(openOrdersAccount); - - const serumFulfillmentConfigAccount = - await makerDriftClient.getSerumV3FulfillmentConfig(serumMarketPublicKey); - openOrdersAccounts.push(serumFulfillmentConfigAccount.serumOpenOrders); - - const consumeEventsIx = await market.makeConsumeEventsInstruction( - openOrdersAccounts, - 10 - ); - - const consumeEventsTx = new Transaction().add(consumeEventsIx); - await bankrunContextWrapper.sendTransaction(consumeEventsTx); - // await provider.sendAndConfirm(consumeEventsTx, []); - - // Open orders need to be sorted correctly but not sure how to do it in js, so will run this - // ix sorted in both direction - const consumeEventsIx2 = await market.makeConsumeEventsInstruction( - openOrdersAccounts.reverse(), - 10 - ); - - const consumeEventsTx2 = new Transaction().add(consumeEventsIx2); - await bankrunContextWrapper.sendTransaction(consumeEventsTx2); - // await provider.sendAndConfirm(consumeEventsTx2, []); - }; - - it('Fill bid', async () => { - const baseAssetAmount = castNumberToSpotPrecision( - 1, - makerDriftClient.getSpotMarketAccount(solSpotMarketIndex) - ); - - await takerDriftClient.placeSpotOrder( - getLimitOrderParams({ - marketIndex: solSpotMarketIndex, - direction: PositionDirection.LONG, - baseAssetAmount, - userOrderId: 1, - price: new BN(100).mul(PRICE_PRECISION), - }) - ); - - await takerDriftClient.fetchAccounts(); - - const spotOrder = takerDriftClient.getOrderByUserId(1); - - assert(isVariant(spotOrder.marketType, 'spot')); - assert(spotOrder.baseAssetAmount.eq(baseAssetAmount)); - - const market = await Market.load( - bankrunContextWrapper.connection.toConnection(), - serumMarketPublicKey, - { commitment: 'recent' }, - SERUM - ); - - // @ts-ignore - const { transaction, signers } = await makePlaceOrderTransaction( - bankrunContextWrapper.connection.toConnection(), - market, - { - // @ts-ignore - owner: bankrunContextWrapper.provider.wallet, - payer: makerWSOL, - side: 'sell', - price: 100, - size: 1, - orderType: 'postOnly', - clientId: undefined, // todo? - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: 'abortTransaction', - } - ); - - openOrdersAccount = signers[0].publicKey; - - const signerKeypairs = signers.map((signer) => { - return Keypair.fromSecretKey(signer.secretKey); - }); - - await bankrunContextWrapper.sendTransaction(transaction, signerKeypairs); - // await provider.sendAndConfirm(transaction, signers); - - const serumFulfillmentConfigAccount = - await makerDriftClient.getSerumV3FulfillmentConfig(serumMarketPublicKey); - const txSig = await makerDriftClient.fillSpotOrder( - await takerDriftClient.getUserAccountPublicKey(), - takerDriftClient.getUserAccount(), - takerDriftClient.getOrderByUserId(1), - serumFulfillmentConfigAccount - ); - - await eventSubscriber.awaitTx(txSig); - bankrunContextWrapper.printTxLogs(txSig); - // await printTxLogs(connection, txSig); - - await takerDriftClient.fetchAccounts(); - - const takerQuoteSpotBalance = takerDriftClient.getSpotPosition(0); - const takerBaseSpotBalance = takerDriftClient.getSpotPosition(1); - - const quoteTokenAmount = getTokenAmount( - takerQuoteSpotBalance.scaledBalance, - takerDriftClient.getQuoteSpotMarketAccount(), - takerQuoteSpotBalance.balanceType - ); - console.log(quoteTokenAmount.toString()); - assert(quoteTokenAmount.eq(new BN(99899999))); - - const baseTokenAmount = getTokenAmount( - takerBaseSpotBalance.scaledBalance, - takerDriftClient.getSpotMarketAccount(1), - takerBaseSpotBalance.balanceType - ); - assert(baseTokenAmount.eq(new BN(1000000000))); - - const takerOrder = takerDriftClient.getUserAccount().orders[0]; - assert(!isVariant(takerOrder.status, 'open')); - - const orderActionRecord = - eventSubscriber.getEventsArray('OrderActionRecord')[0]; - assert(isVariant(orderActionRecord.action, 'fill')); - assert(orderActionRecord.baseAssetAmountFilled.eq(new BN(1000000000))); - assert(orderActionRecord.quoteAssetAmountFilled.eq(new BN(100000000))); - assert(orderActionRecord.takerFee.eq(new BN(100000))); - - await makerDriftClient.fetchAccounts(); - assert(makerDriftClient.getQuoteAssetTokenAmount().eq(new BN(11800))); - - const solSpotMarket = - takerDriftClient.getSpotMarketAccount(solSpotMarketIndex); - const spotFeePoolAmount = getTokenAmount( - solSpotMarket.spotFeePool.scaledBalance, - takerDriftClient.getQuoteSpotMarketAccount(), - SpotBalanceType.DEPOSIT - ); - assert(spotFeePoolAmount.eq(new BN(48200))); - - await crankMarkets(); - }); - - it('Fill ask', async () => { - const baseAssetAmount = castNumberToSpotPrecision( - 1, - makerDriftClient.getSpotMarketAccount(solSpotMarketIndex) - ); - - await takerDriftClient.placeSpotOrder( - getLimitOrderParams({ - marketIndex: solSpotMarketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount, - userOrderId: 1, - price: new BN(100).mul(PRICE_PRECISION), - }) - ); - await takerDriftClient.fetchAccounts(); - - const spotOrder = takerDriftClient.getOrderByUserId(1); - - assert(isVariant(spotOrder.marketType, 'spot')); - assert(spotOrder.baseAssetAmount.eq(baseAssetAmount)); - - const market = await Market.load( - bankrunContextWrapper.connection.toConnection(), - serumMarketPublicKey, - { commitment: 'recent' }, - SERUM - ); - - // @ts-ignore - const { transaction, signers } = await makePlaceOrderTransaction( - bankrunContextWrapper.connection.toConnection(), - market, - { - // @ts-ignore - owner: bankrunContextWrapper.provider.wallet, - payer: makerUSDC.publicKey, - side: 'buy', - price: 100, - size: 1, - orderType: 'postOnly', - clientId: undefined, // todo? - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: 'abortTransaction', - } - ); - - const signerKeypairs = signers.map((signer) => { - return Keypair.fromSecretKey(signer.secretKey); - }); - - await bankrunContextWrapper.sendTransaction(transaction, signerKeypairs); - - const serumFulfillmentConfigAccount = - await makerDriftClient.getSerumV3FulfillmentConfig(serumMarketPublicKey); - const txSig = await makerDriftClient.fillSpotOrder( - await takerDriftClient.getUserAccountPublicKey(), - takerDriftClient.getUserAccount(), - takerDriftClient.getOrderByUserId(1), - serumFulfillmentConfigAccount - ); - - await eventSubscriber.awaitTx(txSig); - bankrunContextWrapper.printTxLogs(txSig); - // await printTxLogs(connection, txSig); - - await takerDriftClient.fetchAccounts(); - - const takerQuoteSpotBalance = takerDriftClient.getSpotPosition(0); - const takerBaseSpotBalance = takerDriftClient.getSpotPosition(1); - - const quoteTokenAmount = getTokenAmount( - takerQuoteSpotBalance.scaledBalance, - takerDriftClient.getQuoteSpotMarketAccount(), - takerQuoteSpotBalance.balanceType - ); - console.log(quoteTokenAmount.toString()); - assert(quoteTokenAmount.eq(new BN(199799999))); - - const baseTokenAmount = getTokenAmount( - takerBaseSpotBalance.scaledBalance, - takerDriftClient.getSpotMarketAccount(1), - takerBaseSpotBalance.balanceType - ); - assert(baseTokenAmount.eq(new BN(0))); - - const takerOrder = takerDriftClient.getUserAccount().orders[0]; - assert(!isVariant(takerOrder.status, 'open')); - - const orderActionRecord = - eventSubscriber.getEventsArray('OrderActionRecord')[0]; - assert(isVariant(orderActionRecord.action, 'fill')); - assert(orderActionRecord.baseAssetAmountFilled.eq(new BN(1000000000))); - assert(orderActionRecord.quoteAssetAmountFilled.eq(new BN(100000000))); - assert(orderActionRecord.takerFee.eq(new BN(100000))); - - assert(makerDriftClient.getQuoteAssetTokenAmount().eq(new BN(23600))); - - const solSpotMarket = - takerDriftClient.getSpotMarketAccount(solSpotMarketIndex); - assert(solSpotMarket.totalSpotFee.eq(new BN(136400))); - const spotFeePoolAmount = getTokenAmount( - solSpotMarket.spotFeePool.scaledBalance, - takerDriftClient.getQuoteSpotMarketAccount(), - SpotBalanceType.DEPOSIT - ); - console.log(spotFeePoolAmount.toString()); - assert(spotFeePoolAmount.eq(new BN(116400))); - - await crankMarkets(); - }); - - // check that moving referrer rebates works properly - it('Fill bid second time', async () => { - const baseAssetAmount = castNumberToSpotPrecision( - 1, - makerDriftClient.getSpotMarketAccount(solSpotMarketIndex) - ); - - await takerDriftClient.placeSpotOrder( - getLimitOrderParams({ - marketIndex: solSpotMarketIndex, - direction: PositionDirection.LONG, - baseAssetAmount, - userOrderId: 1, - price: new BN(100).mul(PRICE_PRECISION), - }) - ); - await takerDriftClient.fetchAccounts(); - - const spotOrder = takerDriftClient.getOrderByUserId(1); - - assert(isVariant(spotOrder.marketType, 'spot')); - assert(spotOrder.baseAssetAmount.eq(baseAssetAmount)); - - const market = await Market.load( - bankrunContextWrapper.connection.toConnection(), - serumMarketPublicKey, - { commitment: 'recent' }, - SERUM - ); - - // @ts-ignore - const { transaction, signers } = await makePlaceOrderTransaction( - bankrunContextWrapper.connection.toConnection(), - market, - { - // @ts-ignore - owner: bankrunContextWrapper.provider.wallet, - payer: makerWSOL, - side: 'sell', - price: 100, - size: 1, - orderType: 'postOnly', - clientId: undefined, // todo? - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: 'abortTransaction', - } - ); - - const signerKeypairs = signers.map((signer) => { - return Keypair.fromSecretKey(signer.secretKey); - }); - - await bankrunContextWrapper.sendTransaction(transaction, signerKeypairs); - // await provider.sendAndConfirm(transaction, signers); - - const serumFulfillmentConfigAccount = - await makerDriftClient.getSerumV3FulfillmentConfig(serumMarketPublicKey); - - const txSig = await makerDriftClient.fillSpotOrder( - await takerDriftClient.getUserAccountPublicKey(), - takerDriftClient.getUserAccount(), - takerDriftClient.getOrderByUserId(1), - serumFulfillmentConfigAccount - ); - bankrunContextWrapper.printTxLogs(txSig); - // await printTxLogs(connection, txSig); - - await eventSubscriber.awaitTx(txSig); - - await takerDriftClient.fetchAccounts(); - - const takerQuoteSpotBalance = takerDriftClient.getSpotPosition(0); - const takerBaseSpotBalance = takerDriftClient.getSpotPosition(1); - - const quoteTokenAmount = getTokenAmount( - takerQuoteSpotBalance.scaledBalance, - takerDriftClient.getQuoteSpotMarketAccount(), - takerQuoteSpotBalance.balanceType - ); - console.log(quoteTokenAmount.toString()); - assert(quoteTokenAmount.eq(new BN(99699999))); // paid ~$.30 - - const baseTokenAmount = getTokenAmount( - takerBaseSpotBalance.scaledBalance, - takerDriftClient.getSpotMarketAccount(1), - takerBaseSpotBalance.balanceType - ); - assert(baseTokenAmount.eq(new BN(1000000000))); - - const takerOrder = takerDriftClient.getUserAccount().orders[0]; - assert(!isVariant(takerOrder.status, 'open')); - - const orderActionRecord = - eventSubscriber.getEventsArray('OrderActionRecord')[0]; - assert(isVariant(orderActionRecord.action, 'fill')); - assert(orderActionRecord.baseAssetAmountFilled.eq(new BN(1000000000))); - assert(orderActionRecord.quoteAssetAmountFilled.eq(new BN(100000000))); - assert(orderActionRecord.takerFee.eq(new BN(100000))); - - const solSpotMarket = - takerDriftClient.getSpotMarketAccount(solSpotMarketIndex); - assert(solSpotMarket.totalSpotFee.eq(new BN(204600))); - // const spotFeePoolAmount = getTokenAmount( - // solSpotMarket.spotFeePool.scaledBalance, - // takerDriftClient.getQuoteSpotMarketAccount(), - // SpotBalanceType.DEPOSIT - // ); - console.log(`${orderActionRecord.fillerReward}`); - console.log(`${solSpotMarket.cumulativeDepositInterest.toString()}`); - console.log(`${orderActionRecord.makerFee.toString()}`); - console.log(solSpotMarket.depositBalance.toString()); - console.log(`${solSpotMarket.borrowBalance.toString()}`); - // TODO: Figure out why this value comes out as 164600 - // assert(spotFeePoolAmount.eq(new BN(184600))); - - assert(orderActionRecord.fillerReward.eq(new BN(11800))); - assert(orderActionRecord.makerFee.eq(new BN(0))); - assert(solSpotMarket.depositBalance.eq(new BN(1_000_000_000))); - - await crankMarkets(); - }); - - // check that moving referrer rebates works properly - it('Place and take', async () => { - const market = await Market.load( - bankrunContextWrapper.connection.toConnection(), - serumMarketPublicKey, - { commitment: 'recent' }, - SERUM - ); - - // @ts-ignore - const { transaction, signers } = await makePlaceOrderTransaction( - bankrunContextWrapper.connection.toConnection(), - market, - { - // @ts-ignore - owner: bankrunContextWrapper.provider.wallet, - payer: makerUSDC.publicKey, - side: 'buy', - price: 100, - size: 1, - orderType: 'postOnly', - clientId: undefined, // todo? - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: 'abortTransaction', - } - ); - - const signerKeypairs = signers.map((signer) => { - return Keypair.fromSecretKey(signer.secretKey); - }); - - await bankrunContextWrapper.sendTransaction(transaction, signerKeypairs); - // await provider.sendAndConfirm(transaction, signers); - const baseAssetAmount = castNumberToSpotPrecision( - 1, - makerDriftClient.getSpotMarketAccount(solSpotMarketIndex) - ); - - const serumFulfillmentConfigAccount = - await makerDriftClient.getSerumV3FulfillmentConfig(serumMarketPublicKey); - - const txSig = await takerDriftClient.placeAndTakeSpotOrder( - getMarketOrderParams({ - marketIndex: solSpotMarketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount, - userOrderId: 1, - }), - serumFulfillmentConfigAccount - ); - - bankrunContextWrapper.printTxLogs(txSig); - // await printTxLogs(connection, txSig); - - await eventSubscriber.awaitTx(txSig); - - await takerDriftClient.fetchAccounts(); - - const takerQuoteSpotBalance = takerDriftClient.getSpotPosition(0); - const takerBaseSpotBalance = takerDriftClient.getSpotPosition(1); - - const quoteTokenAmount = getTokenAmount( - takerQuoteSpotBalance.scaledBalance, - takerDriftClient.getQuoteSpotMarketAccount(), - takerQuoteSpotBalance.balanceType - ); - console.log(quoteTokenAmount.toString()); - assert(quoteTokenAmount.eq(new BN(199599999))); // paid ~$.40 - - const baseTokenAmount = getTokenAmount( - takerBaseSpotBalance.scaledBalance, - takerDriftClient.getSpotMarketAccount(1), - takerBaseSpotBalance.balanceType - ); - assert(baseTokenAmount.eq(ZERO)); - - const takerOrder = takerDriftClient.getUserAccount().orders[0]; - assert(!isVariant(takerOrder.status, 'open')); - - const orderActionRecord = - eventSubscriber.getEventsArray('OrderActionRecord')[0]; - assert(isVariant(orderActionRecord.action, 'fill')); - assert(orderActionRecord.baseAssetAmountFilled.eq(new BN(1000000000))); - assert(orderActionRecord.quoteAssetAmountFilled.eq(new BN(100000000))); - assert(orderActionRecord.takerFee.eq(new BN(100000))); - - const solSpotMarket = - takerDriftClient.getSpotMarketAccount(solSpotMarketIndex); - console.log(solSpotMarket.totalSpotFee.toString()); - assert(solSpotMarket.totalSpotFee.eq(new BN(284600))); - // const spotFeePoolAmount = getTokenAmount( - // solSpotMarket.spotFeePool.scaledBalance, - // takerDriftClient.getQuoteSpotMarketAccount(), - // SpotBalanceType.DEPOSIT - // ); - console.log(`${orderActionRecord.fillerReward.toString()}`); - console.log(`${solSpotMarket.cumulativeDepositInterest.toString()}`); - console.log(`${orderActionRecord.makerFee.toString()}`); - console.log(`${solSpotMarket.borrowBalance.toString()}`); - - assert(orderActionRecord.fillerReward.eq(new BN(0))); - assert(orderActionRecord.makerFee.eq(new BN(0))); - assert(solSpotMarket.depositBalance.eq(new BN(0))); - // TODO: Figure out why this value comes out as 224000 - // assert(spotFeePoolAmount.eq(new BN(264600))); - - await crankMarkets(); - }); -}); diff --git a/tests/spotSwap.ts b/tests/spotSwap.ts index d7e244e224..ed87155636 100644 --- a/tests/spotSwap.ts +++ b/tests/spotSwap.ts @@ -26,6 +26,7 @@ import { UserStatsAccount, getUserStatsAccountPublicKey, FUEL_WINDOW, + getSerumOpenOrdersPublicKey, } from '../sdk/src'; import { @@ -217,14 +218,6 @@ describe('spot swap', () => { console.log('\n\n\n\n\n here \n\n\n\n\n'); - await makerDriftClient.initializeSerumFulfillmentConfig( - solSpotMarketIndex, - serumMarketPublicKey, - SERUM - ); - - console.log('\n\n\n\n\n here \n\n\n\n\n'); - const market = await Market.load( bankrunContextWrapper.connection.toConnection(), serumMarketPublicKey, @@ -262,11 +255,12 @@ describe('spot swap', () => { SERUM ); - openOrdersAccounts.push(openOrdersAccount); - - const serumFulfillmentConfigAccount = - await makerDriftClient.getSerumV3FulfillmentConfig(serumMarketPublicKey); - openOrdersAccounts.push(serumFulfillmentConfigAccount.serumOpenOrders); + openOrdersAccounts.push( + getSerumOpenOrdersPublicKey( + makerDriftClient.program.programId, + market.publicKey + ) + ); const consumeEventsIx = await market.makeConsumeEventsInstruction( openOrdersAccounts, @@ -353,23 +347,21 @@ describe('spot swap', () => { } ); - const serumConfig = await takerDriftClient.getSerumV3FulfillmentConfig( - market.publicKey - ); const settleFundsIx = DexInstructions.settleFunds({ market: market.publicKey, openOrders: takerOpenOrders, owner: takerDriftClient.wallet.publicKey, // @ts-ignore - baseVault: serumConfig.serumBaseVault, + baseVault: market._decoded.baseVault, // @ts-ignore - quoteVault: serumConfig.serumQuoteVault, + quoteVault: market._decoded.quoteVault, baseWallet: takerWSOL, quoteWallet: takerUSDC, vaultSigner: getSerumSignerPublicKey( market.programId, market.publicKey, - serumConfig.serumSignerNonce + // @ts-ignore + market._decoded.vaultSignerNonce ), programId: market.programId, }); @@ -541,23 +533,21 @@ describe('spot swap', () => { } ); - const serumConfig = await takerDriftClient.getSerumV3FulfillmentConfig( - market.publicKey - ); const settleFundsIx = DexInstructions.settleFunds({ market: market.publicKey, openOrders: takerOpenOrders, owner: takerDriftClient.wallet.publicKey, // @ts-ignore - baseVault: serumConfig.serumBaseVault, + baseVault: market._decoded.baseVault, // @ts-ignore - quoteVault: serumConfig.serumQuoteVault, + quoteVault: market._decoded.quoteVault, baseWallet: takerWSOL, quoteWallet: takerUSDC, vaultSigner: getSerumSignerPublicKey( market.programId, market.publicKey, - serumConfig.serumSignerNonce + // @ts-ignore + market._decoded.vaultSignerNonce ), programId: market.programId, });