From 93f9eb905438b5373bcb01a6d73bf2fb9dac3e45 Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Fri, 25 Jul 2025 16:08:02 -0400 Subject: [PATCH 001/216] program: calc-ref-price-offset-calc-enhance --- programs/drift/src/math/amm_spread.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/programs/drift/src/math/amm_spread.rs b/programs/drift/src/math/amm_spread.rs index 744d253fb7..7509035f8f 100644 --- a/programs/drift/src/math/amm_spread.rs +++ b/programs/drift/src/math/amm_spread.rs @@ -11,9 +11,9 @@ use crate::math::constants::{ AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO_I128, AMM_TO_QUOTE_PRECISION_RATIO_I128, BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, DEFAULT_LARGE_BID_ASK_FACTOR, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, FUNDING_RATE_BUFFER, - MAX_BID_ASK_INVENTORY_SKEW_FACTOR, PEG_PRECISION, PERCENTAGE_PRECISION, - PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, - PRICE_PRECISION_I64, + FUNDING_RATE_OFFSET_DENOMINATOR, MAX_BID_ASK_INVENTORY_SKEW_FACTOR, PEG_PRECISION, + PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, + PRICE_PRECISION_I128, PRICE_PRECISION_I64, }; use crate::math::safe_math::SafeMath; use crate::state::perp_market::{ContractType, PerpMarket, AMM}; @@ -591,9 +591,13 @@ pub fn calculate_reference_price_offset( let mark_premium_day: i64 = last_24h_avg_funding_rate .safe_div(FUNDING_RATE_BUFFER.cast()?)? .safe_mul(24)? + .safe_sub( + oracle_twap_slow + .abs() + .safe_div(FUNDING_RATE_OFFSET_DENOMINATOR)?, + )? .clamp(-max_offset_in_price, max_offset_in_price); // todo: look at how 24h funding is calc w.r.t. the funding_period - - // take average clamped premium as the price-based offset + // take average clamped premium as the price-based offset let mark_premium_avg = mark_premium_minute .safe_add(mark_premium_hour)? .safe_add(mark_premium_day)? From f16158958b302351d940e6d7fc72aa5bba6dbaac Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Tue, 29 Jul 2025 17:41:39 -0400 Subject: [PATCH 002/216] less jumpy calc and smaller control for max offset --- programs/drift/src/math/amm_spread.rs | 14 +++++--------- programs/drift/src/state/perp_market.rs | 6 ++++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/programs/drift/src/math/amm_spread.rs b/programs/drift/src/math/amm_spread.rs index 7509035f8f..5953b04d0f 100644 --- a/programs/drift/src/math/amm_spread.rs +++ b/programs/drift/src/math/amm_spread.rs @@ -607,17 +607,13 @@ pub fn calculate_reference_price_offset( .safe_mul(PRICE_PRECISION_I64)? .safe_div(reserve_price.cast()?)?; - let inventory_pct = liquidity_fraction - .cast::()? - .safe_mul(max_offset_pct)? - .safe_div(PERCENTAGE_PRECISION.cast::()?)? - .clamp(-max_offset_pct, max_offset_pct); - // only apply when inventory is consistent with recent and 24h market premium - let offset_pct = if (mark_premium_avg_pct >= 0 && inventory_pct >= 0) - || (mark_premium_avg_pct <= 0 && inventory_pct <= 0) + let offset_pct = if (mark_premium_avg_pct >= 0 && liquidity_fraction >= 0) + || (mark_premium_avg_pct <= 0 && liquidity_fraction <= 0) { - mark_premium_avg_pct.safe_add(inventory_pct)? + mark_premium_avg_pct + .safe_mul(liquidity_fraction.cast::()?)? + .safe_div(5)? } else { 0 }; diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 2135183f23..6fbad9209b 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -1225,15 +1225,17 @@ impl AMM { pub fn get_max_reference_price_offset(self) -> DriftResult { if self.curve_update_intensity <= 100 { return Ok(0); + } else if self.curve_update_intensity >= 200 { + return Ok(self.max_spread.cast::()? / 2); } let lower_bound_multiplier: i64 = self.curve_update_intensity.safe_sub(100)?.cast::()?; - // always higher of 1-100 bps of price offset and half of the market's max_spread + // always the lesser of 1-100 bps of price offset and half of the market's max_spread let lb_bps = (PERCENTAGE_PRECISION.cast::()? / 10000).safe_mul(lower_bound_multiplier)?; - let max_offset = (self.max_spread.cast::()? / 2).max(lb_bps); + let max_offset = (self.max_spread.cast::()? / 2).min(lb_bps); Ok(max_offset) } From 004efafb5bc26928cbfcb1f4117a231bbe76f5cf Mon Sep 17 00:00:00 2001 From: wphan Date: Sat, 26 Jul 2025 19:37:49 -0700 Subject: [PATCH 003/216] sdk: add detailed subscription log name (#1773) --- sdk/src/accounts/webSocketAccountSubscriber.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sdk/src/accounts/webSocketAccountSubscriber.ts b/sdk/src/accounts/webSocketAccountSubscriber.ts index 5af792f5c3..6e04beaa57 100644 --- a/sdk/src/accounts/webSocketAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketAccountSubscriber.ts @@ -13,6 +13,7 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { dataAndSlot?: DataAndSlot; bufferAndSlot?: BufferAndSlot; accountName: string; + logAccountName: string; program: Program; accountPublicKey: PublicKey; decodeBufferFn: (buffer: Buffer) => T; @@ -37,13 +38,14 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { commitment?: Commitment ) { this.accountName = accountName; + this.logAccountName = `${accountName}-${accountPublicKey.toBase58()}`; this.program = program; this.accountPublicKey = accountPublicKey; this.decodeBufferFn = decodeBuffer; this.resubOpts = resubOpts; if (this.resubOpts?.resubTimeoutMs < 1000) { console.log( - 'resubTimeoutMs should be at least 1000ms to avoid spamming resub' + `resubTimeoutMs should be at least 1000ms to avoid spamming resub ${this.logAccountName}` ); } this.receivingData = false; @@ -108,7 +110,7 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { if (this.receivingData) { if (this.resubOpts?.logResubMessages) { console.log( - `No ws data from ${this.accountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing` + `No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing` ); } await this.unsubscribe(true); From d4953c4ef8944f8727e43b0cc53e274cbce9647c Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 27 Jul 2025 02:42:23 +0000 Subject: [PATCH 004/216] sdk: release v2.130.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index f50dc831a7..6132d5b393 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.1 \ No newline at end of file +2.130.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index c0a3680d2f..4fcc7319cc 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.1", + "version": "2.130.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 2c09f905aa41265d16b123543072462584b59722 Mon Sep 17 00:00:00 2001 From: wphan Date: Sun, 27 Jul 2025 17:49:53 -0700 Subject: [PATCH 005/216] sdk: ws resubscribe verbose logging (#1774) --- .../accounts/webSocketAccountSubscriber.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/sdk/src/accounts/webSocketAccountSubscriber.ts b/sdk/src/accounts/webSocketAccountSubscriber.ts index 6e04beaa57..7aa4d2435e 100644 --- a/sdk/src/accounts/webSocketAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketAccountSubscriber.ts @@ -55,6 +55,11 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { async subscribe(onChange: (data: T) => void): Promise { if (this.listenerId != null || this.isUnsubscribing) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Subscribe returning early - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` + ); + } return; } @@ -104,18 +109,34 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { async () => { if (this.isUnsubscribing) { // If we are in the process of unsubscribing, do not attempt to resubscribe + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Timeout fired but isUnsubscribing=true, skipping resubscribe` + ); + } return; } if (this.receivingData) { if (this.resubOpts?.logResubMessages) { console.log( - `No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing` + `No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` ); } await this.unsubscribe(true); this.receivingData = false; await this.subscribe(this.onChange); + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Resubscribe completed - receivingData=${this.receivingData}, listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` + ); + } + } else { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Timeout fired but receivingData=false, skipping resubscribe` + ); + } } }, this.resubOpts?.resubTimeoutMs From b5486ef2263038ffff0aa0b8465ea7ca00e08228 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 00:54:04 +0000 Subject: [PATCH 006/216] sdk: release v2.130.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 6132d5b393..1dab62c18e 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.2 \ No newline at end of file +2.130.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 4fcc7319cc..a756f36094 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.2", + "version": "2.130.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 4ab214375c60902ea421fc181cdc298dae345a70 Mon Sep 17 00:00:00 2001 From: wphan Date: Sun, 27 Jul 2025 21:25:04 -0700 Subject: [PATCH 007/216] sdk: add timing on ws unsubscribe --- .../accounts/webSocketAccountSubscriber.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/sdk/src/accounts/webSocketAccountSubscriber.ts b/sdk/src/accounts/webSocketAccountSubscriber.ts index 7aa4d2435e..3ea3c08b51 100644 --- a/sdk/src/accounts/webSocketAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketAccountSubscriber.ts @@ -214,11 +214,30 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { this.timeoutId = undefined; if (this.listenerId != null) { - const promise = this.program.provider.connection - .removeAccountChangeListener(this.listenerId) + const promise = Promise.race([ + this.program.provider.connection.removeAccountChangeListener( + this.listenerId + ), + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error( + `Unsubscribe timeout for account ${this.logAccountName}` + ) + ), + 10000 + ) + ), + ]) .then(() => { this.listenerId = undefined; this.isUnsubscribing = false; + }) + .catch(() => { + // Force cleanup even if RPC call fails + this.listenerId = undefined; + this.isUnsubscribing = false; }); return promise; } else { From 6b6bee7f71ead3f543bb9a436f3fc9b49fb29db6 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 04:29:59 +0000 Subject: [PATCH 008/216] sdk: release v2.130.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 1dab62c18e..8da3ec0371 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.3 \ No newline at end of file +2.130.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index a756f36094..d3f97b7914 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.3", + "version": "2.130.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 1a8d0e8af8cc8e4dfcd2a04df8942ae67129b645 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:46:17 -0400 Subject: [PATCH 009/216] program: add high leverage maintenance mode (#1759) * program: add-high-leverage-maintenance-mode * hlm disable bool * rm update last active slot in settle pnl * improve booting logic * add type * cargo fmt -- --------- Co-authored-by: Chris Heaney Co-authored-by: Nick Caradonna --- programs/drift/src/controller/liquidation.rs | 8 +-- programs/drift/src/controller/orders.rs | 10 ++-- programs/drift/src/instructions/keeper.rs | 53 ++++++++++++++++--- programs/drift/src/instructions/user.rs | 2 +- programs/drift/src/lib.rs | 3 +- programs/drift/src/math/margin.rs | 2 +- programs/drift/src/math/orders.rs | 2 +- .../src/state/high_leverage_mode_config.rs | 9 +++- programs/drift/src/state/user.rs | 4 +- sdk/src/driftClient.ts | 3 +- sdk/src/types.ts | 3 ++ sdk/src/user.ts | 24 +++++---- 12 files changed, 89 insertions(+), 34 deletions(-) diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index d470c4757b..5e2d73f1de 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -337,7 +337,7 @@ pub fn liquidate_perp( let margin_ratio = perp_market_map.get_ref(&market_index)?.get_margin_ratio( user_base_asset_amount.cast()?, MarginRequirementType::Maintenance, - user.is_high_leverage_mode(), + user.is_high_leverage_mode(MarginRequirementType::Maintenance), )?; let margin_ratio_with_buffer = margin_ratio.safe_add(liquidation_margin_buffer_ratio)?; @@ -351,7 +351,9 @@ pub fn liquidate_perp( .price; let liquidator_fee = get_liquidation_fee( - market.get_base_liquidator_fee(user.is_high_leverage_mode()), + market.get_base_liquidator_fee( + user.is_high_leverage_mode(MarginRequirementType::Maintenance), + ), market.get_max_liquidation_fee()?, user.last_active_slot, slot, @@ -964,7 +966,7 @@ pub fn liquidate_perp_with_fill( let margin_ratio = perp_market_map.get_ref(&market_index)?.get_margin_ratio( user_base_asset_amount.cast()?, MarginRequirementType::Maintenance, - user.is_high_leverage_mode(), + user.is_high_leverage_mode(MarginRequirementType::Maintenance), )?; let margin_ratio_with_buffer = margin_ratio.safe_add(liquidation_margin_buffer_ratio)?; diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 97603981f9..2de96e9a68 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -126,7 +126,7 @@ pub fn place_perp_order( if let Some(config) = high_leverage_mode_config { let mut config = load_mut!(config)?; if !config.is_full() || params.is_max_leverage_order() { - config.update_user(user)?; + config.enable_high_leverage(user)?; } else { msg!("high leverage mode config is full"); } @@ -2114,7 +2114,7 @@ pub fn fulfill_perp_order_with_amm( user_stats, fee_structure, &MarketType::Perp, - user.is_high_leverage_mode(), + user.is_high_leverage_mode(MarginRequirementType::Initial), )?; let (base_asset_amount, limit_price) = calculate_base_asset_amount_for_amm_to_fulfill( &user.orders[order_index], @@ -2223,7 +2223,7 @@ pub fn fulfill_perp_order_with_amm( quote_asset_amount_surplus, order_post_only, market.fee_adjustment, - user.is_high_leverage_mode(), + user.is_high_leverage_mode(MarginRequirementType::Initial), )?; let user_position_delta = @@ -2714,7 +2714,7 @@ pub fn fulfill_perp_order_with_match( referrer_stats, &MarketType::Perp, market.fee_adjustment, - taker.is_high_leverage_mode(), + taker.is_high_leverage_mode(MarginRequirementType::Initial), )?; // Increment the markets house's total fee variables @@ -3357,7 +3357,7 @@ pub fn burn_user_lp_shares_for_risk_reduction( quote_oracle_price, margin_calc.margin_shortage()?, user_custom_margin_ratio, - user.is_high_leverage_mode(), + user.is_high_leverage_mode(MarginRequirementType::Initial), )?; let (position_delta, pnl) = burn_lp_shares( diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index f94a09189d..1a3b6ce491 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -953,8 +953,6 @@ pub fn handle_settle_pnl<'c: 'info, 'info>( SettlePnlMode::MustSettle, ) .map(|_| ErrorCode::InvalidOracleForSettlePnl)?; - - user.update_last_active_slot(clock.slot); } let spot_market = spot_market_map.get_quote_spot_market()?; @@ -1039,8 +1037,6 @@ pub fn handle_settle_multiple_pnls<'c: 'info, 'info>( mode, ) .map(|_| ErrorCode::InvalidOracleForSettlePnl)?; - - user.update_last_active_slot(clock.slot); } } @@ -2744,6 +2740,7 @@ pub fn handle_update_user_gov_token_insurance_stake_devnet( pub fn handle_disable_user_high_leverage_mode<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DisableUserHighLeverageMode<'info>>, + disable_maintenance: bool, ) -> Result<()> { let state = &ctx.accounts.state; let mut user = load_mut!(ctx.accounts.user)?; @@ -2762,13 +2759,45 @@ pub fn handle_disable_user_high_leverage_mode<'c: 'info, 'info>( Some(state.oracle_guard_rails), )?; + let in_high_leverage_mode = user.is_high_leverage_mode(MarginRequirementType::Maintenance); validate!( - user.margin_mode == MarginMode::HighLeverage, + in_high_leverage_mode, ErrorCode::DefaultError, - "user must be in high leverage mode" + "user is not in high leverage mode" )?; - user.margin_mode = MarginMode::Default; + let old_margin_mode = user.margin_mode; + + if disable_maintenance { + validate!( + user.margin_mode == MarginMode::HighLeverageMaintenance, + ErrorCode::DefaultError, + "user must be in high leverage maintenance mode" + )?; + + user.margin_mode = MarginMode::Default; + } else { + let mut has_high_leverage_pos = false; + for position in user.perp_positions.iter().filter(|p| !p.is_available()) { + let perp_market = perp_market_map.get_ref(&position.market_index)?; + if perp_market.is_high_leverage_mode_enabled() { + has_high_leverage_pos = true; + break; + } + } + + if !has_high_leverage_pos { + user.margin_mode = MarginMode::Default; + } else { + validate!( + user.margin_mode == MarginMode::HighLeverage, + ErrorCode::DefaultError, + "user must be in high leverage mode" + )?; + + user.margin_mode = MarginMode::HighLeverageMaintenance; + } + } let custom_margin_ratio_before = user.max_margin_ratio; user.max_margin_ratio = 0; @@ -2821,7 +2850,15 @@ pub fn handle_disable_user_high_leverage_mode<'c: 'info, 'info>( let mut config = load_mut!(ctx.accounts.high_leverage_mode_config)?; - config.current_users = config.current_users.safe_sub(1)?; + if old_margin_mode == MarginMode::HighLeverageMaintenance { + config.current_maintenance_users = config.current_maintenance_users.safe_sub(1)?; + } else { + config.current_users = config.current_users.safe_sub(1)?; + } + + if user.margin_mode == MarginMode::HighLeverageMaintenance { + config.current_maintenance_users = config.current_maintenance_users.safe_add(1)?; + } config.validate()?; diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index e14444bbfc..abeb747d90 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -3434,7 +3434,7 @@ pub fn handle_enable_user_high_leverage_mode<'c: 'info, 'info>( let mut config = load_mut!(ctx.accounts.high_leverage_mode_config)?; - config.update_user(&mut user)?; + config.enable_high_leverage(&mut user)?; Ok(()) } diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 17ec3eec82..16e1d89fa4 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -491,8 +491,9 @@ pub mod drift { pub fn disable_user_high_leverage_mode<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DisableUserHighLeverageMode<'info>>, + disable_maintenance: bool, ) -> Result<()> { - handle_disable_user_high_leverage_mode(ctx) + handle_disable_user_high_leverage_mode(ctx, disable_maintenance) } pub fn update_user_fuel_bonus<'c: 'info, 'info>( diff --git a/programs/drift/src/math/margin.rs b/programs/drift/src/math/margin.rs index 73415cb58b..6aee60be04 100644 --- a/programs/drift/src/math/margin.rs +++ b/programs/drift/src/math/margin.rs @@ -252,7 +252,7 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( } let user_pool_id = user.pool_id; - let user_high_leverage_mode = user.is_high_leverage_mode(); + let user_high_leverage_mode = user.is_high_leverage_mode(context.margin_type); for spot_position in user.spot_positions.iter() { validation::position::validate_spot_position(spot_position)?; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 0a808fc03a..fa329750a5 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -836,7 +836,7 @@ pub fn calculate_max_perp_order_size( )?; let user_custom_margin_ratio = user.max_margin_ratio; - let user_high_leverage_mode = user.is_high_leverage_mode(); + let user_high_leverage_mode = user.is_high_leverage_mode(MarginRequirementType::Initial); let free_collateral_before = total_collateral.safe_sub(margin_requirement.cast()?)?; diff --git a/programs/drift/src/state/high_leverage_mode_config.rs b/programs/drift/src/state/high_leverage_mode_config.rs index a520406a49..2e69bd2553 100644 --- a/programs/drift/src/state/high_leverage_mode_config.rs +++ b/programs/drift/src/state/high_leverage_mode_config.rs @@ -15,7 +15,9 @@ pub struct HighLeverageModeConfig { pub max_users: u32, pub current_users: u32, pub reduce_only: u8, - pub padding: [u8; 31], + pub padding1: [u8; 3], + pub current_maintenance_users: u32, + pub padding2: [u8; 24], } // implement SIZE const for ProtocolIfSharesTransferConfig @@ -44,10 +46,13 @@ impl HighLeverageModeConfig { (self.current_users >= self.max_users) || self.is_reduce_only() } - pub fn update_user(&mut self, user: &mut User) -> DriftResult { + pub fn enable_high_leverage(&mut self, user: &mut User) -> DriftResult { if user.margin_mode == MarginMode::HighLeverage { return Ok(()); } + if user.margin_mode == MarginMode::HighLeverageMaintenance { + self.current_maintenance_users = self.current_maintenance_users.saturating_sub(1); + } user.margin_mode = MarginMode::HighLeverage; diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 5b08d8eff9..4056dbb2d8 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -443,8 +443,10 @@ impl User { false } - pub fn is_high_leverage_mode(&self) -> bool { + pub fn is_high_leverage_mode(&self, margin_type: MarginRequirementType) -> bool { self.margin_mode == MarginMode::HighLeverage + || (margin_type == MarginRequirementType::Maintenance + && self.margin_mode == MarginMode::HighLeverageMaintenance) } pub fn get_fuel_bonus_numerator(&self, now: i64) -> DriftResult { diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 1096b3b9fb..b7a78062d8 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -9215,7 +9215,8 @@ export class DriftClient { ) { let feeTier; const userHLM = - (user?.isHighLeverageMode() ?? false) || enteringHighLeverageMode; + (user?.isHighLeverageMode('Initial') ?? false) || + enteringHighLeverageMode; if (user && !userHLM) { feeTier = user.getUserFeeTier(marketType); } else { diff --git a/sdk/src/types.ts b/sdk/src/types.ts index e4fb4877cc..9697181936 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -74,6 +74,9 @@ export enum UserStatus { export class MarginMode { static readonly DEFAULT = { default: {} }; static readonly HIGH_LEVERAGE = { highLeverage: {} }; + static readonly HIGH_LEVERAGE_MAINTENANCE = { + highLeverageMaintenance: {}, + }; } export class ContractType { diff --git a/sdk/src/user.ts b/sdk/src/user.ts index c57245ade6..456cf50b38 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -700,7 +700,7 @@ export class User { baseAssetAmount, 'Initial', this.getUserAccount().maxMarginRatio, - enterHighLeverageMode || this.isHighLeverageMode() + enterHighLeverageMode || this.isHighLeverageMode('Initial') ); return freeCollateral.mul(MARGIN_PRECISION).div(new BN(marginRatio)); @@ -1478,7 +1478,7 @@ export class User { baseAssetAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode() || enteringHighLeverage + this.isHighLeverageMode(marginCategory) || enteringHighLeverage ) ); @@ -1920,13 +1920,13 @@ export class User { perpMarketIndex, PositionDirection.LONG, false, - enterHighLeverageMode || this.isHighLeverageMode() + enterHighLeverageMode || this.isHighLeverageMode('Initial') ).tradeSize, this.getMaxTradeSizeUSDCForPerp( perpMarketIndex, PositionDirection.SHORT, false, - enterHighLeverageMode || this.isHighLeverageMode() + enterHighLeverageMode || this.isHighLeverageMode('Initial') ).tradeSize ).sub(lpBuffer), ZERO @@ -2091,8 +2091,12 @@ export class User { return (this.getUserAccount().status & UserStatus.BANKRUPT) > 0; } - public isHighLeverageMode(): boolean { - return isVariant(this.getUserAccount().marginMode, 'highLeverage'); + public isHighLeverageMode(marginCategory: MarginCategory): boolean { + return ( + isVariant(this.getUserAccount().marginMode, 'highLeverage') || + (isVariant(marginCategory, 'maintenance') && + isVariant(this.getUserAccount().marginMode, 'highLeverageMaintenance')) + ); } /** @@ -2420,7 +2424,7 @@ export class User { baseAssetAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode() || enteringHighLeverage + this.isHighLeverageMode(marginCategory) || enteringHighLeverage ); return liabilityValue.mul(new BN(marginRatio)).div(MARGIN_PRECISION); @@ -2467,7 +2471,7 @@ export class User { proposedBaseAssetAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode() || enteringHighLeverage + this.isHighLeverageMode(marginCategory) || enteringHighLeverage ); const marginRatioQuotePrecision = new BN(marginRatio) @@ -3478,7 +3482,7 @@ export class User { let feeTierIndex = 0; if (isVariant(marketType, 'perp')) { - if (this.isHighLeverageMode()) { + if (this.isHighLeverageMode('Initial')) { return state.perpFeeStructure.feeTiers[0]; } @@ -3825,7 +3829,7 @@ export class User { worstCaseBaseAmount.abs(), marginCategory, this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode() + this.isHighLeverageMode(marginCategory) ) ); From 79ba807ccb6811176584e619ec833a694066dffa Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:51:42 +0000 Subject: [PATCH 010/216] sdk: release v2.130.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8da3ec0371..29693b8079 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.4 \ No newline at end of file +2.130.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index d3f97b7914..b25b90d04e 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.4", + "version": "2.130.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 6848d82c5330943b807e7fe5e7511140fceecbf8 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 28 Jul 2025 07:46:07 -0700 Subject: [PATCH 011/216] sdk: log ws unsubscribe timeout error --- sdk/src/accounts/webSocketAccountSubscriber.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/src/accounts/webSocketAccountSubscriber.ts b/sdk/src/accounts/webSocketAccountSubscriber.ts index 3ea3c08b51..deee456166 100644 --- a/sdk/src/accounts/webSocketAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketAccountSubscriber.ts @@ -234,8 +234,11 @@ export class WebSocketAccountSubscriber implements AccountSubscriber { this.listenerId = undefined; this.isUnsubscribing = false; }) - .catch(() => { - // Force cleanup even if RPC call fails + .catch((error) => { + console.error( + `[${this.logAccountName}] Unsubscribe failed, forcing cleanup - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`, + error + ); this.listenerId = undefined; this.isUnsubscribing = false; }); From e049be9e5e6992a44eeae5661c169b52220f8125 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:50:55 +0000 Subject: [PATCH 012/216] sdk: release v2.130.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 29693b8079..64f81b3730 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.5 \ No newline at end of file +2.130.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index b25b90d04e..b846b55767 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.5", + "version": "2.130.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 94e35e80d2747cce47304ec8c8463c91c7689681 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 29 Jul 2025 01:07:45 +0800 Subject: [PATCH 013/216] Remove barrel imports from SDK (#1777) --- sdk/bun.lock | 233 +++++++++++++++++- sdk/package.json | 6 +- sdk/src/accounts/types.ts | 4 +- sdk/src/addresses/pda.ts | 2 +- sdk/src/clock/clockSubscriber.ts | 2 +- sdk/src/config.ts | 4 +- sdk/src/constants/perpMarkets.ts | 4 +- sdk/src/constants/spotMarkets.ts | 4 +- sdk/src/decode/user.ts | 5 +- sdk/src/dlob/DLOB.ts | 42 ++-- sdk/src/dlob/DLOBNode.ts | 14 +- sdk/src/dlob/NodeList.ts | 7 +- sdk/src/dlob/orderBookLevels.ts | 25 +- .../indicativeQuotesSender.ts | 2 +- sdk/src/math/amm.ts | 3 +- sdk/src/math/auction.ts | 10 +- sdk/src/math/bankruptcy.ts | 4 +- sdk/src/math/conversion.ts | 2 +- sdk/src/math/fuel.ts | 2 +- sdk/src/math/margin.ts | 14 +- sdk/src/math/position.ts | 9 +- sdk/src/math/protectedMakerParams.ts | 3 +- sdk/src/math/state.ts | 7 +- sdk/src/math/userStatus.ts | 2 +- sdk/src/math/utils.ts | 3 +- sdk/src/oracles/pythLazerClient.ts | 3 +- sdk/src/oracles/pythPullClient.ts | 3 +- sdk/src/swift/swiftOrderSubscriber.ts | 12 +- sdk/src/tokenFaucet.ts | 3 +- sdk/src/tx/txParamProcessor.ts | 2 +- sdk/src/types.ts | 3 +- sdk/src/user.ts | 28 ++- sdk/src/userMap/userMap.ts | 21 +- sdk/src/userMap/userStatsMap.ts | 18 +- .../util/TransactionConfirmationManager.ts | 2 +- 35 files changed, 389 insertions(+), 119 deletions(-) diff --git a/sdk/bun.lock b/sdk/bun.lock index 30a8bfe284..f9c0573966 100644 --- a/sdk/bun.lock +++ b/sdk/bun.lock @@ -45,6 +45,7 @@ "eslint-config-prettier": "8.3.0", "eslint-plugin-prettier": "3.4.0", "lodash": "4.17.21", + "madge": "^8.0.0", "mocha": "10.7.3", "object-sizeof": "2.6.5", "prettier": "3.0.1", @@ -62,10 +63,16 @@ "packages": { "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + "@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="], + "@babel/runtime": ["@babel/runtime@7.26.9", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg=="], + "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], + "@coral-xyz/anchor": ["@coral-xyz/anchor@0.29.0", "", { "dependencies": { "@coral-xyz/borsh": "^0.29.0", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA=="], "@coral-xyz/anchor-30": ["@coral-xyz/anchor@0.30.1", "", { "dependencies": { "@coral-xyz/anchor-errors": "^0.30.1", "@coral-xyz/borsh": "^0.30.1", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ=="], @@ -76,6 +83,8 @@ "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], + "@dependents/detective-less": ["@dependents/detective-less@5.0.1", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ=="], + "@ellipsis-labs/phoenix-sdk": ["@ellipsis-labs/phoenix-sdk@1.4.5", "", { "dependencies": { "@metaplex-foundation/beet": "^0.7.1", "@metaplex-foundation/rustbin": "^0.3.1", "@metaplex-foundation/solita": "^0.12.2", "@solana/spl-token": "^0.3.7", "@types/node": "^18.11.13", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^5.0.0" } }, "sha512-vEYgMXuV5/mpnpEi+VK4HO8f6SheHtVLdHHlULBiDN1eECYmL67gq+/cRV7Ar6jAQ7rJZL7xBxhbUW5kugMl6A=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], @@ -212,6 +221,14 @@ "@triton-one/yellowstone-grpc": ["@triton-one/yellowstone-grpc@1.3.0", "", { "dependencies": { "@grpc/grpc-js": "^1.8.0" } }, "sha512-tuwHtoYzvqnahsMrecfNNkQceCYwgiY0qKS8RwqtaxvDEgjm0E+0bXwKz2eUD3ZFYifomJmRKDmSBx9yQzAeMQ=="], + "@ts-graphviz/adapter": ["@ts-graphviz/adapter@2.0.6", "", { "dependencies": { "@ts-graphviz/common": "^2.1.5" } }, "sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q=="], + + "@ts-graphviz/ast": ["@ts-graphviz/ast@2.0.7", "", { "dependencies": { "@ts-graphviz/common": "^2.1.5" } }, "sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw=="], + + "@ts-graphviz/common": ["@ts-graphviz/common@2.1.5", "", {}, "sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg=="], + + "@ts-graphviz/core": ["@ts-graphviz/core@2.0.7", "", { "dependencies": { "@ts-graphviz/ast": "^2.0.7", "@ts-graphviz/common": "^2.1.5" } }, "sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg=="], + "@tsconfig/node10": ["@tsconfig/node10@1.0.11", "", {}, "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="], "@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="], @@ -260,8 +277,12 @@ "@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.38.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.38.0", "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg=="], + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.38.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ=="], + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], "@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], @@ -274,6 +295,16 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.18", "", { "dependencies": { "@babel/parser": "^7.28.0", "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.18", "", { "dependencies": { "@vue/compiler-core": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.18", "", { "dependencies": { "@babel/parser": "^7.28.0", "@vue/compiler-core": "3.5.18", "@vue/compiler-dom": "3.5.18", "@vue/compiler-ssr": "3.5.18", "@vue/shared": "3.5.18", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.18", "", { "dependencies": { "@vue/compiler-dom": "3.5.18", "@vue/shared": "3.5.18" } }, "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g=="], + + "@vue/shared": ["@vue/shared@3.5.18", "", {}, "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA=="], + "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], @@ -296,8 +327,12 @@ "ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="], + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "app-module-path": ["app-module-path@2.2.0", "", {}, "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ=="], + "arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -308,6 +343,8 @@ "assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="], + "ast-module-types": ["ast-module-types@6.0.1", "", {}, "sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA=="], + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], @@ -330,6 +367,8 @@ "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "bn.js": ["bn.js@5.2.1", "", {}, "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="], "borsh": ["borsh@0.7.0", "", { "dependencies": { "bn.js": "^5.2.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" } }, "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA=="], @@ -368,6 +407,10 @@ "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], + + "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], @@ -378,7 +421,9 @@ "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], @@ -398,8 +443,12 @@ "deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="], + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], @@ -408,6 +457,26 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "dependency-tree": ["dependency-tree@11.2.0", "", { "dependencies": { "commander": "^12.1.0", "filing-cabinet": "^5.0.3", "precinct": "^12.2.0", "typescript": "^5.8.3" }, "bin": { "dependency-tree": "bin/cli.js" } }, "sha512-+C1H3mXhcvMCeu5i2Jpg9dc0N29TWTuT6vJD7mHLAfVmAbo9zW8NlkvQ1tYd3PDMab0IRQM0ccoyX68EZtx9xw=="], + + "detective-amd": ["detective-amd@6.0.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "escodegen": "^2.1.0", "get-amd-module-type": "^6.0.1", "node-source-walk": "^7.0.1" }, "bin": { "detective-amd": "bin/cli.js" } }, "sha512-TtyZ3OhwUoEEIhTFoc1C9IyJIud3y+xYkSRjmvCt65+ycQuc3VcBrPRTMWoO/AnuCyOB8T5gky+xf7Igxtjd3g=="], + + "detective-cjs": ["detective-cjs@6.0.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" } }, "sha512-tLTQsWvd2WMcmn/60T2inEJNhJoi7a//PQ7DwRKEj1yEeiQs4mrONgsUtEJKnZmrGWBBmE0kJ1vqOG/NAxwaJw=="], + + "detective-es6": ["detective-es6@5.0.1", "", { "dependencies": { "node-source-walk": "^7.0.1" } }, "sha512-XusTPuewnSUdoxRSx8OOI6xIA/uld/wMQwYsouvFN2LAg7HgP06NF1lHRV3x6BZxyL2Kkoih4ewcq8hcbGtwew=="], + + "detective-postcss": ["detective-postcss@7.0.1", "", { "dependencies": { "is-url": "^1.2.4", "postcss-values-parser": "^6.0.2" }, "peerDependencies": { "postcss": "^8.4.47" } }, "sha512-bEOVpHU9picRZux5XnwGsmCN4+8oZo7vSW0O0/Enq/TO5R2pIAP2279NsszpJR7ocnQt4WXU0+nnh/0JuK4KHQ=="], + + "detective-sass": ["detective-sass@6.0.1", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-jSGPO8QDy7K7pztUmGC6aiHkexBQT4GIH+mBAL9ZyBmnUIOFbkfZnO8wPRRJFP/QP83irObgsZHCoDHZ173tRw=="], + + "detective-scss": ["detective-scss@5.0.1", "", { "dependencies": { "gonzales-pe": "^4.3.0", "node-source-walk": "^7.0.1" } }, "sha512-MAyPYRgS6DCiS6n6AoSBJXLGVOydsr9huwXORUlJ37K3YLyiN0vYHpzs3AdJOgHobBfispokoqrEon9rbmKacg=="], + + "detective-stylus": ["detective-stylus@5.0.1", "", {}, "sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA=="], + + "detective-typescript": ["detective-typescript@14.0.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "^8.23.0", "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" }, "peerDependencies": { "typescript": "^5.4.4" } }, "sha512-pgN43/80MmWVSEi5LUuiVvO/0a9ss5V7fwVfrJ4QzAQRd3cwqU1SfWGXJFcNKUqoD5cS+uIovhw5t/0rSeC5Mw=="], + + "detective-vue2": ["detective-vue2@2.2.0", "", { "dependencies": { "@dependents/detective-less": "^5.0.1", "@vue/compiler-sfc": "^3.5.13", "detective-es6": "^5.0.1", "detective-sass": "^6.0.1", "detective-scss": "^5.0.1", "detective-stylus": "^5.0.1", "detective-typescript": "^14.0.0" }, "peerDependencies": { "typescript": "^5.4.4" } }, "sha512-sVg/t6O2z1zna8a/UIV6xL5KUa2cMTQbdTIIvqNM0NIPswp52fe43Nwmbahzj3ww4D844u/vC2PYfiGLvD3zFA=="], + "diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], "diff-sequences": ["diff-sequences@28.1.1", "", {}, "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw=="], @@ -426,6 +495,10 @@ "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + "enhanced-resolve": ["enhanced-resolve@5.18.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], @@ -442,6 +515,8 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], + "eslint": ["eslint@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.0", "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ=="], "eslint-config-prettier": ["eslint-config-prettier@8.3.0", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew=="], @@ -454,12 +529,16 @@ "espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], @@ -488,6 +567,8 @@ "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "filing-cabinet": ["filing-cabinet@5.0.3", "", { "dependencies": { "app-module-path": "^2.2.0", "commander": "^12.1.0", "enhanced-resolve": "^5.18.0", "module-definition": "^6.0.1", "module-lookup-amd": "^9.0.3", "resolve": "^1.22.10", "resolve-dependency-path": "^4.0.1", "sass-lookup": "^6.1.0", "stylus-lookup": "^6.1.0", "tsconfig-paths": "^4.2.0", "typescript": "^5.7.3" }, "bin": { "filing-cabinet": "bin/cli.js" } }, "sha512-PlPcMwVWg60NQkhvfoxZs4wEHjhlOO/y7OAm4sKM60o1Z9nttRY4mcdQxp/iZ+kg/Vv6Hw1OAaTbYVM9DA9pYg=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], "find": ["find@0.3.0", "", { "dependencies": { "traverse-chain": "~0.1.0" } }, "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw=="], @@ -514,12 +595,16 @@ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "get-amd-module-type": ["get-amd-module-type@6.0.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" } }, "sha512-MtjsmYiCXcYDDrGqtNbeIYdAl85n+5mSv2r3FbzER/YV3ZILw4HNNIw34HuV5pyl0jzs6GFYU1VHVEefhgcNHQ=="], + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], "get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + "get-own-enumerable-property-symbols": ["get-own-enumerable-property-symbols@3.0.2", "", {}, "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g=="], + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], "glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], @@ -530,6 +615,8 @@ "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + "gonzales-pe": ["gonzales-pe@4.3.0", "", { "dependencies": { "minimist": "^1.2.5" }, "bin": { "gonzales": "bin/gonzales.js" } }, "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ=="], + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], @@ -564,12 +651,16 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], @@ -578,20 +669,30 @@ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="], + "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-obj": ["is-obj@1.0.1", "", {}, "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="], + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], "is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="], "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + "is-regexp": ["is-regexp@1.0.0", "", {}, "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA=="], + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], + "is-url": ["is-url@1.2.4", "", {}, "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww=="], + + "is-url-superb": ["is-url-superb@4.0.0", "", {}, "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "isomorphic-ws": ["isomorphic-ws@4.0.1", "", { "peerDependencies": { "ws": "*" } }, "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="], @@ -624,6 +725,8 @@ "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], "just-extend": ["just-extend@6.2.0", "", {}, "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw=="], @@ -652,6 +755,10 @@ "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], + "madge": ["madge@8.0.0", "", { "dependencies": { "chalk": "^4.1.2", "commander": "^7.2.0", "commondir": "^1.0.1", "debug": "^4.3.4", "dependency-tree": "^11.0.0", "ora": "^5.4.1", "pluralize": "^8.0.0", "pretty-ms": "^7.0.1", "rc": "^1.2.8", "stream-to-array": "^2.3.0", "ts-graphviz": "^2.1.2", "walkdir": "^0.4.1" }, "peerDependencies": { "typescript": "^5.4.4" }, "optionalPeers": ["typescript"], "bin": { "madge": "bin/cli.js" } }, "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw=="], + + "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + "make-error": ["make-error@1.3.6", "", {}, "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -664,10 +771,18 @@ "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "mocha": ["mocha@10.7.3", "", { "dependencies": { "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", "chokidar": "^3.5.3", "debug": "^4.3.5", "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^8.1.0", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^5.1.6", "ms": "^2.1.3", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", "yargs": "^16.2.0", "yargs-parser": "^20.2.9", "yargs-unparser": "^2.0.0" }, "bin": { "mocha": "bin/mocha.js", "_mocha": "bin/_mocha" } }, "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A=="], + "module-definition": ["module-definition@6.0.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" }, "bin": { "module-definition": "bin/cli.js" } }, "sha512-FeVc50FTfVVQnolk/WQT8MX+2WVcDnTGiq6Wo+/+lJ2ET1bRVi3HG3YlJUfqagNMc/kUlFSoR96AJkxGpKz13g=="], + + "module-lookup-amd": ["module-lookup-amd@9.0.5", "", { "dependencies": { "commander": "^12.1.0", "glob": "^7.2.3", "requirejs": "^2.3.7", "requirejs-config-file": "^4.0.0" }, "bin": { "lookup-amd": "bin/cli.js" } }, "sha512-Rs5FVpVcBYRHPLuhHOjgbRhosaQYLtEo3JIeDIbmNo7mSssi1CTzwMh8v36gAzpbzLGXI9wB/yHh+5+3fY1QVw=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.4", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="], @@ -684,6 +799,8 @@ "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + "node-source-walk": ["node-source-walk@7.0.1", "", { "dependencies": { "@babel/parser": "^7.26.7" } }, "sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg=="], + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], @@ -696,8 +813,12 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -706,12 +827,16 @@ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + "parse-ms": ["parse-ms@2.1.0", "", {}, "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], @@ -722,8 +847,16 @@ "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-values-parser": ["postcss-values-parser@6.0.2", "", { "dependencies": { "color-name": "^1.1.4", "is-url-superb": "^4.0.0", "quote-unquote": "^1.0.0" }, "peerDependencies": { "postcss": "^8.2.9" } }, "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw=="], + + "precinct": ["precinct@12.2.0", "", { "dependencies": { "@dependents/detective-less": "^5.0.1", "commander": "^12.1.0", "detective-amd": "^6.0.1", "detective-cjs": "^6.0.1", "detective-es6": "^5.0.1", "detective-postcss": "^7.0.1", "detective-sass": "^6.0.1", "detective-scss": "^5.0.1", "detective-stylus": "^5.0.1", "detective-typescript": "^14.0.0", "detective-vue2": "^2.2.0", "module-definition": "^6.0.1", "node-source-walk": "^7.0.1", "postcss": "^8.5.1", "typescript": "^5.7.3" }, "bin": { "precinct": "bin/cli.js" } }, "sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w=="], + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], "prettier": ["prettier@3.0.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ=="], @@ -732,6 +865,8 @@ "pretty-format": ["pretty-format@28.1.3", "", { "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q=="], + "pretty-ms": ["pretty-ms@7.0.1", "", { "dependencies": { "parse-ms": "^2.1.0" } }, "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q=="], + "protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="], "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], @@ -740,18 +875,34 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "quote-unquote": ["quote-unquote@1.0.0", "", {}, "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg=="], + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "requirejs": ["requirejs@2.3.7", "", { "bin": { "r.js": "bin/r.js", "r_js": "bin/r.js" } }, "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw=="], + + "requirejs-config-file": ["requirejs-config-file@4.0.0", "", { "dependencies": { "esprima": "^4.0.0", "stringify-object": "^3.2.1" } }, "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw=="], + + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "resolve-dependency-path": ["resolve-dependency-path@4.0.1", "", {}, "sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ=="], + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], @@ -766,6 +917,8 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sass-lookup": ["sass-lookup@6.1.0", "", { "dependencies": { "commander": "^12.1.0", "enhanced-resolve": "^5.18.0" }, "bin": { "sass-lookup": "bin/cli.js" } }, "sha512-Zx+lVyoWqXZxHuYWlTA17Z5sczJ6braNT2C7rmClw+c4E7r/n911Zwss3h1uHI9reR5AgHZyNHF7c2+VIp5AUA=="], + "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="], @@ -776,6 +929,8 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + "sinon": ["sinon@18.0.1", "", { "dependencies": { "@sinonjs/commons": "^3.0.1", "@sinonjs/fake-timers": "11.2.2", "@sinonjs/samsam": "^8.0.0", "diff": "^5.2.0", "nise": "^6.0.0", "supports-color": "^7" } }, "sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw=="], "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], @@ -794,22 +949,40 @@ "solana-bankrun-linux-x64-musl": ["solana-bankrun-linux-x64-musl@0.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-6r8i0NuXg3CGURql8ISMIUqhE7Hx/O7MlIworK4oN08jYrP0CXdLeB/hywNn7Z8d1NXrox/NpYUgvRm2yIzAsQ=="], + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "spok": ["spok@1.5.5", "", { "dependencies": { "ansicolors": "~0.3.2", "find-process": "^1.4.7" } }, "sha512-IrJIXY54sCNFASyHPOY+jEirkiJ26JDqsGiI0Dvhwcnkl0PEWi1PSsrkYql0rzDw8LFVTcA7rdUCAJdE2HE+2Q=="], "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], + "stream-to-array": ["stream-to-array@2.3.0", "", { "dependencies": { "any-promise": "^1.1.0" } }, "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA=="], + "strict-event-emitter-types": ["strict-event-emitter-types@2.0.0", "", {}, "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "stringify-object": ["stringify-object@3.3.0", "", { "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + "stylus-lookup": ["stylus-lookup@6.1.0", "", { "dependencies": { "commander": "^12.1.0" }, "bin": { "stylus-lookup": "bin/cli.js" } }, "sha512-5QSwgxAzXPMN+yugy61C60PhoANdItfdjSEZR8siFwz7yL9jTmV0UBKDCfn3K8GkGB4g0Y9py7vTCX8rFu4/pQ=="], + "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], + "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], @@ -826,8 +999,12 @@ "ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], + "ts-graphviz": ["ts-graphviz@2.1.6", "", { "dependencies": { "@ts-graphviz/adapter": "^2.0.6", "@ts-graphviz/ast": "^2.0.7", "@ts-graphviz/common": "^2.1.5", "@ts-graphviz/core": "^2.0.7" } }, "sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw=="], + "ts-node": ["ts-node@10.9.2", "", { "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", "@tsconfig/node16": "^1.0.2", "acorn": "^8.4.1", "acorn-walk": "^8.1.1", "arg": "^4.1.0", "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "bin": { "ts-node": "dist/bin.js", "ts-script": "dist/bin-script-deprecated.js", "ts-node-cwd": "dist/bin-cwd.js", "ts-node-esm": "dist/bin-esm.js", "ts-node-script": "dist/bin-script.js", "ts-node-transpile-only": "dist/bin-transpile.js" } }, "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="], + "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="], @@ -850,10 +1027,16 @@ "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], "v8-compile-cache-lib": ["v8-compile-cache-lib@3.0.1", "", {}, "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="], + "walkdir": ["walkdir@0.4.1", "", {}, "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ=="], + + "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], @@ -888,6 +1071,8 @@ "zstddec": ["zstddec@0.1.0", "", {}, "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="], + "@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], + "@coral-xyz/anchor-30/@coral-xyz/borsh": ["@coral-xyz/borsh@0.30.1", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ=="], "@ellipsis-labs/phoenix-sdk/@solana/spl-token": ["@solana/spl-token@0.3.7", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.47.4" } }, "sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg=="], @@ -932,22 +1117,40 @@ "@switchboard-xyz/on-demand/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], + "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.38.0", "", {}, "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "defaults/clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], + + "dependency-tree/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "dependency-tree/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "detective-typescript/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.38.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.38.0", "@typescript-eslint/tsconfig-utils": "8.38.0", "@typescript-eslint/types": "8.38.0", "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "filing-cabinet/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "filing-cabinet/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "find-process/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], "glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + "jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], "jito-ts/dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], @@ -958,14 +1161,30 @@ "mocha/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], + "module-lookup-amd/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "module-lookup-amd/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "nise/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="], + "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "precinct/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "precinct/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "sass-lookup/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "sinon/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + "stylus-lookup/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "ts-node/diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -990,10 +1209,22 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.38.0", "", {}, "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw=="], + + "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.38.0", "", { "dependencies": { "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g=="], + + "detective-typescript/@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "detective-typescript/@typescript-eslint/typescript-estree/ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], + "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "mocha/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "mocha/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + + "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "detective-typescript/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], } } diff --git a/sdk/package.json b/sdk/package.json index b846b55767..16e12e847f 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -22,7 +22,8 @@ "patch-and-pub": "npm version patch --force && npm publish", "prettify": "prettier --check './src/***/*.ts'", "prettify:fix": "prettier --write './{src,tests}/***/*.ts'", - "version": "node ./scripts/updateVersion.js" + "version": "node ./scripts/updateVersion.js", + "circular-deps": "bunx madge --circular --circular --extensions ts src" }, "keywords": [ "drift-labs", @@ -78,6 +79,7 @@ "eslint-config-prettier": "8.3.0", "eslint-plugin-prettier": "3.4.0", "lodash": "4.17.21", + "madge": "^8.0.0", "mocha": "10.7.3", "object-sizeof": "2.6.5", "prettier": "3.0.1", @@ -94,4 +96,4 @@ "@solana/errors": "2.0.0-preview.4", "@solana/codecs-data-structures": "2.0.0-preview.4" } -} +} \ No newline at end of file diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index c99b464690..a4b4425253 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -6,12 +6,14 @@ import { UserAccount, UserStatsAccount, InsuranceFundStake, + HighLeverageModeConfig, } from '../types'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Context, PublicKey } from '@solana/web3.js'; import { Account } from '@solana/spl-token'; -import { HighLeverageModeConfig, OracleInfo, OraclePriceData, User } from '..'; +import { OracleInfo, OraclePriceData } from '../oracles/types'; +import { User } from '../user'; import { ChannelOptions, CommitmentLevel } from '../isomorphic/grpc'; export interface AccountSubscriber { diff --git a/sdk/src/addresses/pda.ts b/sdk/src/addresses/pda.ts index 57fdd015d7..2dd95c417a 100644 --- a/sdk/src/addresses/pda.ts +++ b/sdk/src/addresses/pda.ts @@ -2,7 +2,7 @@ import { PublicKey } from '@solana/web3.js'; import * as anchor from '@coral-xyz/anchor'; import { BN } from '@coral-xyz/anchor'; import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token'; -import { SpotMarketAccount, TokenProgramFlag } from '..'; +import { SpotMarketAccount, TokenProgramFlag } from '../types'; export async function getDriftStateAccountPublicKeyAndNonce( programId: PublicKey diff --git a/sdk/src/clock/clockSubscriber.ts b/sdk/src/clock/clockSubscriber.ts index 626fce8c0f..efc621440d 100644 --- a/sdk/src/clock/clockSubscriber.ts +++ b/sdk/src/clock/clockSubscriber.ts @@ -1,7 +1,7 @@ import { Commitment, Connection, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types/types/src'; -import { BN } from '..'; +import { BN } from '@coral-xyz/anchor'; // eslint-disable-next-line @typescript-eslint/ban-types type ClockSubscriberConfig = { diff --git a/sdk/src/config.ts b/sdk/src/config.ts index eb9f158a8e..0ff967b7ad 100644 --- a/sdk/src/config.ts +++ b/sdk/src/config.ts @@ -1,5 +1,5 @@ -import { ConfirmOptions } from '@solana/web3.js'; -import { PerpMarketAccount, PublicKey, SpotMarketAccount } from '.'; +import { ConfirmOptions, PublicKey } from '@solana/web3.js'; +import { PerpMarketAccount, SpotMarketAccount } from './types'; import { DevnetPerpMarkets, MainnetPerpMarkets, diff --git a/sdk/src/constants/perpMarkets.ts b/sdk/src/constants/perpMarkets.ts index 26cc0dac8a..d23acbe648 100644 --- a/sdk/src/constants/perpMarkets.ts +++ b/sdk/src/constants/perpMarkets.ts @@ -1,6 +1,6 @@ -import { OracleSource } from '../'; -import { DriftEnv } from '../'; import { PublicKey } from '@solana/web3.js'; +import { OracleSource } from '../types'; +import { DriftEnv } from '../config'; export type PerpMarketConfig = { fullName?: string; diff --git a/sdk/src/constants/spotMarkets.ts b/sdk/src/constants/spotMarkets.ts index 133927be60..a732ea2270 100644 --- a/sdk/src/constants/spotMarkets.ts +++ b/sdk/src/constants/spotMarkets.ts @@ -1,5 +1,4 @@ import { PublicKey } from '@solana/web3.js'; -import { BN, DriftEnv, OracleSource } from '../'; import { QUOTE_PRECISION, QUOTE_PRECISION_EXP, @@ -10,6 +9,9 @@ import { NINE, FIVE, } from './numericConstants'; +import { OracleSource } from '../types'; +import { BN } from '@coral-xyz/anchor'; +import { DriftEnv } from '../config'; export type SpotMarketConfig = { symbol: string; diff --git a/sdk/src/decode/user.ts b/sdk/src/decode/user.ts index 24cf15a188..688a773a1f 100644 --- a/sdk/src/decode/user.ts +++ b/sdk/src/decode/user.ts @@ -1,4 +1,6 @@ +import { BN } from '@coral-xyz/anchor'; import { + MarginMode, MarketType, Order, OrderStatus, @@ -11,8 +13,7 @@ import { UserAccount, } from '../types'; import { PublicKey } from '@solana/web3.js'; -import { BN, MarginMode } from '../'; -import { ZERO } from '../'; +import { ZERO } from '../constants/numericConstants'; function readUnsignedBigInt64LE(buffer: Buffer, offset: number): BN { return new BN(buffer.subarray(offset, offset + 8), 10, 'le'); diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 82ee14cab7..64109658c3 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -1,40 +1,40 @@ import { getOrderSignature, NodeList } from './NodeList'; +import { BN } from '@coral-xyz/anchor'; import { BASE_PRECISION, - BN, BN_MAX, - convertToNumber, - decodeName, - DLOBNode, - DLOBNodeType, - DriftClient, + PRICE_PRECISION, + QUOTE_PRECISION, + ZERO, +} from '../constants/numericConstants'; +import { decodeName } from '../userName'; +import { DLOBNode, DLOBNodeType, TriggerOrderNode } from './DLOBNode'; +import { DriftClient } from '../driftClient'; +import { getLimitPrice, - getVariant, - isFallbackAvailableLiquiditySource, - isOneOfVariant, isOrderExpired, isRestingLimitOrder, isTriggered, - isUserProtectedMaker, + mustBeTriggered, +} from '../math/orders'; +import { + getVariant, + isOneOfVariant, isVariant, MarketType, MarketTypeStr, - mustBeTriggered, - OraclePriceData, Order, PerpMarketAccount, PositionDirection, - PRICE_PRECISION, ProtectedMakerParams, - ProtectMakerParamsMap, - QUOTE_PRECISION, - SlotSubscriber, SpotMarketAccount, StateAccount, - TriggerOrderNode, - UserMap, - ZERO, -} from '..'; +} from '../types'; +import { isUserProtectedMaker } from '../math/userStatus'; +import { OraclePriceData } from '../oracles/types'; +import { ProtectMakerParamsMap } from './types'; +import { SlotSubscriber } from '../slot/SlotSubscriber'; +import { UserMap } from '../userMap/userMap'; import { PublicKey } from '@solana/web3.js'; import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus'; import { @@ -46,6 +46,8 @@ import { L3OrderBook, mergeL2LevelGenerators, } from './orderBookLevels'; +import { isFallbackAvailableLiquiditySource } from '../math/auction'; +import { convertToNumber } from '../math/conversion'; export type DLOBOrder = { user: PublicKey; order: Order }; export type DLOBOrders = DLOBOrder[]; diff --git a/sdk/src/dlob/DLOBNode.ts b/sdk/src/dlob/DLOBNode.ts index 4975a54ab9..4b7717e7ce 100644 --- a/sdk/src/dlob/DLOBNode.ts +++ b/sdk/src/dlob/DLOBNode.ts @@ -1,15 +1,13 @@ +import { BN } from '@coral-xyz/anchor'; import { AMM_RESERVE_PRECISION, - BN, - convertToNumber, - getLimitPrice, - isVariant, PRICE_PRECISION, - OraclePriceData, - Order, ZERO, - ProtectedMakerParams, -} from '..'; +} from '../constants/numericConstants'; +import { getLimitPrice } from '../math/orders'; +import { isVariant, Order, ProtectedMakerParams } from '../types'; +import { OraclePriceData } from '../oracles/types'; +import { convertToNumber } from '../math/conversion'; import { getOrderSignature } from './NodeList'; export interface DLOBNode { diff --git a/sdk/src/dlob/NodeList.ts b/sdk/src/dlob/NodeList.ts index 479d4ca776..ddb483f14a 100644 --- a/sdk/src/dlob/NodeList.ts +++ b/sdk/src/dlob/NodeList.ts @@ -1,4 +1,9 @@ -import { isVariant, MarketTypeStr, Order, ProtectedMakerParams } from '..'; +import { + isVariant, + MarketTypeStr, + Order, + ProtectedMakerParams, +} from '../types'; import { createNode, DLOBNode, DLOBNodeMap } from './DLOBNode'; export type SortDirection = 'asc' | 'desc'; diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index 56500b4030..ac0fc5a86d 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -1,27 +1,30 @@ +import { BN } from '@coral-xyz/anchor'; import { BASE_PRECISION, - BN, + QUOTE_PRECISION, + ZERO, + PRICE_PRECISION, + AMM_TO_QUOTE_PRECISION_RATIO, +} from '../constants/numericConstants'; +import { calculateAmmReservesAfterSwap, calculateMarketOpenBidAsk, calculateQuoteAssetAmountSwapped, calculateSpreadReserves, calculateUpdatedAMM, - DLOBNode, - isOperationPaused, +} from '../math/amm'; +import { DLOBNode } from './DLOBNode'; +import { isOperationPaused } from '../math/exchangeStatus'; +import { isVariant, - OraclePriceData, PerpMarketAccount, PerpOperation, PositionDirection, - QUOTE_PRECISION, - standardizePrice, SwapDirection, - ZERO, - PRICE_PRECISION, - AMM_TO_QUOTE_PRECISION_RATIO, - standardizeBaseAssetAmount, -} from '..'; +} from '../types'; +import { OraclePriceData } from '../oracles/types'; import { PublicKey } from '@solana/web3.js'; +import { standardizeBaseAssetAmount, standardizePrice } from '../math/orders'; type liquiditySource = | 'serum' diff --git a/sdk/src/indicative-quotes/indicativeQuotesSender.ts b/sdk/src/indicative-quotes/indicativeQuotesSender.ts index 9d54e96052..f43d11758a 100644 --- a/sdk/src/indicative-quotes/indicativeQuotesSender.ts +++ b/sdk/src/indicative-quotes/indicativeQuotesSender.ts @@ -1,5 +1,5 @@ import { Keypair } from '@solana/web3.js'; -import { BN } from '..'; +import { BN } from '@coral-xyz/anchor'; import nacl from 'tweetnacl'; import { decodeUTF8 } from 'tweetnacl-util'; import WebSocket from 'ws'; diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index c4f28a4cd5..855a856239 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -23,7 +23,8 @@ import { isVariant, } from '../types'; import { assert } from '../assert/assert'; -import { squareRootBN, sigNum, clampBN, standardizeBaseAssetAmount } from '..'; +import { squareRootBN, sigNum, clampBN } from './utils'; +import { standardizeBaseAssetAmount } from './orders'; import { OraclePriceData } from '../oracles/types'; import { diff --git a/sdk/src/math/auction.ts b/sdk/src/math/auction.ts index bb74163dd7..d0daf315be 100644 --- a/sdk/src/math/auction.ts +++ b/sdk/src/math/auction.ts @@ -1,15 +1,13 @@ import { isOneOfVariant, isVariant, Order, PositionDirection } from '../types'; +import { BN } from '@coral-xyz/anchor'; import { - BN, - getVariant, ONE, - OrderBitFlag, ZERO, - PerpMarketAccount, - getPerpMarketTierNumber, QUOTE_PRECISION, PRICE_PRECISION, -} from '../.'; +} from '../constants/numericConstants'; +import { getVariant, OrderBitFlag, PerpMarketAccount } from '../types'; +import { getPerpMarketTierNumber } from './tiers'; export function isAuctionComplete(order: Order, slot: number): boolean { if (order.auctionDuration === 0) { diff --git a/sdk/src/math/bankruptcy.ts b/sdk/src/math/bankruptcy.ts index 23054ccdaa..22cca5813b 100644 --- a/sdk/src/math/bankruptcy.ts +++ b/sdk/src/math/bankruptcy.ts @@ -1,4 +1,6 @@ -import { ZERO, hasOpenOrders, isVariant } from '..'; +import { ZERO } from '../constants/numericConstants'; +import { hasOpenOrders } from './position'; +import { isVariant } from '../types'; import { User } from '../user'; export function isUserBankrupt(user: User): boolean { diff --git a/sdk/src/math/conversion.ts b/sdk/src/math/conversion.ts index 1cfc43de74..f24e81a5da 100644 --- a/sdk/src/math/conversion.ts +++ b/sdk/src/math/conversion.ts @@ -1,4 +1,4 @@ -import { BN } from '../'; +import { BN } from '@coral-xyz/anchor'; import { PRICE_PRECISION } from '../constants/numericConstants'; export const convertToNumber = ( diff --git a/sdk/src/math/fuel.ts b/sdk/src/math/fuel.ts index 89eefd2600..ecce0927d3 100644 --- a/sdk/src/math/fuel.ts +++ b/sdk/src/math/fuel.ts @@ -1,5 +1,5 @@ import { BN } from '@coral-xyz/anchor'; -import { SpotMarketAccount, PerpMarketAccount } from '..'; +import { SpotMarketAccount, PerpMarketAccount } from '../types'; import { QUOTE_PRECISION, ZERO, diff --git a/sdk/src/math/margin.ts b/sdk/src/math/margin.ts index 451685ecb8..cba701015e 100644 --- a/sdk/src/math/margin.ts +++ b/sdk/src/math/margin.ts @@ -13,18 +13,18 @@ import { } from '../constants/numericConstants'; import { BN } from '@coral-xyz/anchor'; import { OraclePriceData } from '../oracles/types'; +import { calculateMarketMarginRatio } from './market'; +import { calculateScaledInitialAssetWeight } from './spotBalance'; +import { DriftClient } from '../driftClient'; +import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber'; import { - calculateMarketMarginRatio, - calculateScaledInitialAssetWeight, - DriftClient, - OneShotUserAccountSubscriber, PerpMarketAccount, PerpPosition, PositionDirection, - PublicKey, - User, UserAccount, -} from '..'; +} from '../types'; +import { PublicKey } from '@solana/web3.js'; +import { User } from '../user'; import { isVariant } from '../types'; import { assert } from '../assert/assert'; diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index c4675facdc..52b3a888bd 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -1,4 +1,4 @@ -import { BN, SpotMarketAccount } from '../'; +import { BN } from '@coral-xyz/anchor'; import { AMM_RESERVE_PRECISION, AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO, @@ -9,7 +9,12 @@ import { ZERO, } from '../constants/numericConstants'; import { OraclePriceData } from '../oracles/types'; -import { PerpMarketAccount, PositionDirection, PerpPosition } from '../types'; +import { + PerpMarketAccount, + PositionDirection, + PerpPosition, + SpotMarketAccount, +} from '../types'; import { calculateUpdatedAMM, calculateUpdatedAMMSpreadReserves, diff --git a/sdk/src/math/protectedMakerParams.ts b/sdk/src/math/protectedMakerParams.ts index b40f356e59..d88f7dab5c 100644 --- a/sdk/src/math/protectedMakerParams.ts +++ b/sdk/src/math/protectedMakerParams.ts @@ -1,4 +1,5 @@ -import { BN, ProtectMakerParamsMap } from '..'; +import { BN } from '@coral-xyz/anchor'; +import { ProtectMakerParamsMap } from '../dlob/types'; import { PerpMarketAccount, ProtectedMakerParams } from '../types'; export function getProtectedMakerParams( diff --git a/sdk/src/math/state.ts b/sdk/src/math/state.ts index 5cce8b0ba2..f4c52cac1c 100644 --- a/sdk/src/math/state.ts +++ b/sdk/src/math/state.ts @@ -1,5 +1,10 @@ +import { BN } from '@coral-xyz/anchor'; +import { + LAMPORTS_PRECISION, + PERCENTAGE_PRECISION, + ZERO, +} from '../constants/numericConstants'; import { StateAccount } from '../types'; -import { BN, LAMPORTS_PRECISION, PERCENTAGE_PRECISION, ZERO } from '../'; export function calculateInitUserFee(stateAccount: StateAccount): BN { const maxInitFee = new BN(stateAccount.maxInitializeUserFee) diff --git a/sdk/src/math/userStatus.ts b/sdk/src/math/userStatus.ts index 9a957e0a38..2021e70ba8 100644 --- a/sdk/src/math/userStatus.ts +++ b/sdk/src/math/userStatus.ts @@ -1,4 +1,4 @@ -import { UserAccount, UserStatus } from '..'; +import { UserAccount, UserStatus } from '../types'; export function isUserProtectedMaker(userAccount: UserAccount): boolean { return (userAccount.status & UserStatus.PROTECTED_MAKER) > 0; diff --git a/sdk/src/math/utils.ts b/sdk/src/math/utils.ts index 6b8f31dd21..fcebbdb78d 100644 --- a/sdk/src/math/utils.ts +++ b/sdk/src/math/utils.ts @@ -1,4 +1,5 @@ -import { BN, ONE, ZERO } from '../'; +import { BN } from '@coral-xyz/anchor'; +import { ONE, ZERO } from '../constants/numericConstants'; export function clampBN(x: BN, min: BN, max: BN): BN { return BN.max(min, BN.min(x, max)); diff --git a/sdk/src/oracles/pythLazerClient.ts b/sdk/src/oracles/pythLazerClient.ts index edc22ddccd..d13ba5fa1c 100644 --- a/sdk/src/oracles/pythLazerClient.ts +++ b/sdk/src/oracles/pythLazerClient.ts @@ -7,7 +7,8 @@ import { QUOTE_PRECISION, TEN, } from '../constants/numericConstants'; -import { DRIFT_PROGRAM_ID, Wallet } from '..'; +import { DRIFT_PROGRAM_ID } from '../config'; +import { Wallet } from '../wallet'; import driftIDL from '../idl/drift.json'; export class PythLazerClient implements OracleClient { diff --git a/sdk/src/oracles/pythPullClient.ts b/sdk/src/oracles/pythPullClient.ts index 8e00d5a04f..90048dc60a 100644 --- a/sdk/src/oracles/pythPullClient.ts +++ b/sdk/src/oracles/pythPullClient.ts @@ -12,7 +12,8 @@ import { pythSolanaReceiverIdl, } from '@pythnetwork/pyth-solana-receiver'; import { PriceUpdateAccount } from '@pythnetwork/pyth-solana-receiver/lib/PythSolanaReceiver'; -import { DRIFT_ORACLE_RECEIVER_ID, Wallet } from '..'; +import { DRIFT_ORACLE_RECEIVER_ID } from '../config'; +import { Wallet } from '../wallet'; export class PythPullClient implements OracleClient { private connection: Connection; diff --git a/sdk/src/swift/swiftOrderSubscriber.ts b/sdk/src/swift/swiftOrderSubscriber.ts index 49ffc606f6..bab3032795 100644 --- a/sdk/src/swift/swiftOrderSubscriber.ts +++ b/sdk/src/swift/swiftOrderSubscriber.ts @@ -1,17 +1,21 @@ import { DevnetPerpMarkets, - DriftClient, - DriftEnv, + MainnetPerpMarkets, +} from '../constants/perpMarkets'; +import { DriftClient } from '../driftClient'; +import { DriftEnv } from '../config'; +import { getUserAccountPublicKey, getUserStatsAccountPublicKey, - MainnetPerpMarkets, +} from '../addresses/pda'; +import { MarketType, OptionalOrderParams, PostOnlyParams, SignedMsgOrderParamsDelegateMessage, SignedMsgOrderParamsMessage, UserAccount, -} from '..'; +} from '../types'; import { Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js'; import nacl from 'tweetnacl'; import { decodeUTF8 } from 'tweetnacl-util'; diff --git a/sdk/src/tokenFaucet.ts b/sdk/src/tokenFaucet.ts index ad065800e4..f110283143 100644 --- a/sdk/src/tokenFaucet.ts +++ b/sdk/src/tokenFaucet.ts @@ -16,10 +16,11 @@ import { TransactionInstruction, TransactionSignature, } from '@solana/web3.js'; -import { BN, DEFAULT_CONFIRMATION_OPTS } from '.'; import tokenFaucet from './idl/token_faucet.json'; import { IWallet } from './types'; import { BankrunContextWrapper } from './bankrun/bankrunConnection'; +import { DEFAULT_CONFIRMATION_OPTS } from './config'; +import { BN } from '@coral-xyz/anchor'; export class TokenFaucet { context?: BankrunContextWrapper; diff --git a/sdk/src/tx/txParamProcessor.ts b/sdk/src/tx/txParamProcessor.ts index 89337a365e..897b8ceeed 100644 --- a/sdk/src/tx/txParamProcessor.ts +++ b/sdk/src/tx/txParamProcessor.ts @@ -4,7 +4,7 @@ import { SimulatedTransactionResponse, VersionedTransaction, } from '@solana/web3.js'; -import { BaseTxParams, ProcessingTxParams } from '..'; +import { BaseTxParams, ProcessingTxParams } from '../types'; const COMPUTE_UNIT_BUFFER_FACTOR = 1.2; const MAX_COMPUTE_UNITS = 1_400_000; diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 9697181936..e58f9467d0 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -5,7 +5,8 @@ import { TransactionVersion, VersionedTransaction, } from '@solana/web3.js'; -import { BN, ZERO } from '.'; +import { BN } from '@coral-xyz/anchor'; +import { ZERO } from './constants/numericConstants'; // Utility type which lets you denote record with values of type A mapped to a record with the same keys but values of type B export type MappedRecord, B> = { diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 456cf50b38..215d0450fd 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -46,31 +46,35 @@ import { UserAccountEvents, UserAccountSubscriber, } from './accounts/types'; +import { BigNum } from './factory/bigNum'; +import { BN } from '@coral-xyz/anchor'; +import { calculateBaseAssetValue, calculatePositionPNL } from './math/position'; import { - BigNum, - BN, - calculateBaseAssetValue, calculateMarketMarginRatio, - calculatePerpLiabilityValue, - calculatePositionPNL, calculateReservePrice, - calculateSpotMarketMarginRatio, calculateUnrealizedAssetWeight, +} from './math/market'; +import { + calculatePerpLiabilityValue, calculateWorstCasePerpLiabilityValue, - divCeil, +} from './math/margin'; +import { calculateSpotMarketMarginRatio } from './math/spotMarket'; +import { divCeil, sigNum } from './math/utils'; +import { getBalance, getSignedTokenAmount, getStrictTokenValue, getTokenValue, - getUser30dRollingVolumeEstimate, +} from './math/spotBalance'; +import { getUser30dRollingVolumeEstimate } from './math/trade'; +import { MarketType, PositionDirection, - sigNum, SpotBalanceType, SpotMarketAccount, - standardizeBaseAssetAmount, - UserStats, -} from '.'; +} from './types'; +import { standardizeBaseAssetAmount } from './math/orders'; +import { UserStats } from './userStats'; import { calculateAssetWeight, calculateLiabilityWeight, diff --git a/sdk/src/userMap/userMap.ts b/sdk/src/userMap/userMap.ts index b751952178..f022be058a 100644 --- a/sdk/src/userMap/userMap.ts +++ b/sdk/src/userMap/userMap.ts @@ -1,9 +1,9 @@ +import { BN } from '@coral-xyz/anchor'; +import { User } from '../user'; +import { DriftClient } from '../driftClient'; import { - User, - DriftClient, UserAccount, OrderRecord, - WrappedEvent, DepositRecord, FundingPaymentRecord, LiquidationRecord, @@ -12,14 +12,13 @@ import { NewUserRecord, LPRecord, StateAccount, - DLOB, - BN, - UserSubscriptionConfig, - DataAndSlot, - OneShotUserAccountSubscriber, - ProtectMakerParamsMap, - UserEvents, -} from '..'; +} from '../types'; +import { WrappedEvent } from '../events/types'; +import { DLOB } from '../dlob/DLOB'; +import { UserSubscriptionConfig } from '../userConfig'; +import { DataAndSlot, UserEvents } from '../accounts/types'; +import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber'; +import { ProtectMakerParamsMap } from '../dlob/types'; import { Commitment, diff --git a/sdk/src/userMap/userStatsMap.ts b/sdk/src/userMap/userStatsMap.ts index e5211af16a..8e9abee863 100644 --- a/sdk/src/userMap/userStatsMap.ts +++ b/sdk/src/userMap/userStatsMap.ts @@ -1,10 +1,8 @@ +import { DriftClient } from '../driftClient'; +import { getUserStatsAccountPublicKey } from '../addresses/pda'; import { - DriftClient, - getUserStatsAccountPublicKey, OrderRecord, UserStatsAccount, - UserStats, - WrappedEvent, DepositRecord, FundingPaymentRecord, LiquidationRecord, @@ -13,11 +11,13 @@ import { NewUserRecord, LPRecord, InsuranceFundStakeRecord, - BulkAccountLoader, - PollingUserStatsAccountSubscriber, - SyncConfig, - getUserStatsFilter, -} from '..'; +} from '../types'; +import { UserStats } from '../userStats'; +import { WrappedEvent } from '../events/types'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; +import { PollingUserStatsAccountSubscriber } from '../accounts/pollingUserStatsAccountSubscriber'; +import { SyncConfig } from './userMapConfig'; +import { getUserStatsFilter } from '../memcmp'; import { PublicKey } from '@solana/web3.js'; import { UserMap } from './userMap'; diff --git a/sdk/src/util/TransactionConfirmationManager.ts b/sdk/src/util/TransactionConfirmationManager.ts index 04ff9520d2..cac9c8cfec 100644 --- a/sdk/src/util/TransactionConfirmationManager.ts +++ b/sdk/src/util/TransactionConfirmationManager.ts @@ -7,7 +7,7 @@ import { TransactionConfirmationStatus, } from '@solana/web3.js'; import { DEFAULT_CONFIRMATION_OPTS } from '../config'; -import { TxSendError } from '..'; +import { TxSendError } from '../tx/types'; import { NOT_CONFIRMED_ERROR_CODE } from '../constants/txConstants'; import { getTransactionErrorFromTxSig, From f4a71fa9b6dab658902d18f08a4a728abd262d14 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 17:13:34 +0000 Subject: [PATCH 014/216] sdk: release v2.130.0-beta.7 --- sdk/VERSION | 2 +- sdk/package.json | 4 +- sdk/yarn.lock | 773 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 766 insertions(+), 13 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 64f81b3730..065f94c56a 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.6 \ No newline at end of file +2.130.0-beta.7 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 16e12e847f..2f563f7686 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.6", + "version": "2.130.0-beta.7", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", @@ -96,4 +96,4 @@ "@solana/errors": "2.0.0-preview.4", "@solana/codecs-data-structures": "2.0.0-preview.4" } -} \ No newline at end of file +} diff --git a/sdk/yarn.lock b/sdk/yarn.lock index 30de8f1320..bb142bcf3d 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -11,16 +11,36 @@ js-tokens "^4.0.0" picocolors "^1.1.1" +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + "@babel/helper-validator-identifier@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/parser@^7.26.7", "@babel/parser@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e" + integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g== + dependencies: + "@babel/types" "^7.28.0" + "@babel/runtime@^7.10.5", "@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== +"@babel/types@^7.28.0": + version "7.28.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" + integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@coral-xyz/anchor-30@npm:@coral-xyz/anchor@0.30.1": version "0.30.1" resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.30.1.tgz#17f3e9134c28cd0ea83574c6bab4e410bcecec5d" @@ -90,6 +110,14 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@dependents/detective-less@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@dependents/detective-less/-/detective-less-5.0.1.tgz#e6c5b502f0d26a81da4170c1ccd848a6eaa68470" + integrity sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^7.0.1" + "@ellipsis-labs/phoenix-sdk@1.4.5": version "1.4.5" resolved "https://registry.yarnpkg.com/@ellipsis-labs/phoenix-sdk/-/phoenix-sdk-1.4.5.tgz#42cf8de8463b32c910ab8844eae71ca082a6773a" @@ -217,7 +245,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.4" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz#7358043433b2e5da569aa02cbc4c121da3af27d7" integrity sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw== @@ -738,6 +766,33 @@ dependencies: "@grpc/grpc-js" "^1.8.0" +"@ts-graphviz/adapter@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@ts-graphviz/adapter/-/adapter-2.0.6.tgz#18d5a42304dca7ffff760fcaf311a3148ef4a3bd" + integrity sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q== + dependencies: + "@ts-graphviz/common" "^2.1.5" + +"@ts-graphviz/ast@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@ts-graphviz/ast/-/ast-2.0.7.tgz#4ec33492e4b4e998d4632030e97a9f7e149afb86" + integrity sha512-e6+2qtNV99UT6DJSoLbHfkzfyqY84aIuoV8Xlb9+hZAjgpum8iVHprGeAMQ4rF6sKUAxrmY8rfF/vgAwoPc3gw== + dependencies: + "@ts-graphviz/common" "^2.1.5" + +"@ts-graphviz/common@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@ts-graphviz/common/-/common-2.1.5.tgz#a256dfaea009a5b147d8f73f25e57fb44f6462a2" + integrity sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg== + +"@ts-graphviz/core@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@ts-graphviz/core/-/core-2.0.7.tgz#2185e390990038b267a2341c3db1cef3680bbee8" + integrity sha512-w071DSzP94YfN6XiWhOxnLpYT3uqtxJBDYdh6Jdjzt+Ce6DNspJsPQgpC7rbts/B8tEkq0LHoYuIF/O5Jh5rPg== + dependencies: + "@ts-graphviz/ast" "^2.0.7" + "@ts-graphviz/common" "^2.1.5" + "@tsconfig/node10@^1.0.7": version "1.0.11" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" @@ -915,6 +970,15 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" +"@typescript-eslint/project-service@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.38.0.tgz#4900771f943163027fd7d2020a062892056b5e2f" + integrity sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.38.0" + "@typescript-eslint/types" "^8.38.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" @@ -923,6 +987,11 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" +"@typescript-eslint/tsconfig-utils@8.38.0", "@typescript-eslint/tsconfig-utils@^8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz#6de4ce224a779601a8df667db56527255c42c4d0" + integrity sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ== + "@typescript-eslint/type-utils@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" @@ -938,6 +1007,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== +"@typescript-eslint/types@8.38.0", "@typescript-eslint/types@^8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.38.0.tgz#297351c994976b93c82ac0f0e206c8143aa82529" + integrity sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw== + "@typescript-eslint/typescript-estree@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" @@ -952,6 +1026,22 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/typescript-estree@^8.23.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz#82262199eb6778bba28a319e25ad05b1158957df" + integrity sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ== + dependencies: + "@typescript-eslint/project-service" "8.38.0" + "@typescript-eslint/tsconfig-utils" "8.38.0" + "@typescript-eslint/types" "8.38.0" + "@typescript-eslint/visitor-keys" "8.38.0" + debug "^4.3.4" + fast-glob "^3.3.2" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.1.0" + "@typescript-eslint/utils@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" @@ -973,11 +1063,66 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" +"@typescript-eslint/visitor-keys@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz#a9765a527b082cb8fc60fd8a16e47c7ad5b60ea5" + integrity sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g== + dependencies: + "@typescript-eslint/types" "8.38.0" + eslint-visitor-keys "^4.2.1" + "@ungap/structured-clone@^1.2.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== +"@vue/compiler-core@3.5.18": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.18.tgz#521a138cdd970d9bfd27e42168d12f77a04b2074" + integrity sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw== + dependencies: + "@babel/parser" "^7.28.0" + "@vue/shared" "3.5.18" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + +"@vue/compiler-dom@3.5.18": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz#e13504492c3061ec5bbe6a2e789f15261d4f03a7" + integrity sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A== + dependencies: + "@vue/compiler-core" "3.5.18" + "@vue/shared" "3.5.18" + +"@vue/compiler-sfc@^3.5.13": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz#ba1e849561337d809937994cdaf900539542eeca" + integrity sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA== + dependencies: + "@babel/parser" "^7.28.0" + "@vue/compiler-core" "3.5.18" + "@vue/compiler-dom" "3.5.18" + "@vue/compiler-ssr" "3.5.18" + "@vue/shared" "3.5.18" + estree-walker "^2.0.2" + magic-string "^0.30.17" + postcss "^8.5.6" + source-map-js "^1.2.1" + +"@vue/compiler-ssr@3.5.18": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz#aecde0b0bff268a9c9014ba66799307c4a784328" + integrity sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g== + dependencies: + "@vue/compiler-dom" "3.5.18" + "@vue/shared" "3.5.18" + +"@vue/shared@3.5.18": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.18.tgz#529f24a88d3ed678d50fd5c07455841fbe8ac95e" + integrity sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA== + "@zod/core@0.11.6": version "0.11.6" resolved "https://registry.yarnpkg.com/@zod/core/-/core-0.11.6.tgz#9216e98848dc9364eda35e3da90f5362f10e8887" @@ -1049,6 +1194,11 @@ ansicolors@^0.3.2, ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -1057,6 +1207,11 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +app-module-path@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" + integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -1088,6 +1243,11 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +ast-module-types@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-6.0.1.tgz#4b4ca0251c57b815bab62604dcb22f8c903e2523" + integrity sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1165,6 +1325,15 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + bn.js@^5.1.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.2" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566" @@ -1240,6 +1409,14 @@ buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.3, buffer@~6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + bufferutil@^4.0.1: version "4.0.9" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.9.tgz#6e81739ad48a95cad45a279588e13e95e24a800a" @@ -1301,7 +1478,7 @@ chai@4.5.0: pathval "^1.1.1" type-detect "^4.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@~4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2, chalk@~4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1341,6 +1518,18 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1364,6 +1553,11 @@ clone@2.x: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w== +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -1371,7 +1565,7 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@~1.1.4: +color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -1393,6 +1587,16 @@ commander@^2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1448,11 +1652,23 @@ deep-eql@^4.1.3: dependencies: type-detect "^4.0.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -1481,6 +1697,92 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +dependency-tree@^11.0.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-11.2.0.tgz#ae764155b2903267181def4b20be49b1fd76da5e" + integrity sha512-+C1H3mXhcvMCeu5i2Jpg9dc0N29TWTuT6vJD7mHLAfVmAbo9zW8NlkvQ1tYd3PDMab0IRQM0ccoyX68EZtx9xw== + dependencies: + commander "^12.1.0" + filing-cabinet "^5.0.3" + precinct "^12.2.0" + typescript "^5.8.3" + +detective-amd@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-6.0.1.tgz#71eb13b5d9b17222d7b4de3fb89a8e684d8b9a23" + integrity sha512-TtyZ3OhwUoEEIhTFoc1C9IyJIud3y+xYkSRjmvCt65+ycQuc3VcBrPRTMWoO/AnuCyOB8T5gky+xf7Igxtjd3g== + dependencies: + ast-module-types "^6.0.1" + escodegen "^2.1.0" + get-amd-module-type "^6.0.1" + node-source-walk "^7.0.1" + +detective-cjs@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-6.0.1.tgz#4fb81a67337630811409abb2148b2b622cacbdcd" + integrity sha512-tLTQsWvd2WMcmn/60T2inEJNhJoi7a//PQ7DwRKEj1yEeiQs4mrONgsUtEJKnZmrGWBBmE0kJ1vqOG/NAxwaJw== + dependencies: + ast-module-types "^6.0.1" + node-source-walk "^7.0.1" + +detective-es6@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-5.0.1.tgz#f0c026bc9b767a243e57ef282f4343fcf3b8ec4e" + integrity sha512-XusTPuewnSUdoxRSx8OOI6xIA/uld/wMQwYsouvFN2LAg7HgP06NF1lHRV3x6BZxyL2Kkoih4ewcq8hcbGtwew== + dependencies: + node-source-walk "^7.0.1" + +detective-postcss@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-7.0.1.tgz#f5822d8988339fb56851fcdb079d51fbcff114db" + integrity sha512-bEOVpHU9picRZux5XnwGsmCN4+8oZo7vSW0O0/Enq/TO5R2pIAP2279NsszpJR7ocnQt4WXU0+nnh/0JuK4KHQ== + dependencies: + is-url "^1.2.4" + postcss-values-parser "^6.0.2" + +detective-sass@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-6.0.1.tgz#fcf5aa51bebf7b721807be418418470ee2409f8a" + integrity sha512-jSGPO8QDy7K7pztUmGC6aiHkexBQT4GIH+mBAL9ZyBmnUIOFbkfZnO8wPRRJFP/QP83irObgsZHCoDHZ173tRw== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^7.0.1" + +detective-scss@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-5.0.1.tgz#6a7f792dc9c0e8cfc0d252a50ba26a6df12596a7" + integrity sha512-MAyPYRgS6DCiS6n6AoSBJXLGVOydsr9huwXORUlJ37K3YLyiN0vYHpzs3AdJOgHobBfispokoqrEon9rbmKacg== + dependencies: + gonzales-pe "^4.3.0" + node-source-walk "^7.0.1" + +detective-stylus@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-5.0.1.tgz#57d54a0b405305ee16655e42008b38a827a9f179" + integrity sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA== + +detective-typescript@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-14.0.0.tgz#3cf429652eb7d7d2be2c050ac47af957a559527d" + integrity sha512-pgN43/80MmWVSEi5LUuiVvO/0a9ss5V7fwVfrJ4QzAQRd3cwqU1SfWGXJFcNKUqoD5cS+uIovhw5t/0rSeC5Mw== + dependencies: + "@typescript-eslint/typescript-estree" "^8.23.0" + ast-module-types "^6.0.1" + node-source-walk "^7.0.1" + +detective-vue2@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/detective-vue2/-/detective-vue2-2.2.0.tgz#35fd1d39e261b064aca9fcaf20e136c76877482a" + integrity sha512-sVg/t6O2z1zna8a/UIV6xL5KUa2cMTQbdTIIvqNM0NIPswp52fe43Nwmbahzj3ww4D844u/vC2PYfiGLvD3zFA== + dependencies: + "@dependents/detective-less" "^5.0.1" + "@vue/compiler-sfc" "^3.5.13" + detective-es6 "^5.0.1" + detective-sass "^6.0.1" + detective-scss "^5.0.1" + detective-stylus "^5.0.1" + detective-typescript "^14.0.0" + diff-sequences@^28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" @@ -1549,6 +1851,19 @@ encoding@0.1.13: dependencies: iconv-lite "^0.6.2" +enhanced-resolve@^5.18.0: + version "5.18.2" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz#7903c5b32ffd4b2143eeb4b92472bd68effd5464" + integrity sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" @@ -1603,6 +1918,17 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-config-prettier@8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" @@ -1628,6 +1954,11 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== + eslint@8.57.0: version "8.57.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" @@ -1681,6 +2012,11 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2: version "1.6.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" @@ -1700,6 +2036,11 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -1741,7 +2082,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9: +fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -1786,6 +2127,23 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +filing-cabinet@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-5.0.3.tgz#e5ab960958653ee7fe70d5d99b3b88c342ce7907" + integrity sha512-PlPcMwVWg60NQkhvfoxZs4wEHjhlOO/y7OAm4sKM60o1Z9nttRY4mcdQxp/iZ+kg/Vv6Hw1OAaTbYVM9DA9pYg== + dependencies: + app-module-path "^2.2.0" + commander "^12.1.0" + enhanced-resolve "^5.18.0" + module-definition "^6.0.1" + module-lookup-amd "^9.0.3" + resolve "^1.22.10" + resolve-dependency-path "^4.0.1" + sass-lookup "^6.1.0" + stylus-lookup "^6.1.0" + tsconfig-paths "^4.2.0" + typescript "^5.7.3" + fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -1874,6 +2232,14 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +get-amd-module-type@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-6.0.1.tgz#191f479ae8706c246b52bf402fbe1bb0965d9f1e" + integrity sha512-MtjsmYiCXcYDDrGqtNbeIYdAl85n+5mSv2r3FbzER/YV3ZILw4HNNIw34HuV5pyl0jzs6GFYU1VHVEefhgcNHQ== + dependencies: + ast-module-types "^6.0.1" + node-source-walk "^7.0.1" + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -1900,6 +2266,11 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: hasown "^2.0.2" math-intrinsics "^1.1.0" +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + get-proto@^1.0.0, get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" @@ -1922,7 +2293,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^7.1.3: +glob@^7.1.3, glob@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1964,12 +2335,19 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gonzales-pe@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" + integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== + dependencies: + minimist "^1.2.5" + gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.2.9: +graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -2029,7 +2407,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -2060,11 +2438,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + is-arguments@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.2.0.tgz#ad58c6aecf563b78ef2bf04df540da8f5d7d8e1b" @@ -2085,6 +2468,13 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -2112,6 +2502,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-nan@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" @@ -2125,6 +2520,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -2145,6 +2545,11 @@ is-regex@^1.2.1: has-tostringtag "^1.0.2" hasown "^2.0.2" +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + is-typed-array@^1.1.3: version "1.1.15" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" @@ -2157,6 +2562,16 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-url-superb@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" + integrity sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA== + +is-url@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2293,6 +2708,11 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + just-extend@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947" @@ -2372,6 +2792,31 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +madge@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/madge/-/madge-8.0.0.tgz#cca4ab66fb388e7b6bf43c1f78dcaab3cad30f50" + integrity sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw== + dependencies: + chalk "^4.1.2" + commander "^7.2.0" + commondir "^1.0.1" + debug "^4.3.4" + dependency-tree "^11.0.0" + ora "^5.4.1" + pluralize "^8.0.0" + pretty-ms "^7.0.1" + rc "^1.2.8" + stream-to-array "^2.3.0" + ts-graphviz "^2.1.2" + walkdir "^0.4.1" + +magic-string@^0.30.17: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -2407,6 +2852,11 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimatch@9.0.3: version "9.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" @@ -2428,6 +2878,18 @@ minimatch@^5.0.1, minimatch@^5.1.6: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + mocha@10.7.3: version "10.7.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" @@ -2454,6 +2916,24 @@ mocha@10.7.3: yargs-parser "^20.2.9" yargs-unparser "^2.0.0" +module-definition@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-6.0.1.tgz#47e73144cc5a9aa31f3380166fddf8e962ccb2e4" + integrity sha512-FeVc50FTfVVQnolk/WQT8MX+2WVcDnTGiq6Wo+/+lJ2ET1bRVi3HG3YlJUfqagNMc/kUlFSoR96AJkxGpKz13g== + dependencies: + ast-module-types "^6.0.1" + node-source-walk "^7.0.1" + +module-lookup-amd@^9.0.3: + version "9.0.5" + resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-9.0.5.tgz#2563ba8e4f9dbcda914eac3ba4dc3ad8af80eb7d" + integrity sha512-Rs5FVpVcBYRHPLuhHOjgbRhosaQYLtEo3JIeDIbmNo7mSssi1CTzwMh8v36gAzpbzLGXI9wB/yHh+5+3fY1QVw== + dependencies: + commander "^12.1.0" + glob "^7.2.3" + requirejs "^2.3.7" + requirejs-config-file "^4.0.0" + ms@^2.0.0, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -2464,6 +2944,11 @@ nanoid@3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -2507,6 +2992,13 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== +node-source-walk@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-7.0.1.tgz#3e4ab8d065377228fd038af7b2d4fb58f61defd3" + integrity sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg== + dependencies: + "@babel/parser" "^7.26.7" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -2551,6 +3043,13 @@ once@^1.3.0: dependencies: wrappy "1" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -2563,6 +3062,21 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -2589,6 +3103,11 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" + integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2604,6 +3123,11 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-to-regexp@^8.1.0: version "8.2.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" @@ -2629,11 +3153,55 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + possible-typed-array-names@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== +postcss-values-parser@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz#636edc5b86c953896f1bb0d7a7a6615df00fb76f" + integrity sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw== + dependencies: + color-name "^1.1.4" + is-url-superb "^4.0.0" + quote-unquote "^1.0.0" + +postcss@^8.5.1, postcss@^8.5.6: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +precinct@^12.2.0: + version "12.2.0" + resolved "https://registry.yarnpkg.com/precinct/-/precinct-12.2.0.tgz#6ab18f48034cc534f2c8fedb318f19a11bcd171b" + integrity sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w== + dependencies: + "@dependents/detective-less" "^5.0.1" + commander "^12.1.0" + detective-amd "^6.0.1" + detective-cjs "^6.0.1" + detective-es6 "^5.0.1" + detective-postcss "^7.0.1" + detective-sass "^6.0.1" + detective-scss "^5.0.1" + detective-stylus "^5.0.1" + detective-typescript "^14.0.0" + detective-vue2 "^2.2.0" + module-definition "^6.0.1" + node-source-walk "^7.0.1" + postcss "^8.5.1" + typescript "^5.7.3" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -2666,6 +3234,13 @@ pretty-format@^28.0.0, pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-ms@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + protobufjs@^7.2.5, protobufjs@^7.4.0: version "7.5.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.3.tgz#13f95a9e3c84669995ec3652db2ac2fb00b89363" @@ -2699,6 +3274,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quote-unquote@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" + integrity sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -2706,11 +3286,30 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +readable-stream@^3.4.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -2723,11 +3322,46 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +requirejs-config-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" + integrity sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw== + dependencies: + esprima "^4.0.0" + stringify-object "^3.2.1" + +requirejs@^2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.7.tgz#0b22032e51a967900e0ae9f32762c23a87036bd0" + integrity sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw== + +resolve-dependency-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-4.0.1.tgz#1b9d43e5b62384301e26d040b9fce61ee5db60bd" + integrity sha512-YQftIIC4vzO9UMhO/sCgXukNyiwVRCVaxiWskCBy7Zpqkplm8kTAISZ8O1MoKW1ca6xzgLUBjZTcDgypXvXxiQ== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve@^1.22.10: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + reusify@^1.0.4: version "1.1.0" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" @@ -2776,7 +3410,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.0.1, safe-buffer@^5.1.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -2795,7 +3429,15 @@ safe-regex-test@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver@^7.3.7, semver@^7.5.4: +sass-lookup@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-6.1.0.tgz#a13b1f31dd44d2b4bcd55ba8f72763db4d95bd7c" + integrity sha512-Zx+lVyoWqXZxHuYWlTA17Z5sczJ6braNT2C7rmClw+c4E7r/n911Zwss3h1uHI9reR5AgHZyNHF7c2+VIp5AUA== + dependencies: + commander "^12.1.0" + enhanced-resolve "^5.18.0" + +semver@^7.3.7, semver@^7.5.4, semver@^7.6.0: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== @@ -2831,6 +3473,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + sinon@18.0.1: version "18.0.1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-18.0.1.tgz#464334cdfea2cddc5eda9a4ea7e2e3f0c7a91c5e" @@ -2895,6 +3542,16 @@ solana-bankrun@0.3.1: solana-bankrun-linux-x64-gnu "0.3.1" solana-bankrun-linux-x64-musl "0.3.1" +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + spok@^1.4.3: version "1.5.5" resolved "https://registry.yarnpkg.com/spok/-/spok-1.5.5.tgz#a51f7f290a53131d7b7a922dfedc461dda0aed72" @@ -2922,6 +3579,13 @@ stream-json@^1.9.1: dependencies: stream-chain "^2.2.5" +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== + dependencies: + any-promise "^1.1.0" + strict-event-emitter-types@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz#05e15549cb4da1694478a53543e4e2f4abcf277f" @@ -2936,6 +3600,22 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +stringify-object@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2943,11 +3623,28 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +stylus-lookup@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-6.1.0.tgz#f0fe88a885b830dc7520f51dd0a7e59e5d3307b4" + integrity sha512-5QSwgxAzXPMN+yugy61C60PhoANdItfdjSEZR8siFwz7yL9jTmV0UBKDCfn3K8GkGB4g0Y9py7vTCX8rFu4/pQ== + dependencies: + commander "^12.1.0" + superstruct@^0.15.4: version "0.15.5" resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.15.5.tgz#0f0a8d3ce31313f0d84c6096cd4fa1bfdedc9dab" @@ -2977,6 +3674,16 @@ supports-color@^8.1.1: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.2.tgz#ab4984340d30cb9989a490032f086dbb8b56d872" + integrity sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg== + text-encoding-utf-8@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" @@ -3014,6 +3721,21 @@ ts-api-utils@^1.0.1: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064" integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw== +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== + +ts-graphviz@^2.1.2: + version "2.1.6" + resolved "https://registry.yarnpkg.com/ts-graphviz/-/ts-graphviz-2.1.6.tgz#007fcb42b4e8c55d26543ece9e86395bd3c3cfd6" + integrity sha512-XyLVuhBVvdJTJr2FJJV2L1pc4MwSjMhcunRVgDE9k4wbb2ee7ORYnPewxMWUav12vxyfUM686MSGsqnVRIInuw== + dependencies: + "@ts-graphviz/adapter" "^2.0.6" + "@ts-graphviz/ast" "^2.0.7" + "@ts-graphviz/common" "^2.1.5" + "@ts-graphviz/core" "^2.0.7" + ts-log@^2.2.7: version "2.2.7" resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.7.tgz#4f4512144898b77c9984e91587076fcb8518688e" @@ -3038,6 +3760,15 @@ ts-node@10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tsconfig-paths@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@^2.0.3, tslib@^2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" @@ -3080,6 +3811,11 @@ typescript@5.4.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== +typescript@^5.7.3, typescript@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -3104,6 +3840,11 @@ utf-8-validate@^5.0.2: dependencies: node-gyp-build "^4.3.0" +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + util@^0.12.5: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" @@ -3125,6 +3866,18 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +walkdir@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" + integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From fc505ac631ac9ec02820964c6490fcbd8718b161 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 28 Jul 2025 18:21:24 -0400 Subject: [PATCH 015/216] Nour/mm oracle 2 (#1767) * program: new amm oracle (#1738) * zero unused amm fields * cargo fmt * bare bones ix * minimal anchor mm oracle impl * update test file * only do admin validate when not anchor test * updates * generalize native entry * fix weird function name chop off * make it compile for --feature cpi (#1748) Co-authored-by: jordy25519 * more efficeint clock and state bit flags check * vamm uses mm oracle (#1747) * add offset * working tests * refactor to use MM oracle as its own type * remove weird preface * sdk updates * bankrun tests all pass * fix test * changes and fixes * widen confidence if mm oracle too diff * sdk side for confidence adjust * changelog * fix lint * fix cargo tests * address comments * add conf check * remove anchor ix and cache oracle confidence * only state admin can reenable mm oracle kill switch * cargo fmt --------- Co-authored-by: jordy25519 * fix tests (#1764) * Nour/move ixs around (#1766) * move around ixs * remove message * add devnet oracle crank wallet * refactored mm oracle * sdk changes + cargo fmt * fix tests * validate price bands with fill fix * normalize fill within price bands * add sdk warning * updated type * undefined guard so anchor tests pass * accept vec for update amm and view amm * adjust test to work with new price bands * Revert "adjust test to work with new price bands" This reverts commit ee40ac8799fa2f6222ea7d0e9b3e07014346a699. * remove price bands logic * add zero ix for mm oracle for reset * mm oracle improvements (#1771) * v1 safety improvements * isolate funding from MM oracle * add cargo tests for amm availability * change oracle validity log bool to enum * address comment --------- Co-authored-by: moosecat Co-authored-by: jordy25519 --- deploy-scripts/build-devnet.sh | 2 +- programs/drift/Cargo.toml | 2 +- programs/drift/src/controller/funding.rs | 4 +- programs/drift/src/controller/liquidation.rs | 14 +- programs/drift/src/controller/orders.rs | 40 +++- programs/drift/src/controller/orders/tests.rs | 180 ++++++++++++++++- .../drift/src/controller/position/tests.rs | 182 ++++++++++++++++-- programs/drift/src/controller/repeg.rs | 43 +++-- programs/drift/src/controller/repeg/tests.rs | 66 +++++-- programs/drift/src/controller/spot_balance.rs | 4 +- programs/drift/src/ids.rs | 23 +++ programs/drift/src/instructions/admin.rs | 131 +++++++++---- programs/drift/src/instructions/keeper.rs | 28 ++- programs/drift/src/lib.rs | 40 +++- programs/drift/src/macros.rs | 12 ++ programs/drift/src/math/amm.rs | 48 +++-- programs/drift/src/math/amm/tests.rs | 106 ++++++++-- programs/drift/src/math/funding/tests.rs | 13 +- programs/drift/src/math/oracle.rs | 45 ++++- programs/drift/src/math/oracle/tests.rs | 6 +- programs/drift/src/math/orders.rs | 2 - programs/drift/src/math/repeg.rs | 7 +- programs/drift/src/math/repeg/tests.rs | 65 ++++++- programs/drift/src/state/oracle.rs | 121 ++++++++++-- programs/drift/src/state/oracle/tests.rs | 112 ++++++++++- programs/drift/src/state/oracle_map.rs | 6 +- programs/drift/src/state/perp_market.rs | 72 +++++-- programs/drift/src/state/perp_market_map.rs | 5 +- programs/drift/src/state/state.rs | 3 +- sdk/src/adminClient.ts | 52 ++++- sdk/src/driftClient.ts | 144 +++++++++++++- sdk/src/idl/drift.json | 70 +++++-- sdk/src/index.ts | 1 + sdk/src/math/amm.ts | 15 ++ sdk/src/oracles/types.ts | 7 + sdk/src/oracles/utils.ts | 13 ++ sdk/src/tx/utils.ts | 10 + sdk/src/types.ts | 7 +- test-scripts/single-anchor-test.sh | 2 +- tests/admin.ts | 61 +++++- tests/liquidityProvider.ts | 4 +- tests/perpLpJit.ts | 8 +- tests/prepegMarketOrderBaseAssetAmount.ts | 31 ++- tests/referencePriceOffset.ts | 128 +++++++++++- tests/testHelpers.ts | 5 +- 45 files changed, 1667 insertions(+), 273 deletions(-) create mode 100644 sdk/src/oracles/utils.ts diff --git a/deploy-scripts/build-devnet.sh b/deploy-scripts/build-devnet.sh index f88517cb3e..9a1c318b69 100644 --- a/deploy-scripts/build-devnet.sh +++ b/deploy-scripts/build-devnet.sh @@ -1,2 +1,2 @@ #!/bin/sh -anchor build -- --no-default-features \ No newline at end of file +anchor build -- --no-default-features --features no-entrypoint \ No newline at end of file diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index fc6a5d3808..04320aa616 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -14,7 +14,7 @@ no-entrypoint = [] cpi = ["no-entrypoint"] mainnet-beta=[] anchor-test= [] -default=["mainnet-beta"] +default=["mainnet-beta", "no-entrypoint"] drift-rs=[] [dependencies] diff --git a/programs/drift/src/controller/funding.rs b/programs/drift/src/controller/funding.rs index a66d7c9cc3..e249f12540 100644 --- a/programs/drift/src/controller/funding.rs +++ b/programs/drift/src/controller/funding.rs @@ -188,11 +188,13 @@ pub fn update_funding_rate( if valid_funding_update { let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; let sanitize_clamp_denominator = market.get_sanitize_clamp_denominator()?; + let mm_oracle_price_data = + market.get_mm_oracle_price_data(*oracle_price_data, slot, &guard_rails.validity)?; let oracle_price_twap = amm::update_oracle_price_twap( &mut market.amm, now, - oracle_price_data, + &mm_oracle_price_data, Some(reserve_price), sanitize_clamp_denominator, )?; diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 5e2d73f1de..1e2e8eb82e 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -203,10 +203,15 @@ pub fn liquidate_perp( let mut market = perp_market_map.get_ref_mut(&market_index)?; let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; + let mm_oracle_price_data = market.get_mm_oracle_price_data( + *oracle_price_data, + slot, + &state.oracle_guard_rails.validity, + )?; update_amm_and_check_validity( &mut market, - oracle_price_data, + &mm_oracle_price_data, state, now, slot, @@ -848,10 +853,15 @@ pub fn liquidate_perp_with_fill( let mut market = perp_market_map.get_ref_mut(&market_index)?; let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; + let mm_oracle_price_data = market.get_mm_oracle_price_data( + *oracle_price_data, + slot, + &state.oracle_guard_rails.validity, + )?; update_amm_and_check_validity( &mut market, - oracle_price_data, + &mm_oracle_price_data, state, now, slot, diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 2de96e9a68..377fcd3c9e 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -42,7 +42,9 @@ use crate::math::matching::{ are_orders_same_market_but_different_sides, calculate_fill_for_matched_orders, calculate_filler_multiplier_for_matched_orders, do_orders_cross, is_maker_for_taker, }; -use crate::math::oracle::{is_oracle_valid_for_action, DriftAction, OracleValidity}; +use crate::math::oracle::{ + self, is_oracle_valid_for_action, oracle_validity, DriftAction, OracleValidity, +}; use crate::math::safe_math::SafeMath; use crate::math::safe_unwrap::SafeUnwrap; use crate::math::spot_balance::{get_signed_token_amount, get_token_amount}; @@ -1048,7 +1050,7 @@ pub fn fill_perp_order( } let reserve_price_before: u64; - let oracle_validity: OracleValidity; + let safe_oracle_validity: OracleValidity; let oracle_price: i64; let oracle_twap_5min: i64; let perp_market_index: u16; @@ -1068,19 +1070,29 @@ pub fn fill_perp_order( "Market is in settlement mode", )?; - let (oracle_price_data, _oracle_validity) = oracle_map.get_price_data_and_validity( + let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; + let mm_oracle_price_data = market.get_mm_oracle_price_data( + *oracle_price_data, + slot, + &state.oracle_guard_rails.validity, + )?; + let safe_oracle_price_data = mm_oracle_price_data.get_safe_oracle_price_data(); + safe_oracle_validity = oracle_validity( MarketType::Perp, market.market_index, - &market.oracle_id(), market.amm.historical_oracle_data.last_oracle_price_twap, + &safe_oracle_price_data, + &state.oracle_guard_rails.validity, market.get_max_confidence_interval_multiplier()?, + &market.amm.oracle_source, + oracle::LogMode::SafeMMOracle, market.amm.oracle_slot_delay_override, )?; oracle_valid_for_amm_fill = - is_oracle_valid_for_action(_oracle_validity, Some(DriftAction::FillOrderAmm))?; + is_oracle_valid_for_action(safe_oracle_validity, Some(DriftAction::FillOrderAmm))?; - oracle_stale_for_margin = oracle_price_data.delay + oracle_stale_for_margin = mm_oracle_price_data.get_delay() > state .oracle_guard_rails .validity @@ -1090,6 +1102,17 @@ pub fn fill_perp_order( amm_is_available &= !market.is_operation_paused(PerpOperation::AmmFill); amm_is_available &= !market.has_too_much_drawdown()?; + // We are already using safe oracle data from MM oracle. + // But AMM isnt available if we could have used MM oracle but fell back due to price diff + let amm_available_mm_oracle_recent_but_volatile = + if mm_oracle_price_data.is_enabled() && mm_oracle_price_data.is_mm_oracle_as_recent() { + let amm_available = !mm_oracle_price_data.is_mm_exchange_diff_bps_high(); + amm_available + } else { + true + }; + amm_is_available &= amm_available_mm_oracle_recent_but_volatile; + let amm_wants_to_jit_make = market.amm.amm_wants_to_jit_make(order_direction)?; amm_lp_allowed_to_jit_make = market .amm @@ -1100,12 +1123,11 @@ pub fn fill_perp_order( user_can_skip_duration = user.can_skip_auction_duration(user_stats)?; reserve_price_before = market.amm.reserve_price()?; - oracle_price = oracle_price_data.price; + oracle_price = mm_oracle_price_data.get_price(); oracle_twap_5min = market .amm .historical_oracle_data .last_oracle_price_twap_5min; - oracle_validity = _oracle_validity; perp_market_index = market.market_index; min_auction_duration = @@ -1114,7 +1136,7 @@ pub fn fill_perp_order( // allow oracle price to be used to calculate limit price if it's valid or stale for amm let valid_oracle_price = - if is_oracle_valid_for_action(oracle_validity, Some(DriftAction::OracleOrderPrice))? { + if is_oracle_valid_for_action(safe_oracle_validity, Some(DriftAction::OracleOrderPrice))? { Some(oracle_price) } else { msg!("Perp market = {} oracle deemed invalid", perp_market_index); diff --git a/programs/drift/src/controller/orders/tests.rs b/programs/drift/src/controller/orders/tests.rs index 2f752066b8..96038e98c7 100644 --- a/programs/drift/src/controller/orders/tests.rs +++ b/programs/drift/src/controller/orders/tests.rs @@ -2973,7 +2973,9 @@ pub mod fulfill_order { use std::str::FromStr; use std::u64; - use crate::controller::orders::{fulfill_perp_order, validate_market_within_price_band}; + use crate::controller::orders::{ + fill_perp_order, fulfill_perp_order, validate_market_within_price_band, + }; use crate::controller::position::PositionDirection; use crate::create_anchor_account_info; use crate::error::ErrorCode; @@ -4845,6 +4847,182 @@ pub mod fulfill_order { assert_eq!(ask_price, 100069968); // ~ 100.1 * (0.9997) } + #[test] + fn amm_unavailable_from_volatile_mm_oracle() { + use anchor_lang::prelude::{AccountLoader, Clock}; + + let slot = 56_u64; + let clock = Clock { + slot, + epoch_start_timestamp: 0, + epoch: 0, + leader_schedule_epoch: 0, + unix_timestamp: 0, + }; + + let mut oracle_price = get_pyth_price(100, 6); + let oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + oracle_price, + &oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + + let mut market = PerpMarket { + amm: AMM { + base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + bid_base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + bid_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + ask_base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + ask_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + terminal_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + sqrt_k: 100 * AMM_RESERVE_PRECISION, + peg_multiplier: 100 * PEG_PRECISION, + max_slippage_ratio: 50, + max_fill_reserve_fraction: 100, + order_step_size: 1000, + order_tick_size: 1, + oracle: oracle_price_key, + base_spread: 0, // 1 basis point + mm_oracle_price: 102 * PRICE_PRECISION_I64, + mm_oracle_slot: slot, + historical_oracle_data: HistoricalOracleData { + last_oracle_price: (100 * PRICE_PRECISION) as i64, + last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, + last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, + + ..HistoricalOracleData::default() + }, + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + status: MarketStatus::Initialized, + ..PerpMarket::default_test() + }; + market.amm.max_base_asset_reserve = u128::MAX; + market.amm.min_base_asset_reserve = 0; + market.status = MarketStatus::Active; + + create_anchor_account_info!(market, PerpMarket, market_account_info); + let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + + let mut spot_market = SpotMarket { + market_index: 0, + oracle_source: OracleSource::QuoteAsset, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 6, + initial_asset_weight: SPOT_WEIGHT_PRECISION, + maintenance_asset_weight: SPOT_WEIGHT_PRECISION, + historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), + ..SpotMarket::default() + }; + create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); + let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); + + let mut taker = User { + orders: get_orders(Order { + market_index: 0, + status: OrderStatus::Open, + order_type: OrderType::Market, + direction: PositionDirection::Long, + base_asset_amount: BASE_PRECISION_U64, + slot: 0, + auction_start_price: 0, + auction_end_price: 100 * PRICE_PRECISION_I64, + auction_duration: 0, + price: 150 * PRICE_PRECISION_U64, + order_id: 1, + ..Order::default() + }), + perp_positions: get_positions(PerpPosition { + market_index: 0, + open_orders: 1, + open_bids: BASE_PRECISION_I64, + ..PerpPosition::default() + }), + spot_positions: get_spot_positions(SpotPosition { + market_index: 0, + balance_type: SpotBalanceType::Deposit, + scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, + ..SpotPosition::default() + }), + ..User::default() + }; + create_anchor_account_info!(taker, User, user_account_info); + let user_account_loader: AccountLoader = + AccountLoader::try_from(&user_account_info).unwrap(); + create_anchor_account_info!(UserStats::default(), UserStats, user_stats_account_info); + let user_stats_account_loader: AccountLoader = + AccountLoader::try_from(&user_stats_account_info).unwrap(); + + let filler_key = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap(); + create_anchor_account_info!(User::default(), &filler_key, User, user_account_info); + let filler_account_loader: AccountLoader = + AccountLoader::try_from(&user_account_info).unwrap(); + + create_anchor_account_info!(UserStats::default(), UserStats, filler_stats_account_info); + let filler_stats_account_loader: AccountLoader = + AccountLoader::try_from(&filler_stats_account_info).unwrap(); + + let state = State { + min_perp_auction_duration: 1, + default_market_order_time_in_force: 10, + ..State::default() + }; + + let (base_asset_amount, _) = fill_perp_order( + 1, + &state, + &user_account_loader, + &user_stats_account_loader, + &spot_market_map, + &market_map, + &mut oracle_map, + &filler_account_loader, + &filler_stats_account_loader, + &UserMap::empty(), + &UserStatsMap::empty(), + None, + &clock, + FillMode::Fill, + ) + .unwrap(); + + assert_eq!(base_asset_amount, 0); + + // Will fill if MM oracle price is not too volatile at mm oracle price + market.amm.mm_oracle_price = 101 * PRICE_PRECISION_I64; + create_anchor_account_info!(market, PerpMarket, market_account_info); + let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + + let (base_asset_amount, quote_asset_amount) = fill_perp_order( + 1, + &state, + &user_account_loader, + &user_stats_account_loader, + &spot_market_map, + &market_map, + &mut oracle_map, + &filler_account_loader, + &filler_stats_account_loader, + &UserMap::empty(), + &UserStatsMap::empty(), + None, + &clock, + FillMode::Fill, + ) + .unwrap(); + + assert_eq!(base_asset_amount, BASE_PRECISION_U64); + assert_eq!(quote_asset_amount, 101010102); + } + // Add back if we check free collateral in fill again // #[test] // fn fulfill_with_negative_free_collateral() { diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index dcf545b049..b1c9926051 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -14,9 +14,10 @@ use crate::math::constants::{ SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, }; use crate::math::lp::calculate_settle_lp_metrics; +use crate::math::oracle::OracleValidity; use crate::math::position::swap_direction_to_close_position; use crate::math::repeg; -use crate::state::oracle::{OraclePriceData, PrelaunchOracle}; +use crate::state::oracle::{MMOraclePriceData, OraclePriceData, PrelaunchOracle}; use crate::state::oracle_map::OracleMap; use crate::state::perp_market::{AMMLiquiditySplit, PerpMarket, AMM}; use crate::state::perp_market_map::PerpMarketMap; @@ -540,6 +541,13 @@ fn amm_pred_market_example() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let (max_bids, max_asks) = calculate_market_open_bids_asks(&perp_market.amm).unwrap(); perp_market.amm.curve_update_intensity = 99; @@ -550,7 +558,7 @@ fn amm_pred_market_example() { assert_eq!(perp_market.amm.sqrt_k, 56_649_660_613_272); let (optimal_peg, fee_budget, _check_lower_bound) = - repeg::calculate_optimal_peg_and_budget(&perp_market, &oracle_price_data).unwrap(); + repeg::calculate_optimal_peg_and_budget(&perp_market, &mm_oracle_price_data).unwrap(); assert_eq!(perp_market.amm.terminal_quote_asset_reserve, 56405211622548); assert_eq!(perp_market.amm.quote_asset_reserve, 56933567973708); @@ -581,7 +589,7 @@ fn amm_pred_market_example() { let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -671,9 +679,16 @@ fn amm_ref_price_decay_tail_test() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -696,10 +711,17 @@ fn amm_ref_price_decay_tail_test() { now += 250; clock_slot += 700; } + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -833,9 +855,16 @@ fn amm_ref_price_offset_decay_logic() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -865,10 +894,17 @@ fn amm_ref_price_offset_decay_logic() { now += 1; clock_slot += 2; } + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -992,9 +1028,16 @@ fn amm_negative_ref_price_offset_decay_logic() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -1025,10 +1068,17 @@ fn amm_negative_ref_price_offset_decay_logic() { now += 1; clock_slot += 2; } + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -1164,9 +1214,16 @@ fn amm_perp_ref_offset() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mm_oracle_price_data, &state, now, clock_slot, @@ -1191,6 +1248,61 @@ fn amm_perp_ref_offset() { assert_eq!(perp_market.amm.ask_base_asset_reserve, 4672813088646692); crate::validation::perp_market::validate_perp_market(&perp_market).unwrap(); + + // Update MM oracle and reference price offset stays the same and is applied to the MM oracle + perp_market.amm.mm_oracle_price = oracle_price_data.price * 1005 / 1000; + perp_market.amm.mm_oracle_slot = clock_slot; + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); + + let _ = _update_amm( + &mut perp_market, + &mm_oracle_price_data, + &state, + now, + clock_slot, + ); + let reserve_price_mm_offset = perp_market.amm.reserve_price().unwrap(); + let (b2, a2) = perp_market + .amm + .bid_ask_price(reserve_price_mm_offset) + .unwrap(); + assert_eq!(perp_market.amm.reference_price_offset, 133); + assert_eq!(reserve_price_mm_offset, 7137107); + assert_eq!(b2, 7101549); + assert_eq!(a2, 7174591); + + // Uses the original oracle if the slot is old, ignoring MM oracle + perp_market.amm.mm_oracle_price = mm_oracle_price_data.get_price() * 995 / 1000; + perp_market.amm.mm_oracle_slot = clock_slot - 100; + let mut mm_oracle_price = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); + + let _ = _update_amm( + &mut perp_market, + &mut mm_oracle_price, + &state, + now, + clock_slot, + ); + let reserve_price_mm_offset_3 = perp_market.amm.reserve_price().unwrap(); + let (b3, a3) = perp_market + .amm + .bid_ask_price(reserve_price_mm_offset_3) + .unwrap(); + assert_eq!(reserve_price_mm_offset_3, r); + assert_eq!(b3, 7066225); + assert_eq!(a3, 7138903); } #[test] @@ -1617,10 +1729,17 @@ fn amm_split_large_k_with_rebase() { delay: 14, has_sufficient_number_of_data_points: true, }; + let mut mm_oracle_price = perp_market + .get_mm_oracle_price_data( + oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + ) + .unwrap(); let cost = _update_amm( &mut perp_market, - &oracle_price_data, + &mut mm_oracle_price, &state, now, clock_slot, @@ -2708,6 +2827,8 @@ fn update_amm_near_boundary() { let mut decoded_bytes = base64::decode(oracle_market_str).unwrap(); let oracle_market_bytes = decoded_bytes.as_mut_slice(); + let state: State = State::default(); + let key = Pubkey::from_str("8ihFLu5FimgTQ1Unh4dVyEHUGodJ5gJQCrQf4KUVB9bN").unwrap(); let owner = Pubkey::from_str("FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH").unwrap(); let mut lamports = 0; @@ -2724,10 +2845,13 @@ fn update_amm_near_boundary() { println!("perp_market: {:?}", perp_market.amm.last_update_slot); let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap(); + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data(*oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); let state = State::default(); - let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap(); + let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost, 18803837952); } @@ -2738,6 +2862,8 @@ fn update_amm_near_boundary2() { let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); let perp_market_bytes = decoded_bytes.as_mut_slice(); + let state: State = State::default(); + let key = Pubkey::from_str("2QeqpeJUVo2LBWNELRfcBwJgrNoxJQSd7gokcaM5nvaa").unwrap(); let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); let mut lamports = 0; @@ -2766,10 +2892,13 @@ fn update_amm_near_boundary2() { println!("perp_market: {:?}", perp_market.amm.last_update_slot); let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap(); - + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data(*oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); let state = State::default(); - let cost: i128 = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap(); + let cost: i128 = + _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert!(perp_market.amm.last_oracle_valid); assert_eq!(cost, 2987010); } @@ -2780,6 +2909,8 @@ fn recenter_amm_1() { let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); let perp_market_bytes = decoded_bytes.as_mut_slice(); + let state: State = State::default(); + let key = Pubkey::from_str("2QeqpeJUVo2LBWNELRfcBwJgrNoxJQSd7gokcaM5nvaa").unwrap(); let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); let mut lamports = 0; @@ -2808,10 +2939,13 @@ fn recenter_amm_1() { println!("perp_market: {:?}", perp_market.amm.last_update_slot); let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap(); + let mm_oracle_price_data = perp_market + .get_mm_oracle_price_data(*oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); let state = State::default(); - let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap(); + let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost, 2987010); @@ -2908,10 +3042,17 @@ fn recenter_amm_2() { let oracle_price_data = oracle_map .get_price_data(&(oracle_price_key, OracleSource::Pyth)) .unwrap(); + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + *oracle_price_data, + ) + .unwrap(); let state = State::default(); - let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap(); + let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost, 0); @@ -3037,10 +3178,17 @@ fn test_move_amm() { let oracle_price_data = oracle_map .get_price_data(&(oracle_price_key, OracleSource::Pyth)) .unwrap(); + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + *oracle_price_data, + ) + .unwrap(); let state = State::default(); - let cost = _update_amm(&mut perp_market, oracle_price_data, &state, now, slot).unwrap(); + let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost, 0); diff --git a/programs/drift/src/controller/repeg.rs b/programs/drift/src/controller/repeg.rs index 861d53e382..aad0a5db5c 100644 --- a/programs/drift/src/controller/repeg.rs +++ b/programs/drift/src/controller/repeg.rs @@ -1,6 +1,9 @@ use std::cmp::min; +use crate::math::oracle::LogMode; use crate::msg; +use crate::state::oracle::MMOraclePriceData; +use crate::state::oracle::OraclePriceData; use anchor_lang::prelude::AccountInfo; use anchor_lang::prelude::*; @@ -24,7 +27,7 @@ use crate::math::repeg; use crate::math::safe_math::SafeMath; use crate::math::spot_balance::get_token_amount; -use crate::state::oracle::{OraclePriceData, OracleSource}; +use crate::state::oracle::OracleSource; use crate::state::oracle_map::OracleMap; use crate::state::perp_market::{MarketStatus, PerpMarket}; use crate::state::perp_market_map::PerpMarketMap; @@ -110,8 +113,14 @@ pub fn update_amms( let updated = true; // todo for (_key, market_account_loader) in perp_market_map.0.iter_mut() { let market = &mut load_mut!(market_account_loader)?; - let oracle_price_data = &oracle_map.get_price_data(&market.oracle_id())?; - _update_amm(market, oracle_price_data, state, now, clock_slot)?; + let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; + let mm_oracle_price_data = market.get_mm_oracle_price_data( + *oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + )?; + + _update_amm(market, &mm_oracle_price_data, state, now, clock_slot)?; } Ok(updated) @@ -126,10 +135,15 @@ pub fn update_amm( ) -> DriftResult { let market = &mut perp_market_map.get_ref_mut(&market_index)?; let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; + let mm_oracle_price_data = market.get_mm_oracle_price_data( + *oracle_price_data, + clock.slot, + &state.oracle_guard_rails.validity, + )?; let cost_of_update = _update_amm( market, - oracle_price_data, + &mm_oracle_price_data, state, clock.unix_timestamp, clock.slot, @@ -140,7 +154,7 @@ pub fn update_amm( pub fn _update_amm( market: &mut PerpMarket, - oracle_price_data: &OraclePriceData, + mm_oracle_price_data: &MMOraclePriceData, state: &State, now: i64, clock_slot: u64, @@ -152,15 +166,16 @@ pub fn _update_amm( return Ok(0); } + let oracle_data = &mm_oracle_price_data.get_safe_oracle_price_data(); let oracle_validity = oracle::oracle_validity( MarketType::Perp, market.market_index, market.amm.historical_oracle_data.last_oracle_price_twap, - oracle_price_data, + oracle_data, &state.oracle_guard_rails.validity, market.get_max_confidence_interval_multiplier()?, &market.amm.oracle_source, - true, + oracle::LogMode::SafeMMOracle, 0, )?; @@ -172,7 +187,7 @@ pub fn _update_amm( if curve_update_intensity > 0 { let (optimal_peg, fee_budget, check_lower_bound) = - repeg::calculate_optimal_peg_and_budget(market, oracle_price_data)?; + repeg::calculate_optimal_peg_and_budget(market, mm_oracle_price_data)?; let (repegged_market, repegged_cost) = repeg::adjust_amm( market, @@ -208,7 +223,7 @@ pub fn _update_amm( amm::update_oracle_price_twap( &mut market.amm, now, - oracle_price_data, + mm_oracle_price_data, Some(reserve_price_after), sanitize_clamp_denominator, )?; @@ -230,13 +245,13 @@ pub fn _update_amm( pub fn update_amm_and_check_validity( market: &mut PerpMarket, - oracle_price_data: &OraclePriceData, + mm_oracle_price_data: &MMOraclePriceData, state: &State, now: i64, clock_slot: u64, action: Option, ) -> DriftResult { - _update_amm(market, oracle_price_data, state, now, clock_slot)?; + _update_amm(market, mm_oracle_price_data, state, now, clock_slot)?; // 1 hour EMA let risk_ema_price = market.amm.historical_oracle_data.last_oracle_price_twap; @@ -245,11 +260,11 @@ pub fn update_amm_and_check_validity( MarketType::Perp, market.market_index, risk_ema_price, - oracle_price_data, + &mm_oracle_price_data.get_safe_oracle_price_data(), &state.oracle_guard_rails.validity, market.get_max_confidence_interval_multiplier()?, &market.amm.oracle_source, - false, + LogMode::SafeMMOracle, 0, )?; @@ -257,7 +272,7 @@ pub fn update_amm_and_check_validity( is_oracle_valid_for_action(oracle_validity, action)?, ErrorCode::InvalidOracle, "Invalid Oracle ({:?} vs ema={:?}) for perp market index={} and action={:?}", - oracle_price_data, + mm_oracle_price_data.get_safe_oracle_price_data(), risk_ema_price, market.market_index, action diff --git a/programs/drift/src/controller/repeg/tests.rs b/programs/drift/src/controller/repeg/tests.rs index 0a2906f6e4..08b4f32d18 100644 --- a/programs/drift/src/controller/repeg/tests.rs +++ b/programs/drift/src/controller/repeg/tests.rs @@ -97,7 +97,11 @@ pub fn update_amm_test() { .unwrap(); assert!(!too_diverge); - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(market.amm.sqrt_k, 63936000000); let is_oracle_valid = oracle::oracle_validity( @@ -108,7 +112,7 @@ pub fn update_amm_test() { &state.oracle_guard_rails.validity, market.get_max_confidence_interval_multiplier().unwrap(), &market.amm.oracle_source, - false, + LogMode::ExchangeOracle, 0, ) .unwrap() @@ -224,8 +228,12 @@ pub fn update_amm_test_bad_oracle() { delay: 12, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); - let _cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let _cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert!(!market.amm.last_oracle_valid); assert!(market.amm.last_update_slot == 0); @@ -237,7 +245,7 @@ pub fn update_amm_test_bad_oracle() { &state.oracle_guard_rails.validity, market.get_max_confidence_interval_multiplier().unwrap(), &market.amm.oracle_source, - false, + LogMode::None, 0, ) .unwrap() @@ -278,10 +286,14 @@ pub fn update_amm_larg_conf_test() { delay: 9, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); assert_eq!(market.amm.long_spread, 0); assert_eq!(market.amm.short_spread, 0); - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, -42992787); // amm wins when price increases assert_eq!(market.amm.short_spread, 12576); @@ -298,9 +310,12 @@ pub fn update_amm_larg_conf_test() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); - slot += 1; - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, 0); let mrk = market.amm.reserve_price().unwrap(); @@ -338,7 +353,11 @@ pub fn update_amm_larg_conf_test() { let optimal_peg_cost = calculate_repeg_cost(&market.amm, optimal_peg).unwrap(); assert_eq!(optimal_peg_cost, 30468749); - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, 30468749); assert_eq!(market.amm.long_spread, 1888); assert_eq!(market.amm.short_spread, 28443); @@ -359,8 +378,12 @@ pub fn update_amm_larg_conf_test() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, -3046875); assert_eq!(market.amm.long_spread, 1877); assert_eq!(market.amm.short_spread, 28289); @@ -410,6 +433,9 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { delay: 9, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); assert_eq!(market.amm.long_spread, 0); assert_eq!(market.amm.short_spread, 0); assert_eq!(market.amm.last_update_slot, 0); @@ -417,7 +443,8 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { let prev_peg_multiplier = market.amm.peg_multiplier; let prev_total_fee_minus_distributions = market.amm.total_fee_minus_distributions; - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert!(market.amm.is_recent_oracle_valid(slot).unwrap()); assert_eq!(cost_of_update, -42992787); // amm wins when price increases assert_eq!(market.amm.sqrt_k, 64000000000); @@ -460,9 +487,12 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); - slot += 1; - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, 0); let mrk = market.amm.reserve_price().unwrap(); @@ -483,6 +513,9 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); let fee_budget = calculate_fee_pool(&market).unwrap(); assert_eq!(market.amm.total_fee_minus_distributions, -9957007213); @@ -502,7 +535,8 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { let prev_peg_multiplier = market.amm.peg_multiplier; let prev_total_fee_minus_distributions = market.amm.total_fee_minus_distributions; - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, 21459587); // amm loses when price decreases (given users are net short) assert_eq!(market.amm.sqrt_k, 63936000000); // k lowered since cost_of_update is positive and total_fee_minus_distributions negative assert_eq!(market.amm.base_asset_reserve, 64935000065); @@ -555,8 +589,12 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); - let cost_of_update = _update_amm(&mut market, &oracle_price_data, &state, now, slot).unwrap(); + let cost_of_update = + _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost_of_update, 299367); assert_eq!(market.amm.long_spread, 11685); assert_eq!(market.amm.short_spread, 18426); diff --git a/programs/drift/src/controller/spot_balance.rs b/programs/drift/src/controller/spot_balance.rs index 11f9511c02..47cf95b22a 100644 --- a/programs/drift/src/controller/spot_balance.rs +++ b/programs/drift/src/controller/spot_balance.rs @@ -1,4 +1,4 @@ -use crate::math::oracle::oracle_validity; +use crate::math::oracle::{oracle_validity, LogMode}; use crate::state::state::ValidityGuardRails; use std::cmp::max; //, OracleValidity}; @@ -403,7 +403,7 @@ pub fn update_spot_market_and_check_validity( validity_guard_rails, spot_market.get_max_confidence_interval_multiplier()?, &spot_market.oracle_source, - false, + LogMode::ExchangeOracle, 0, )?; diff --git a/programs/drift/src/ids.rs b/programs/drift/src/ids.rs index e1964b8477..0c2a80addb 100644 --- a/programs/drift/src/ids.rs +++ b/programs/drift/src/ids.rs @@ -72,7 +72,10 @@ pub mod marinade_mainnet { pub mod admin_hot_wallet { use solana_program::declare_id; + #[cfg(not(feature = "anchor-test"))] declare_id!("5hMjmxexWu954pX9gB9jkHxMqdjpxArQS2XdvkaevRax"); + #[cfg(feature = "anchor-test")] + declare_id!("1ucYHAGrBbi1PaecC4Ptq5ocZLWGLBmbGWysoDGNB1N"); } pub mod if_rebalance_wallet { @@ -84,3 +87,23 @@ pub mod lighthouse { use solana_program::declare_id; declare_id!("L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95"); } + +pub mod mm_oracle_crank_wallet { + use solana_program::declare_id; + #[cfg(not(feature = "anchor-test"))] + #[cfg(feature = "mainnet-beta")] + declare_id!("uZ1N4C9dc71Euu4GLYt5UURpFtg1WWSwo3F4Rn46Fr3"); + #[cfg(not(feature = "anchor-test"))] + #[cfg(not(feature = "mainnet-beta"))] + declare_id!("8X35rQUK2u9hfn8rMPwwr6ZSEUhbmfDPEapp589XyoM1"); + #[cfg(feature = "anchor-test")] + declare_id!("1ucYHAGrBbi1PaecC4Ptq5ocZLWGLBmbGWysoDGNB1N"); +} + +pub mod amm_spread_adjust_wallet { + use solana_program::declare_id; + #[cfg(not(feature = "anchor-test"))] + declare_id!("w1DrTeayRMutAiwzfJfK9zLcpkF7RzwPy1BLCgQA1aF"); + #[cfg(feature = "anchor-test")] + declare_id!("1ucYHAGrBbi1PaecC4Ptq5ocZLWGLBmbGWysoDGNB1N"); +} diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 110a26b8b1..0cff63f410 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -1,10 +1,8 @@ -use std::convert::identity; +use std::convert::{identity, TryInto}; use std::mem::size_of; -use crate::math::amm::calculate_amm_available_liquidity; use crate::msg; use anchor_lang::prelude::*; -use anchor_spl::token::Token; use anchor_spl::token_2022::Token2022; use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use phoenix::quantities::WrapperU64; @@ -14,18 +12,17 @@ use serum_dex::state::ToAlignedBytes; use crate::controller::token::close_vault; use crate::error::ErrorCode; -use crate::ids::admin_hot_wallet; +use crate::ids::{admin_hot_wallet, amm_spread_adjust_wallet, mm_oracle_crank_wallet}; use crate::instructions::constraints::*; use crate::instructions::optional_accounts::{load_maps, AccountMaps}; use crate::math::casting::Cast; use crate::math::constants::{ - AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO, AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO_I128, - DEFAULT_LIQUIDATION_MARGIN_BUFFER_RATIO, FEE_POOL_TO_REVENUE_POOL_THRESHOLD, - IF_FACTOR_PRECISION, INSURANCE_A_MAX, INSURANCE_B_MAX, INSURANCE_C_MAX, - INSURANCE_SPECULATIVE_MAX, LIQUIDATION_FEE_PRECISION, MAX_CONCENTRATION_COEFFICIENT, - MAX_SQRT_K, MAX_UPDATE_K_PRICE_CHANGE, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I64, - QUOTE_SPOT_MARKET_INDEX, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_IMF_PRECISION, - SPOT_WEIGHT_PRECISION, THIRTEEN_DAY, TWENTY_FOUR_HOUR, + AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO, DEFAULT_LIQUIDATION_MARGIN_BUFFER_RATIO, + FEE_POOL_TO_REVENUE_POOL_THRESHOLD, IF_FACTOR_PRECISION, INSURANCE_A_MAX, INSURANCE_B_MAX, + INSURANCE_C_MAX, INSURANCE_SPECULATIVE_MAX, LIQUIDATION_FEE_PRECISION, + MAX_CONCENTRATION_COEFFICIENT, MAX_SQRT_K, MAX_UPDATE_K_PRICE_CHANGE, PERCENTAGE_PRECISION, + PERCENTAGE_PRECISION_I64, QUOTE_SPOT_MARKET_INDEX, SPOT_CUMULATIVE_INTEREST_PRECISION, + SPOT_IMF_PRECISION, SPOT_WEIGHT_PRECISION, THIRTEEN_DAY, TWENTY_FOUR_HOUR, }; use crate::math::cp_curve::get_update_k_result; use crate::math::helpers::get_proportion_u128; @@ -117,7 +114,8 @@ pub fn handle_initialize(ctx: Context) -> Result<()> { initial_pct_to_liquidate: 0, max_number_of_sub_accounts: 0, max_initialize_user_fee: 0, - padding: [0; 10], + disable_bit_flags: 0, + padding: [0; 9], }; Ok(()) @@ -1016,7 +1014,7 @@ pub fn handle_initialize_perp_market( order_step_size, order_tick_size, min_order_size, - max_position_size: 0, + mm_oracle_price: 0, max_slippage_ratio: 50, // ~2% max_fill_reserve_fraction: 100, // moves price ~2% base_spread, @@ -1037,9 +1035,8 @@ pub fn handle_initialize_perp_market( mark_std: 0, oracle_std: 0, volume_24h: 0, - long_intensity_count: 0, long_intensity_volume: 0, - short_intensity_count: 0, + mm_oracle_slot: 0, short_intensity_volume: 0, last_trade_ts: now, curve_update_intensity, @@ -1059,7 +1056,7 @@ pub fn handle_initialize_perp_market( oracle_slot_delay_override: 0, taker_speed_bump_override: 0, amm_spread_adjustment: 0, - total_fee_earned_per_lp: 0, + mm_oracle_sequence_id: 0, net_unsettled_funding_pnl: 0, quote_asset_amount_with_unsettled_lp: 0, reference_price_offset: 0, @@ -4845,16 +4842,79 @@ pub fn handle_update_if_rebalance_config( Ok(()) } -pub fn handle_zero_amm_fields_prep_mm_oracle_info(ctx: Context) -> Result<()> { - let mut perp_market = ctx.accounts.perp_market.load_mut()?; - perp_market.amm.long_intensity_count = 0; - perp_market.amm.short_intensity_count = 0; - perp_market.amm.max_position_size = 0; - perp_market.amm.total_fee_earned_per_lp = 0; - msg!( - "zeroed amm fields for perp market {}", - perp_market.market_index +pub fn handle_zero_mm_oracle_fields(ctx: Context) -> Result<()> { + let mut perp_market = load_mut!(ctx.accounts.perp_market)?; + perp_market.amm.mm_oracle_price = 0; + perp_market.amm.mm_oracle_sequence_id = 0; + perp_market.amm.mm_oracle_slot = 0; + Ok(()) +} + +pub fn handle_update_mm_oracle_native(accounts: &[AccountInfo], data: &[u8]) -> Result<()> { + // Verify this ix is allowed + let state = &accounts[3].data.borrow(); + assert!(state[982] & 1 == 0, "ix disabled by admin state"); + + let signer_account = &accounts[1]; + #[cfg(not(feature = "anchor-test"))] + assert!( + signer_account.is_signer && *signer_account.key == mm_oracle_crank_wallet::id(), + "signer must be mm oracle crank wallet, signer: {}, mm oracle crank wallet: {}", + signer_account.key, + mm_oracle_crank_wallet::id() + ); + + let mut perp_market = accounts[0].data.borrow_mut(); + let perp_market_sequence_id = u64::from_le_bytes(perp_market[936..944].try_into().unwrap()); + let incoming_sequence_id = u64::from_le_bytes(data[8..16].try_into().unwrap()); + + if incoming_sequence_id > perp_market_sequence_id { + let clock_account = &accounts[2]; + let clock_data = clock_account.data.borrow(); + + perp_market[832..840].copy_from_slice(&clock_data[0..8]); + perp_market[912..920].copy_from_slice(&data[0..8]); + perp_market[936..944].copy_from_slice(&data[8..16]); + } + + Ok(()) +} + +pub fn handle_update_amm_spread_adjustment_native( + accounts: &[AccountInfo], + data: &[u8], +) -> Result<()> { + let signer_account = &accounts[1]; + #[cfg(not(feature = "anchor-test"))] + assert!( + signer_account.is_signer && *signer_account.key == amm_spread_adjust_wallet::id(), + "signer must be amm spread adjust wallet, signer: {}, amm spread adjust wallet: {}", + signer_account.key, + amm_spread_adjust_wallet::id() ); + let mut perp_market = accounts[0].data.borrow_mut(); + perp_market[934..935].copy_from_slice(&[data[0]]); + + Ok(()) +} + +pub fn handle_update_disable_bitflags_mm_oracle( + ctx: Context, + disable: bool, +) -> Result<()> { + let state = &mut ctx.accounts.state; + if disable { + msg!("Setting first bit to 1, disabling mm oracle update"); + state.disable_bit_flags = state.disable_bit_flags | 1; + } else { + validate!( + ctx.accounts.admin.key().eq(&state.admin), + ErrorCode::DefaultError, + "Only state admin can re-enable after kill switch" + )?; + msg!("Setting first bit to 0, enabling mm oracle update"); + state.disable_bit_flags = state.disable_bit_flags & !1; + } Ok(()) } @@ -5253,6 +5313,16 @@ pub struct AdminUpdateState<'info> { pub state: Box>, } +#[derive(Accounts)] +pub struct HotAdminUpdateState<'info> { + #[account( + constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin + )] + pub admin: Signer<'info>, + #[account(mut)] + pub state: Box>, +} + #[derive(Accounts)] pub struct AdminUpdateK<'info> { pub admin: Signer<'info>, @@ -5664,14 +5734,3 @@ pub struct UpdateIfRebalanceConfig<'info> { )] pub state: Box>, } - -#[derive(Accounts)] -pub struct UpdateAmmParams<'info> { - #[account( - mut, - constraint = admin.key() == admin_hot_wallet::id() - )] - pub admin: Signer<'info>, - #[account(mut)] - pub perp_market: AccountLoader<'info, PerpMarket>, -} diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 1a3b6ce491..34b2abe837 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -2315,8 +2315,13 @@ pub fn handle_update_funding_rate( Some(state.oracle_guard_rails), )?; - let oracle_price_data = &oracle_map.get_price_data(&perp_market.oracle_id())?; - controller::repeg::_update_amm(perp_market, oracle_price_data, state, now, clock_slot)?; + let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id())?; + let mm_oracle_price_data = perp_market.get_mm_oracle_price_data( + *oracle_price_data, + clock_slot, + &state.oracle_guard_rails.validity, + )?; + controller::repeg::_update_amm(perp_market, &mm_oracle_price_data, state, now, clock_slot)?; validate!( matches!( @@ -2411,7 +2416,12 @@ pub fn handle_update_perp_bid_ask_twap<'c: 'info, 'info>( )?; let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id())?; - controller::repeg::_update_amm(perp_market, oracle_price_data, state, now, slot)?; + let mm_oracle_price_data = perp_market.get_mm_oracle_price_data( + *oracle_price_data, + slot, + &state.oracle_guard_rails.validity, + )?; + controller::repeg::_update_amm(perp_market, &mm_oracle_price_data, state, now, slot)?; let remaining_accounts_iter = &mut ctx.remaining_accounts.iter().peekable(); let makers = load_user_map(remaining_accounts_iter, false)?; @@ -2607,8 +2617,12 @@ pub fn handle_update_spot_market_cumulative_interest( )] pub fn handle_update_amms<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateAMM<'info>>, - market_indexes: [u16; 5], + market_indexes: Vec, ) -> Result<()> { + if market_indexes.len() > 5 { + msg!("Too many markets passed, max 5"); + return Err(ErrorCode::DefaultError.into()); + } // up to ~60k compute units (per amm) worst case let clock = Clock::get()?; @@ -2632,8 +2646,12 @@ pub fn handle_update_amms<'c: 'info, 'info>( )] pub fn view_amm_liquidity<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateAMM<'info>>, - market_indexes: [u16; 5], + market_indexes: Vec, ) -> Result<()> { + if market_indexes.len() > 5 { + msg!("Too many markets passed, max 5"); + return Err(ErrorCode::DefaultError.into()); + } // up to ~60k compute units (per amm) worst case let clock = Clock::get()?; diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 16e1d89fa4..49b75fe87d 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -34,6 +34,33 @@ pub mod state; mod test_utils; mod validation; +// main program entrypoint +// anchor `#[program]` entrypoint is compiled out by `no-entrypoint` +#[cfg(not(feature = "cpi"))] +solana_program::entrypoint!(program_entry); + +pub fn program_entry<'info>( + program_id: &Pubkey, + accounts: &'info [AccountInfo<'info>], + data: &[u8], +) -> anchor_lang::solana_program::entrypoint::ProgramResult { + if let [0xFF, 0xFF, 0xFF, 0xFF, discriminator, ref payload @ ..] = data { + match *discriminator { + 0 => Ok(handle_update_mm_oracle_native(accounts, payload)?), + 1 => Ok(handle_update_amm_spread_adjustment_native( + accounts, payload, + )?), + _ => Err( + anchor_lang::solana_program::program_error::ProgramError::InvalidInstructionData + .into(), + ), + } + } else { + // Fallback to anchor generated entry + entry(program_id, accounts, data) + } +} + #[cfg(feature = "mainnet-beta")] declare_id!("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"); #[cfg(not(feature = "mainnet-beta"))] @@ -703,7 +730,7 @@ pub mod drift { pub fn update_amms<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateAMM<'info>>, - market_indexes: [u16; 5], + market_indexes: Vec, ) -> Result<()> { handle_update_amms(ctx, market_indexes) } @@ -1783,8 +1810,15 @@ pub mod drift { handle_update_if_rebalance_config(ctx, params) } - pub fn zero_amm_fields_prep_mm_oracle_info(ctx: Context) -> Result<()> { - handle_zero_amm_fields_prep_mm_oracle_info(ctx) + pub fn update_disable_bitflags_mm_oracle( + ctx: Context, + disable: bool, + ) -> Result<()> { + handle_update_disable_bitflags_mm_oracle(ctx, disable) + } + + pub fn zero_mm_oracle_fields(ctx: Context) -> Result<()> { + handle_zero_mm_oracle_fields(ctx) } } diff --git a/programs/drift/src/macros.rs b/programs/drift/src/macros.rs index 4bd07c664a..cdd2004825 100644 --- a/programs/drift/src/macros.rs +++ b/programs/drift/src/macros.rs @@ -117,3 +117,15 @@ macro_rules! msg { (solana_program::msg!(&format!($($arg)*))); } } + +#[macro_export] +macro_rules! compute_fn { + ($msg:expr=> $($tt:tt)*) => { + ::solana_program::msg!(concat!($msg, " {")); + ::solana_program::log::sol_log_compute_units(); + let res = { $($tt)* }; + ::solana_program::log::sol_log_compute_units(); + ::solana_program::msg!(concat!(" } // ", $msg)); + res + }; +} diff --git a/programs/drift/src/math/amm.rs b/programs/drift/src/math/amm.rs index 2a98eac0cd..525082dd9d 100644 --- a/programs/drift/src/math/amm.rs +++ b/programs/drift/src/math/amm.rs @@ -11,12 +11,12 @@ use crate::math::constants::{ BID_ASK_SPREAD_PRECISION_I128, CONCENTRATION_PRECISION, DEFAULT_MAX_TWAP_UPDATE_PRICE_BAND_DENOMINATOR, FIVE_MINUTE, ONE_HOUR, ONE_MINUTE, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128, - PRICE_TO_PEG_PRECISION_RATIO, QUOTE_PRECISION_I64, + PRICE_TO_PEG_PRECISION_RATIO, }; use crate::math::orders::standardize_base_asset_amount; use crate::math::quote_asset::reserve_to_asset_amount; use crate::math::stats::{calculate_new_twap, calculate_rolling_sum, calculate_weighted_average}; -use crate::state::oracle::OraclePriceData; +use crate::state::oracle::{MMOraclePriceData, OraclePriceData}; use crate::state::perp_market::AMM; use crate::state::state::PriceDivergenceGuardRails; use crate::{validate, PERCENTAGE_PRECISION_U64}; @@ -400,7 +400,7 @@ pub fn sanitize_new_price( pub fn update_oracle_price_twap( amm: &mut AMM, now: i64, - oracle_price_data: &OraclePriceData, + mm_oracle_price_data: &MMOraclePriceData, precomputed_reserve_price: Option, sanitize_clamp: Option, ) -> DriftResult { @@ -408,8 +408,12 @@ pub fn update_oracle_price_twap( Some(reserve_price) => reserve_price, None => amm.reserve_price()?, }; - - let oracle_price = normalise_oracle_price(amm, oracle_price_data, Some(reserve_price))?; + let oracle_confidence = mm_oracle_price_data.get_confidence(); + let oracle_price = normalise_oracle_price( + amm, + &mm_oracle_price_data.get_exchange_oracle_price_data(), + Some(reserve_price), + )?; let capped_oracle_update_price = sanitize_new_price( oracle_price, @@ -435,15 +439,21 @@ pub fn update_oracle_price_twap( )?; amm.last_oracle_normalised_price = capped_oracle_update_price; - amm.historical_oracle_data.last_oracle_price = oracle_price_data.price; + amm.historical_oracle_data.last_oracle_price = + mm_oracle_price_data.get_exchange_oracle_price_data().price; + // Adjust confidence if the mm oracle and oracle price data are different by 5bps or more // use decayed last_oracle_conf_pct as lower bound amm.last_oracle_conf_pct = - amm.get_new_oracle_conf_pct(oracle_price_data.confidence, reserve_price, now)?; + amm.get_new_oracle_conf_pct(oracle_confidence, reserve_price, now)?; - amm.historical_oracle_data.last_oracle_delay = oracle_price_data.delay; - amm.last_oracle_reserve_price_spread_pct = - calculate_oracle_reserve_price_spread_pct(amm, oracle_price_data, Some(reserve_price))?; + amm.historical_oracle_data.last_oracle_delay = + mm_oracle_price_data.get_exchange_oracle_price_data().delay; + amm.last_oracle_reserve_price_spread_pct = calculate_oracle_reserve_price_spread_pct( + amm, + mm_oracle_price_data, + Some(reserve_price), + )?; // update std stat update_amm_oracle_std( @@ -712,7 +722,7 @@ pub fn calculate_terminal_price_and_reserves(amm: &AMM) -> DriftResult<(u64, u12 pub fn calculate_oracle_reserve_price_spread( amm: &AMM, - oracle_price_data: &OraclePriceData, + mm_oracle_price_data: &MMOraclePriceData, precomputed_reserve_price: Option, ) -> DriftResult<(i64, i64)> { let reserve_price = match precomputed_reserve_price { @@ -720,7 +730,7 @@ pub fn calculate_oracle_reserve_price_spread( None => amm.reserve_price()?.cast::()?, }; - let oracle_price = oracle_price_data.price; + let oracle_price = mm_oracle_price_data.get_price(); let price_spread = reserve_price.safe_sub(oracle_price)?; @@ -729,14 +739,10 @@ pub fn calculate_oracle_reserve_price_spread( pub fn normalise_oracle_price( amm: &AMM, - oracle_price: &OraclePriceData, + oracle_price_data: &OraclePriceData, precomputed_reserve_price: Option, ) -> DriftResult { - let OraclePriceData { - price: oracle_price, - confidence: oracle_conf, - .. - } = *oracle_price; + let oracle_price = oracle_price_data.price; let reserve_price = match precomputed_reserve_price { Some(reserve_price) => reserve_price.cast::()?, @@ -745,7 +751,7 @@ pub fn normalise_oracle_price( // 2.5 bps of the mark price let reserve_price_2p5_bps = reserve_price.safe_div(4000)?; - let conf_int = oracle_conf.cast::()?; + let conf_int = oracle_price_data.confidence.cast::()?; // normalises oracle toward mark price based on the oracle’s confidence interval // if mark above oracle: use oracle+conf unless it exceeds .99975 * mark price @@ -768,7 +774,7 @@ pub fn normalise_oracle_price( pub fn calculate_oracle_reserve_price_spread_pct( amm: &AMM, - oracle_price_data: &OraclePriceData, + mm_oracle_price_data: &MMOraclePriceData, precomputed_reserve_price: Option, ) -> DriftResult { let reserve_price = match precomputed_reserve_price { @@ -776,7 +782,7 @@ pub fn calculate_oracle_reserve_price_spread_pct( None => amm.reserve_price()?, }; let (_oracle_price, price_spread) = - calculate_oracle_reserve_price_spread(amm, oracle_price_data, Some(reserve_price))?; + calculate_oracle_reserve_price_spread(amm, mm_oracle_price_data, Some(reserve_price))?; price_spread .cast::()? diff --git a/programs/drift/src/math/amm/tests.rs b/programs/drift/src/math/amm/tests.rs index 32f4ff51dd..d8dfd37c72 100644 --- a/programs/drift/src/math/amm/tests.rs +++ b/programs/drift/src/math/amm/tests.rs @@ -3,6 +3,7 @@ use crate::math::constants::{ AMM_RESERVE_PRECISION, PEG_PRECISION, PRICE_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64, QUOTE_PRECISION, }; +use crate::math::oracle::OracleValidity; use crate::state::oracle::HistoricalOracleData; use crate::state::perp_market::PerpMarket; use crate::state::user::PerpPosition; @@ -483,8 +484,15 @@ fn calc_mark_std_tests() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 3, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); amm.peg_multiplier = px as u128; let trade_direction = PositionDirection::Long; @@ -598,6 +606,13 @@ fn update_mark_twap_tests() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 3, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); // $40 everything init let mut amm = AMM { @@ -622,7 +637,7 @@ fn update_mark_twap_tests() { ..AMM::default() }; - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); assert_eq!( amm.historical_oracle_data.last_oracle_price, oracle_price_data.price @@ -656,7 +671,7 @@ fn update_mark_twap_tests() { while now < 3600 { now += 1; - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); update_mark_twap_from_estimates( &mut amm, now, @@ -690,10 +705,17 @@ fn update_mark_twap_tests() { delay: 14, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 15, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); while now <= 3600 * 2 { now += 1; - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); if now % 200 == 0 { update_mark_twap_from_estimates( &mut amm, @@ -752,9 +774,16 @@ fn calc_oracle_twap_tests() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 2, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let _new_oracle_twap = - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); assert_eq!( amm.historical_oracle_data.last_oracle_price_twap, (34 * PRICE_PRECISION - PRICE_PRECISION / 100) as i64 @@ -770,9 +799,17 @@ fn calc_oracle_twap_tests() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 3, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); + // let old_oracle_twap_2 = amm.historical_oracle_data.last_oracle_price_twap; let _new_oracle_twap_2 = - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); assert_eq!(amm.historical_oracle_data.last_oracle_price_twap, 33940167); assert_eq!( amm.historical_oracle_data.last_oracle_price_twap_5min, @@ -781,7 +818,8 @@ fn calc_oracle_twap_tests() { assert_eq!(amm.oracle_std, 2_990_000); let _new_oracle_twap_2 = - update_oracle_price_twap(&mut amm, now + 60 * 5, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now + 60 * 5, &mm_oracle_price_data, None, None) + .unwrap(); assert_eq!(amm.historical_oracle_data.last_oracle_price_twap, 33695154); assert_eq!( @@ -796,10 +834,22 @@ fn calc_oracle_twap_tests() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 3, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); - let _new_oracle_twap_2 = - update_oracle_price_twap(&mut amm, now + 60 * 5 + 60, &oracle_price_data, None, None) - .unwrap(); + let _new_oracle_twap_2 = update_oracle_price_twap( + &mut amm, + now + 60 * 5 + 60, + &mm_oracle_price_data, + None, + None, + ) + .unwrap(); assert_eq!( amm.historical_oracle_data.last_oracle_price_twap_5min, 31200001 @@ -849,9 +899,16 @@ fn calc_oracle_twap_clamp_update_tests() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 2, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); while now < prev + 3600 { - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); now += 1; } assert_eq!( @@ -865,7 +922,7 @@ fn calc_oracle_twap_clamp_update_tests() { assert_eq!(amm.last_oracle_normalised_price, 24_188_600); while now < prev + 3600 * 2 { - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); now += 1; } @@ -880,7 +937,7 @@ fn calc_oracle_twap_clamp_update_tests() { assert_eq!(amm.last_oracle_normalised_price, 33_760_245); while now < prev + 3600 * 10 { - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); now += 1; } @@ -929,8 +986,15 @@ fn test_last_oracle_conf_update() { delay: 1, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 2, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); assert_eq!(amm.last_oracle_conf_pct, 7692); @@ -942,16 +1006,24 @@ fn test_last_oracle_conf_update() { has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + 2, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); + // unchanged if now hasnt changed - update_oracle_price_twap(&mut amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now, &mm_oracle_price_data, None, None).unwrap(); assert_eq!(amm.last_oracle_conf_pct, 7692); - update_oracle_price_twap(&mut amm, now + 1, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now + 1, &mm_oracle_price_data, None, None).unwrap(); assert_eq!(amm.last_oracle_conf_pct, 7692 - 7692 / 20); // 7287 // longer time between update means delay is faster - update_oracle_price_twap(&mut amm, now + 60, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut amm, now + 60, &mm_oracle_price_data, None, None).unwrap(); assert_eq!(amm.last_oracle_conf_pct, 7307 - 7307 / 5 + 1); //5847 } diff --git a/programs/drift/src/math/funding/tests.rs b/programs/drift/src/math/funding/tests.rs index 114159d752..704355bd07 100644 --- a/programs/drift/src/math/funding/tests.rs +++ b/programs/drift/src/math/funding/tests.rs @@ -1,7 +1,7 @@ use crate::controller::funding::update_funding_rate; use crate::controller::repeg::_update_amm; use crate::math::helpers::on_the_hour_update; -use crate::math::oracle::block_operation; +use crate::math::oracle::{block_operation, OracleValidity}; use crate::math::constants::{ AMM_RESERVE_PRECISION, ONE_HOUR_I128, PRICE_PRECISION, PRICE_PRECISION_U64, QUOTE_PRECISION, @@ -12,7 +12,7 @@ use std::cmp::min; use crate::test_utils::get_pyth_price; // use crate::create_anchor_account_info; -use crate::state::oracle::HistoricalOracleData; +use crate::state::oracle::{HistoricalOracleData, MMOraclePriceData}; use crate::state::oracle_map::OracleMap; use crate::state::perp_market::{ContractTier, PerpMarket, AMM}; use crate::state::state::{OracleGuardRails, State, ValidityGuardRails}; @@ -653,6 +653,13 @@ fn unsettled_funding_pnl() { ) .unwrap(); let oracle_price_data = oracle_map.get_price_data(&market.oracle_id()).unwrap(); + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + *oracle_price_data, + ) + .unwrap(); assert_eq!(time_until_next_update, 0); let block_funding_rate_update = block_operation( @@ -669,7 +676,7 @@ fn unsettled_funding_pnl() { now += 3600; slot += 3600 * 2; - let cost = _update_amm(&mut market, oracle_price_data, &state, now, slot).unwrap(); + let cost = _update_amm(&mut market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert_eq!(cost, 0); assert_eq!(market.amm.last_update_slot, slot); assert_eq!(market.amm.last_mark_price_twap, 50000000); diff --git a/programs/drift/src/math/oracle.rs b/programs/drift/src/math/oracle.rs index 13d5da5fa0..78be9ce782 100644 --- a/programs/drift/src/math/oracle.rs +++ b/programs/drift/src/math/oracle.rs @@ -69,6 +69,7 @@ pub enum DriftAction { UpdateTwap, UpdateAMMCurve, OracleOrderPrice, + UseMMOraclePrice, } pub fn is_oracle_valid_for_action( @@ -128,6 +129,10 @@ pub fn is_oracle_valid_for_action( ), DriftAction::UpdateTwap => !matches!(oracle_validity, OracleValidity::NonPositive), DriftAction::UpdateAMMCurve => !matches!(oracle_validity, OracleValidity::NonPositive), + DriftAction::UseMMOraclePrice => !matches!( + oracle_validity, + OracleValidity::NonPositive | OracleValidity::TooVolatile, + ), }, None => { matches!(oracle_validity, OracleValidity::Valid) @@ -187,7 +192,7 @@ pub fn get_oracle_status( &guard_rails.validity, market.get_max_confidence_interval_multiplier()?, &market.amm.oracle_source, - false, + LogMode::None, 0, )?; let oracle_reserve_price_spread_pct = @@ -205,6 +210,14 @@ pub fn get_oracle_status( }) } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LogMode { + None, + ExchangeOracle, + MMOracle, + SafeMMOracle, +} + pub fn oracle_validity( market_type: MarketType, market_index: u16, @@ -213,7 +226,7 @@ pub fn oracle_validity( valid_oracle_guard_rails: &ValidityGuardRails, max_confidence_interval_multiplier: u64, oracle_source: &OracleSource, - log_validity: bool, + log_mode: LogMode, slots_before_stale_for_amm_override: i8, ) -> DriftResult { let OraclePriceData { @@ -272,28 +285,38 @@ pub fn oracle_validity( OracleValidity::Valid }; - if log_validity { + if log_mode != LogMode::None { + let oracle_type = if log_mode == LogMode::ExchangeOracle { + "Exchange" + } else if log_mode == LogMode::SafeMMOracle { + "SafeMM" + } else { + "MM" + }; if !has_sufficient_number_of_data_points { crate::msg!( - "Invalid {} {} Oracle: Insufficient Data Points", + "Invalid {} {} {} Oracle: Insufficient Data Points", market_type, - market_index + market_index, + oracle_type ); } if is_oracle_price_nonpositive { crate::msg!( - "Invalid {} {} Oracle: Non-positive (oracle_price <=0)", + "Invalid {} {} {} Oracle: Non-positive (oracle_price <=0)", market_type, - market_index + market_index, + oracle_type ); } if is_oracle_price_too_volatile { crate::msg!( - "Invalid {} {} Oracle: Too Volatile (last_oracle_price_twap={:?} vs oracle_price={:?})", + "Invalid {} {} {} Oracle: Too Volatile (last_oracle_price_twap={:?} vs oracle_price={:?})", market_type, market_index, + oracle_type, last_oracle_twap, oracle_price, ); @@ -301,18 +324,20 @@ pub fn oracle_validity( if is_conf_too_large { crate::msg!( - "Invalid {} {} Oracle: Confidence Too Large (is_conf_too_large={:?})", + "Invalid {} {} {} Oracle: Confidence Too Large (is_conf_too_large={:?})", market_type, market_index, + oracle_type, conf_pct_of_price ); } if is_stale_for_amm || is_stale_for_margin { crate::msg!( - "Invalid {} {} Oracle: Stale (oracle_delay={:?})", + "Invalid {} {} {} Oracle: Stale (oracle_delay={:?})", market_type, market_index, + oracle_type, oracle_delay ); } diff --git a/programs/drift/src/math/oracle/tests.rs b/programs/drift/src/math/oracle/tests.rs index d8906c860e..2295e15288 100644 --- a/programs/drift/src/math/oracle/tests.rs +++ b/programs/drift/src/math/oracle/tests.rs @@ -11,6 +11,7 @@ use crate::state::state::{OracleGuardRails, PriceDivergenceGuardRails, State, Va fn calculate_oracle_valid() { let prev = 1656682258; let now = prev + 3600; + let state = State::default(); let px = 32 * PRICE_PRECISION; let amm = AMM { @@ -39,6 +40,9 @@ fn calculate_oracle_valid() { contract_tier: ContractTier::B, ..PerpMarket::default() }; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, 10000, &state.oracle_guard_rails.validity) + .unwrap(); let state = State { oracle_guard_rails: OracleGuardRails { @@ -69,7 +73,7 @@ fn calculate_oracle_valid() { assert!(!oracle_status.mark_too_divergent); let _new_oracle_twap = - update_oracle_price_twap(&mut market.amm, now, &oracle_price_data, None, None).unwrap(); + update_oracle_price_twap(&mut market.amm, now, &mm_oracle_price_data, None, None).unwrap(); assert_eq!( market.amm.historical_oracle_data.last_oracle_price_twap, (34 * PRICE_PRECISION - PRICE_PRECISION / 100) as i64 diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index fa329750a5..6b3b3c88fe 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -7,9 +7,7 @@ use crate::controller::position::PositionDelta; use crate::controller::position::PositionDirection; use crate::error::{DriftResult, ErrorCode}; use crate::math::amm::calculate_amm_available_liquidity; -use crate::math::auction::is_amm_available_liquidity_source; use crate::math::casting::Cast; -use crate::state::fill_mode::FillMode; use crate::state::protected_maker_mode_config::ProtectedMakerParams; use crate::state::user::OrderBitFlag; use crate::{ diff --git a/programs/drift/src/math/repeg.rs b/programs/drift/src/math/repeg.rs index 725e404d5f..0255ac452e 100644 --- a/programs/drift/src/math/repeg.rs +++ b/programs/drift/src/math/repeg.rs @@ -1,6 +1,7 @@ use std::cmp::{max, min}; use crate::msg; +use crate::state::oracle::MMOraclePriceData; use anchor_lang::prelude::AccountInfo; use crate::error::*; @@ -44,7 +45,7 @@ pub fn calculate_repeg_validity_from_oracle_account( &oracle_guard_rails.validity, market.get_max_confidence_interval_multiplier()?, &market.amm.oracle_source, - true, + oracle::LogMode::ExchangeOracle, 0, )? == OracleValidity::Valid; @@ -345,13 +346,13 @@ pub fn adjust_amm( pub fn calculate_optimal_peg_and_budget( market: &PerpMarket, - oracle_price_data: &OraclePriceData, + mm_oracle_price_data: &MMOraclePriceData, ) -> DriftResult<(u128, u128, bool)> { let reserve_price_before = market.amm.reserve_price()?; let mut fee_budget = calculate_fee_pool(market)?; - let target_price_i64 = oracle_price_data.price; + let target_price_i64 = mm_oracle_price_data.get_price(); let target_price = target_price_i64.cast()?; let mut optimal_peg = calculate_peg_from_target_price( market.amm.quote_asset_reserve, diff --git a/programs/drift/src/math/repeg/tests.rs b/programs/drift/src/math/repeg/tests.rs index ed62793591..2407675814 100644 --- a/programs/drift/src/math/repeg/tests.rs +++ b/programs/drift/src/math/repeg/tests.rs @@ -63,9 +63,16 @@ fn calculate_optimal_peg_and_budget_test() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 13110290527); assert_eq!(optimal_peg > oracle_price_data.price as u128, true); @@ -79,8 +86,15 @@ fn calculate_optimal_peg_and_budget_test() { delay: 21, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 19496270752); assert_eq!(budget, 39500000); @@ -93,8 +107,15 @@ fn calculate_optimal_peg_and_budget_test() { delay: 21, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 19186822509); assert_eq!(budget, 39500000); @@ -107,8 +128,15 @@ fn calculate_optimal_peg_and_budget_test() { delay: 21, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 21042480468); assert_eq!(budget, 39500000); @@ -121,8 +149,15 @@ fn calculate_optimal_peg_and_budget_test() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 43735351562); assert_eq!(budget, 39500000); @@ -152,8 +187,15 @@ fn calculate_optimal_peg_and_budget_test() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 42641967773); assert_eq!(budget, 22190932405); // $2219.032405 @@ -205,9 +247,16 @@ fn calculate_optimal_peg_and_budget_2_test() { delay: 2, has_sufficient_number_of_data_points: true, }; + let mm_oracle_price_data = MMOraclePriceData::new( + oracle_price_data.price, + oracle_price_data.delay + 1, + OracleValidity::default(), + oracle_price_data, + ) + .unwrap(); let (optimal_peg, budget, check_lb) = - calculate_optimal_peg_and_budget(&market, &oracle_price_data).unwrap(); + calculate_optimal_peg_and_budget(&market, &mm_oracle_price_data).unwrap(); assert_eq!(optimal_peg, 17796790576); assert_eq!(optimal_peg > oracle_price_data.price as u128, false); @@ -233,7 +282,7 @@ fn calculate_optimal_peg_and_budget_2_test() { // test amm update assert_eq!(market.amm.last_update_slot, 0); - let c = _update_amm(&mut market, &oracle_price_data, &state, 1, 1337).unwrap(); + let c = _update_amm(&mut market, &mm_oracle_price_data, &state, 1, 1337).unwrap(); assert!(market.amm.is_recent_oracle_valid(1337).unwrap()); assert!(!market.amm.is_recent_oracle_valid(1338).unwrap()); assert!(!market.amm.is_recent_oracle_valid(1336).unwrap()); diff --git a/programs/drift/src/state/oracle.rs b/programs/drift/src/state/oracle.rs index b1b328dba7..2feebb5a23 100644 --- a/programs/drift/src/state/oracle.rs +++ b/programs/drift/src/state/oracle.rs @@ -3,12 +3,15 @@ use std::cell::Ref; use crate::error::{DriftResult, ErrorCode}; use crate::math::casting::Cast; -use crate::math::constants::{PRICE_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64}; +use crate::math::constants::{ + PERCENTAGE_PRECISION, PRICE_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64, +}; use crate::math::safe_math::SafeMath; use switchboard::{AggregatorAccountData, SwitchboardDecimal}; use switchboard_on_demand::{PullFeedAccountData, SB_ON_DEMAND_PRECISION}; use crate::error::ErrorCode::{InvalidOracle, UnableToLoadOracle}; +use crate::math::oracle::{is_oracle_valid_for_action, DriftAction, OracleValidity}; use crate::math::safe_unwrap::SafeUnwrap; use crate::state::load_ref::load_ref; use crate::state::perp_market::PerpMarket; @@ -169,6 +172,111 @@ impl OracleSource { } } +const MM_EXCHANGE_FALLBACK_THRESHOLD: u128 = PERCENTAGE_PRECISION / 100; // 1% +#[derive(Default, Clone, Copy, Debug)] +pub struct MMOraclePriceData { + mm_oracle_price: i64, + mm_oracle_delay: i64, + mm_oracle_validity: OracleValidity, + mm_exchange_diff_bps: u128, + exchange_oracle_price_data: OraclePriceData, + safe_oracle_price_data: OraclePriceData, +} + +impl MMOraclePriceData { + pub fn new( + mm_oracle_price: i64, + mm_oracle_delay: i64, + mm_oracle_validity: OracleValidity, + oracle_price_data: OraclePriceData, + ) -> DriftResult { + let price_diff = mm_oracle_price.safe_sub(oracle_price_data.price)?; + let price_diff_bps = price_diff + .abs() + .cast::()? + .safe_mul(PERCENTAGE_PRECISION)? + .safe_div(oracle_price_data.price.abs().max(1).cast::()?)?; + let safe_oracle_price_data = if mm_oracle_delay > oracle_price_data.delay + || mm_oracle_price == 0i64 + || !is_oracle_valid_for_action(mm_oracle_validity, Some(DriftAction::UseMMOraclePrice))? + || price_diff_bps > MM_EXCHANGE_FALLBACK_THRESHOLD + // 1% price difference + { + oracle_price_data + } else { + let mm_oracle_diff_premium = mm_oracle_price.abs_diff(oracle_price_data.price); + let adjusted_confidence = oracle_price_data + .confidence + .safe_add(mm_oracle_diff_premium)?; + + OraclePriceData { + price: mm_oracle_price, + confidence: adjusted_confidence, + delay: mm_oracle_delay, + has_sufficient_number_of_data_points: true, + } + }; + + Ok(MMOraclePriceData { + mm_oracle_price, + mm_oracle_delay, + mm_oracle_validity, + mm_exchange_diff_bps: price_diff_bps, + exchange_oracle_price_data: oracle_price_data, + safe_oracle_price_data, + }) + } + + pub fn get_price(&self) -> i64 { + self.safe_oracle_price_data.price + } + + pub fn get_delay(&self) -> i64 { + self.safe_oracle_price_data.delay + } + + pub fn get_confidence(&self) -> u64 { + self.safe_oracle_price_data.confidence + } + + pub fn get_safe_oracle_price_data(&self) -> OraclePriceData { + self.safe_oracle_price_data + } + + pub fn get_exchange_oracle_price_data(&self) -> OraclePriceData { + self.exchange_oracle_price_data + } + + pub fn get_mm_oracle_validity(&self) -> OracleValidity { + self.mm_oracle_validity + } + + pub fn get_mm_exchange_diff_bps(&self) -> u128 { + self.mm_exchange_diff_bps + } + + pub fn is_mm_exchange_diff_bps_high(&self) -> bool { + self.mm_exchange_diff_bps > MM_EXCHANGE_FALLBACK_THRESHOLD + } + + pub fn is_mm_oracle_as_recent(&self) -> bool { + self.mm_oracle_delay <= self.safe_oracle_price_data.delay + } + + pub fn is_enabled(&self) -> bool { + self.mm_oracle_price != 0 && self.mm_oracle_delay >= 0 + } + + // For potential future observability + pub fn _get_mm_oracle_price(&self) -> i64 { + self.mm_oracle_price + } + + pub fn get_mm_oracle_delay(&self) -> i64 { + self.mm_oracle_delay + } +} + #[derive(Default, Clone, Copy, Debug)] pub struct OraclePriceData { pub price: i64, @@ -177,17 +285,6 @@ pub struct OraclePriceData { pub has_sufficient_number_of_data_points: bool, } -impl OraclePriceData { - pub fn default_usd() -> Self { - OraclePriceData { - price: PRICE_PRECISION_I64, - confidence: 1, - delay: 0, - has_sufficient_number_of_data_points: true, - } - } -} - pub fn get_oracle_price( oracle_source: &OracleSource, price_oracle: &AccountInfo, diff --git a/programs/drift/src/state/oracle/tests.rs b/programs/drift/src/state/oracle/tests.rs index 0b737c6c31..62d3525b57 100644 --- a/programs/drift/src/state/oracle/tests.rs +++ b/programs/drift/src/state/oracle/tests.rs @@ -2,9 +2,11 @@ use solana_program::pubkey::Pubkey; use std::str::FromStr; use crate::create_account_info; -use crate::state::oracle::{get_oracle_price, OracleSource}; +use crate::math::constants::{AMM_RESERVE_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64}; +use crate::state::oracle::{get_oracle_price, HistoricalOracleData, OraclePriceData, OracleSource}; use crate::state::oracle_map::OracleMap; -use crate::state::perp_market::AMM; +use crate::state::perp_market::{PerpMarket, AMM}; +use crate::state::state::State; use crate::test_utils::*; #[test] @@ -157,3 +159,109 @@ fn oracle_map_diff_oracle_source() { .unwrap(); assert_eq!(oracle_price_data.price, 34); } + +#[test] +fn use_mm_oracle() { + let slot = 303030303; + let oracle_price_data = OraclePriceData { + price: 130 * PRICE_PRECISION_I64 + 873, + confidence: PRICE_PRECISION_U64 / 10, + delay: 1, + has_sufficient_number_of_data_points: true, + }; + let mut market = PerpMarket { + market_index: 0, + amm: AMM { + base_asset_reserve: 512295081967, + quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, + sqrt_k: 500 * AMM_RESERVE_PRECISION, + peg_multiplier: 22_100_000_000, + base_asset_amount_with_amm: (12295081967_i128), + max_spread: 1000, + mm_oracle_price: 130 * PRICE_PRECISION_I64 + 973, + mm_oracle_slot: slot, + historical_oracle_data: HistoricalOracleData::default_with_current_oracle( + oracle_price_data, + ), + // assume someone else has other half same entry, + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + imf_factor: 1000, // 1_000/1_000_000 = .001 + unrealized_pnl_initial_asset_weight: 100, + unrealized_pnl_maintenance_asset_weight: 100, + ..PerpMarket::default() + }; + let state = State::default(); + + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + + // Use the MM oracle when it's recent and it's valid to use + assert_eq!( + mm_oracle_price_data.get_price(), + mm_oracle_price_data.mm_oracle_price + ); + assert_eq!( + mm_oracle_price_data.get_delay(), + mm_oracle_price_data.mm_oracle_delay + ); + + // Update the MM oracle slot to be delayed, should use oracle price data + market.amm.mm_oracle_slot = slot - 5; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + assert_eq!(mm_oracle_price_data.get_price(), oracle_price_data.price); + assert_eq!(mm_oracle_price_data.get_delay(), oracle_price_data.delay,); +} + +#[test] +fn mm_oracle_confidence() { + let slot = 303030303; + let oracle_price_data = OraclePriceData { + price: 130 * PRICE_PRECISION_I64 + 873, + confidence: PRICE_PRECISION_U64 / 10, + delay: 1, + has_sufficient_number_of_data_points: true, + }; + let market = PerpMarket { + market_index: 0, + amm: AMM { + base_asset_reserve: 512295081967, + quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, + sqrt_k: 500 * AMM_RESERVE_PRECISION, + peg_multiplier: 22_100_000_000, + base_asset_amount_with_amm: (12295081967_i128), + max_spread: 1000, + mm_oracle_price: 130 * PRICE_PRECISION_I64 + 999, + mm_oracle_slot: slot, + historical_oracle_data: HistoricalOracleData::default_with_current_oracle( + oracle_price_data, + ), + // assume someone else has other half same entry, + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + imf_factor: 1000, // 1_000/1_000_000 = .001 + unrealized_pnl_initial_asset_weight: 100, + unrealized_pnl_maintenance_asset_weight: 100, + ..PerpMarket::default() + }; + let state = State::default(); + + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + + let expected_confidence = oracle_price_data.confidence + + (mm_oracle_price_data._get_mm_oracle_price() + - mm_oracle_price_data.get_exchange_oracle_price_data().price) + .abs() as u64; + + let confidence = mm_oracle_price_data.get_confidence(); + assert_eq!(confidence, expected_confidence); +} diff --git a/programs/drift/src/state/oracle_map.rs b/programs/drift/src/state/oracle_map.rs index 9f70ec4a92..64d979315c 100644 --- a/programs/drift/src/state/oracle_map.rs +++ b/programs/drift/src/state/oracle_map.rs @@ -4,7 +4,7 @@ use crate::ids::{ drift_oracle_receiver_program, pyth_program, switchboard_on_demand, switchboard_program, }; use crate::math::constants::PRICE_PRECISION_I64; -use crate::math::oracle::{oracle_validity, OracleValidity}; +use crate::math::oracle::{oracle_validity, LogMode, OracleValidity}; use crate::msg; use crate::state::oracle::{get_oracle_price, OraclePriceData, OracleSource, PrelaunchOracle}; use crate::state::state::OracleGuardRails; @@ -108,7 +108,7 @@ impl<'a> OracleMap<'a> { &self.oracle_guard_rails.validity, max_confidence_interval_multiplier, &oracle_id.1, - true, + LogMode::ExchangeOracle, slots_before_stale_for_amm_override, )?; self.validity.insert(*oracle_id, oracle_validity); @@ -138,7 +138,7 @@ impl<'a> OracleMap<'a> { &self.oracle_guard_rails.validity, max_confidence_interval_multiplier, &oracle_id.1, - true, + LogMode::ExchangeOracle, slots_before_stale_for_amm_override, )?; self.validity.insert(*oracle_id, oracle_validity); diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 6fbad9209b..249f986541 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -1,7 +1,8 @@ use crate::state::pyth_lazer_oracle::PythLazerOracle; +use crate::state::user::MarketType; use anchor_lang::prelude::*; -use crate::state::state::State; +use crate::state::state::{State, ValidityGuardRails}; use std::cmp::max; use crate::controller::position::{PositionDelta, PositionDirection}; @@ -33,7 +34,7 @@ use num_integer::Roots; use crate::state::oracle::{ get_prelaunch_price, get_sb_on_demand_price, get_switchboard_price, HistoricalOracleData, - OracleSource, + MMOraclePriceData, OraclePriceData, OracleSource, }; use crate::state::spot_market::{AssetTier, SpotBalance, SpotBalanceType}; use crate::state::traits::{MarketIndexOffset, Size}; @@ -45,6 +46,7 @@ use static_assertions::const_assert_eq; use super::oracle_map::OracleIdentifier; use super::protected_maker_mode_config::ProtectedMakerParams; +use crate::math::oracle::{oracle_validity, LogMode, OracleValidity}; #[cfg(test)] mod tests; @@ -747,6 +749,44 @@ impl PerpMarket { default_min_auction_duration } } + + pub fn get_mm_oracle_price_data( + &self, + oracle_price_data: OraclePriceData, + clock_slot: u64, + oracle_guard_rails: &ValidityGuardRails, + ) -> DriftResult { + let delay = clock_slot + .cast::()? + .safe_sub(self.amm.mm_oracle_slot.cast::()?)?; + let oracle_data = OraclePriceData { + price: self.amm.mm_oracle_price, + delay, + confidence: oracle_price_data.confidence, + has_sufficient_number_of_data_points: true, + }; + let oracle_validity = if self.amm.mm_oracle_price == 0 { + OracleValidity::NonPositive + } else { + oracle_validity( + MarketType::Perp, + self.market_index, + self.amm.historical_oracle_data.last_oracle_price_twap, + &oracle_data, + &oracle_guard_rails, + self.get_max_confidence_interval_multiplier()?, + &self.amm.oracle_source, + LogMode::MMOracle, + self.amm.oracle_slot_delay_override, + )? + }; + Ok(MMOraclePriceData::new( + self.amm.mm_oracle_price, + delay, + oracle_validity, + oracle_price_data, + )?) + } } #[cfg(test)] @@ -998,7 +1038,7 @@ pub struct AMM { pub min_order_size: u64, /// the max base size a single user can have /// precision: BASE_PRECISION - pub max_position_size: u64, + pub mm_oracle_slot: u64, /// estimated total of volume in market /// QUOTE_PRECISION pub volume_24h: u64, @@ -1024,10 +1064,8 @@ pub struct AMM { pub long_spread: u32, /// the spread for bids vs the reserve price pub short_spread: u32, - /// the count intensity of long fills against AMM - pub long_intensity_count: u32, - /// the count intensity of short fills against AMM - pub short_intensity_count: u32, + /// MM oracle price + pub mm_oracle_price: i64, /// the fraction of total available liquidity a single fill on the AMM can consume pub max_fill_reserve_fraction: u16, /// the maximum slippage a single fill on the AMM can push @@ -1052,7 +1090,7 @@ pub struct AMM { /// signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double) pub amm_spread_adjustment: i8, pub oracle_slot_delay_override: i8, - pub total_fee_earned_per_lp: u64, + pub mm_oracle_sequence_id: u64, pub net_unsettled_funding_pnl: i64, pub quote_asset_amount_with_unsettled_lp: i64, pub reference_price_offset: i32, @@ -1119,7 +1157,6 @@ impl Default for AMM { order_step_size: 0, order_tick_size: 0, min_order_size: 1, - max_position_size: 0, volume_24h: 0, long_intensity_volume: 0, short_intensity_volume: 0, @@ -1131,8 +1168,8 @@ impl Default for AMM { max_spread: 0, long_spread: 0, short_spread: 0, - long_intensity_count: 0, - short_intensity_count: 0, + mm_oracle_price: 0, + mm_oracle_slot: 0, max_fill_reserve_fraction: 0, max_slippage_ratio: 0, curve_update_intensity: 0, @@ -1144,7 +1181,7 @@ impl Default for AMM { taker_speed_bump_override: 0, amm_spread_adjustment: 0, oracle_slot_delay_override: 0, - total_fee_earned_per_lp: 0, + mm_oracle_sequence_id: 0, net_unsettled_funding_pnl: 0, quote_asset_amount_with_unsettled_lp: 0, reference_price_offset: 0, @@ -1418,7 +1455,6 @@ impl AMM { pub fn bid_price(&self, reserve_price: u64) -> DriftResult { let adjusted_spread = (-(self.short_spread.cast::()?)).safe_add(self.reference_price_offset)?; - let multiplier = BID_ASK_SPREAD_PRECISION_I128.safe_add(adjusted_spread.cast::()?)?; reserve_price @@ -1639,6 +1675,16 @@ impl AMM { pub fn is_recent_oracle_valid(&self, current_slot: u64) -> DriftResult { Ok(self.last_oracle_valid && current_slot == self.last_update_slot) } + + pub fn update_mm_oracle_info( + &mut self, + mm_oracle_price: i64, + mm_oracle_slot: u64, + ) -> DriftResult { + self.mm_oracle_price = mm_oracle_price; + self.mm_oracle_slot = mm_oracle_slot; + Ok(()) + } } #[cfg(test)] diff --git a/programs/drift/src/state/perp_market_map.rs b/programs/drift/src/state/perp_market_map.rs index bedadbc1ce..73c6d37d9d 100644 --- a/programs/drift/src/state/perp_market_map.rs +++ b/programs/drift/src/state/perp_market_map.rs @@ -238,12 +238,9 @@ pub fn get_writable_perp_market_set_from_vec(market_indexes: &[u16]) -> MarketSe writable_markets } -pub fn get_market_set_from_list(market_indexes: [u16; 5]) -> MarketSet { +pub fn get_market_set_from_list(market_indexes: Vec) -> MarketSet { let mut writable_markets = MarketSet::new(); for market_index in market_indexes.iter() { - if *market_index == 100 { - continue; // todo - } writable_markets.insert(*market_index); } writable_markets diff --git a/programs/drift/src/state/state.rs b/programs/drift/src/state/state.rs index 3af765c7e0..de763c2a7f 100644 --- a/programs/drift/src/state/state.rs +++ b/programs/drift/src/state/state.rs @@ -41,7 +41,8 @@ pub struct State { pub initial_pct_to_liquidate: u16, pub max_number_of_sub_accounts: u16, pub max_initialize_user_fee: u16, - pub padding: [u8; 10], + pub disable_bit_flags: u8, + pub padding: [u8; 9], } #[derive(BitFlags, Clone, Copy, PartialEq, Debug, Eq)] diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index 7bb0c2293e..5e99f564bc 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -4552,26 +4552,58 @@ export class AdminClient extends DriftClient { }); } - public async zeroAmmFieldsPrepMmOracleInfo( - marketIndex: number + public async updateDisableBitFlagsMMOracle( + disable: boolean ): Promise { - const zeroAmmFieldsPrepMmOracleInfoIx = - await this.getZeroAmmFieldsPrepMmOracleInfoIx(marketIndex); - - const tx = await this.buildTransaction(zeroAmmFieldsPrepMmOracleInfoIx); + const updateDisableBitFlagsMMOracleIx = + await this.getUpdateDisableBitFlagsMMOracleIx(disable); + const tx = await this.buildTransaction(updateDisableBitFlagsMMOracleIx); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; } + public async getUpdateDisableBitFlagsMMOracleIx( + disable: boolean + ): Promise { + return await this.program.instruction.updateDisableBitflagsMmOracle( + disable, + { + accounts: { + admin: this.isSubscribed + ? this.getStateAccount().admin + : this.wallet.publicKey, + state: await this.getStatePublicKey(), + }, + } + ); + } + + public async zeroMMOracleFields( + marketIndex: number + ): Promise { + const zeroMMOracleFieldsIx = await this.getZeroMMOracleFieldsIx( + marketIndex + ); + + const tx = await this.buildTransaction(zeroMMOracleFieldsIx); + const { txSig } = await this.sendTransaction(tx, [], this.opts); - public async getZeroAmmFieldsPrepMmOracleInfoIx( + return txSig; + } + public async getZeroMMOracleFieldsIx( marketIndex: number ): Promise { - return await this.program.instruction.zeroAmmFieldsPrepMmOracleInfo({ + return await this.program.instruction.zeroMmOracleFields({ accounts: { - admin: this.wallet.publicKey, - perpMarket: this.getPerpMarketAccount(marketIndex).pubkey, + admin: this.isSubscribed + ? this.getStateAccount().admin + : this.wallet.publicKey, + state: await this.getStatePublicKey(), + perpMarket: await getPerpMarketPublicKey( + this.program.programId, + marketIndex + ), }, }); } diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index b7a78062d8..e8db6a24f8 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -78,6 +78,7 @@ import { PublicKey, Signer, SystemProgram, + SYSVAR_CLOCK_PUBKEY, SYSVAR_INSTRUCTIONS_PUBKEY, Transaction, TransactionInstruction, @@ -177,7 +178,10 @@ import { WormholeCoreBridgeSolana } from '@pythnetwork/pyth-solana-receiver/lib/ import { PythSolanaReceiver } from '@pythnetwork/pyth-solana-receiver/lib/idl/pyth_solana_receiver'; import { getFeedIdUint8Array, trimFeedId } from './util/pythOracleUtils'; import { createMinimalEd25519VerifyIx } from './util/ed25519Utils'; -import { isVersionedTransaction } from './tx/utils'; +import { + createNativeInstructionDiscriminatorBuffer, + isVersionedTransaction, +} from './tx/utils'; import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json'; import { asV0Tx, PullFeed, AnchorUtils } from '@switchboard-xyz/on-demand'; import { gprcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber'; @@ -186,6 +190,7 @@ import { Slothash } from './slot/SlothashSubscriber'; import { getOracleId } from './oracles/oracleId'; import { SignedMsgOrderParams } from './types'; import { sha256 } from '@noble/hashes/sha256'; +import { getOracleConfidenceFromMMOracleData } from './oracles/utils'; type RemainingAccountParams = { userAccounts: UserAccount[]; @@ -8485,6 +8490,37 @@ export class DriftClient { ).data; } + public getMMOracleDataForPerpMarket(marketIndex: number): OraclePriceData { + const perpMarket = this.getPerpMarketAccount(marketIndex); + const oracleData = this.getOracleDataForPerpMarket(marketIndex); + const stateAccountAndSlot = this.accountSubscriber.getStateAccountAndSlot(); + if ( + !isOracleValid( + perpMarket, + oracleData, + stateAccountAndSlot.data.oracleGuardRails, + stateAccountAndSlot.slot + ) || + perpMarket.amm.mmOraclePrice.eq(ZERO) || + perpMarket.amm.mmOracleSlot < oracleData.slot + ) { + return { ...oracleData, fetchedWithMMOracle: true }; + } else { + const conf = getOracleConfidenceFromMMOracleData({ + mmOraclePrice: perpMarket.amm.mmOraclePrice, + mmOracleSlot: perpMarket.amm.mmOracleSlot, + oraclePriceData: oracleData, + }); + return { + price: perpMarket.amm.mmOraclePrice, + slot: perpMarket.amm.mmOracleSlot, + confidence: conf, + hasSufficientNumberOfDataPoints: true, + fetchedWithMMOracle: true, + }; + } + } + public getOracleDataForSpotMarket(marketIndex: number): OraclePriceData { return this.accountSubscriber.getOraclePriceDataAndSlotForSpotMarket( marketIndex @@ -9909,6 +9945,112 @@ export class DriftClient { return txSig; } + public async updateMmOracleNative( + marketIndex: number, + oraclePrice: BN, + oracleSequenceId: BN + ): Promise { + const updateMmOracleIx = await this.getUpdateMmOracleNativeIx( + marketIndex, + oraclePrice, + oracleSequenceId + ); + + const tx = await this.buildTransaction(updateMmOracleIx, { + computeUnits: 5000, + computeUnitsPrice: 0, + }); + const { txSig } = await this.sendTransaction(tx, [], this.opts); + + return txSig; + } + + public async getUpdateMmOracleNativeIx( + marketIndex: number, + oraclePrice: BN, + oracleSequenceId: BN + ): Promise { + const discriminatorBuffer = createNativeInstructionDiscriminatorBuffer(0); + const data = Buffer.alloc(discriminatorBuffer.length + 16); + data.set(discriminatorBuffer, 0); + data.set(oraclePrice.toArrayLike(Buffer, 'le', 8), 5); // next 8 bytes + data.set(oracleSequenceId.toArrayLike(Buffer, 'le', 8), 13); // next 8 bytes + + // Build the instruction manually + return new TransactionInstruction({ + programId: this.program.programId, + keys: [ + { + pubkey: this.getPerpMarketAccount(marketIndex).pubkey, + isWritable: true, + isSigner: false, + }, + { + pubkey: this.wallet.publicKey, + isWritable: false, + isSigner: true, + }, + { + pubkey: SYSVAR_CLOCK_PUBKEY, + isWritable: false, + isSigner: false, + }, + { + pubkey: await this.getStatePublicKey(), + isWritable: false, + isSigner: false, + }, + ], + data, + }); + } + + public async updateAmmSpreadAdjustmentNative( + marketIndex: number, + ammSpreadAdjustment: number + ): Promise { + const updateMmOracleIx = await this.getUpdateAmmSpreadAdjustmentNativeIx( + marketIndex, + ammSpreadAdjustment + ); + + const tx = await this.buildTransaction(updateMmOracleIx, { + computeUnits: 1000, + computeUnitsPrice: 0, + }); + const { txSig } = await this.sendTransaction(tx, [], this.opts); + + return txSig; + } + + public getUpdateAmmSpreadAdjustmentNativeIx( + marketIndex: number, + ammSpreadAdjustment: number // i8 + ): TransactionInstruction { + const discriminatorBuffer = createNativeInstructionDiscriminatorBuffer(1); + const data = Buffer.alloc(discriminatorBuffer.length + 4); + data.set(discriminatorBuffer, 0); + data.writeInt8(ammSpreadAdjustment, 5); // next byte + + // Build the instruction manually + return new TransactionInstruction({ + programId: this.program.programId, + keys: [ + { + pubkey: this.getPerpMarketAccount(marketIndex).pubkey, + isWritable: true, + isSigner: false, + }, + { + pubkey: this.wallet.publicKey, + isWritable: false, + isSigner: true, + }, + ], + data, + }); + } + private handleSignedTransaction(signedTxs: SignedTxData[]) { if (this.enableMetricsEvents && this.metricsEventEmitter) { this.metricsEventEmitter.emit('txSigned', signedTxs); diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index a127fefc7b..78a6bafe37 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1461,6 +1461,11 @@ "name": "user", "isMut": true, "isSigner": false + }, + { + "name": "signer", + "isMut": false, + "isSigner": true } ], "args": [ @@ -3110,10 +3115,7 @@ { "name": "marketIndexes", "type": { - "array": [ - "u16", - 5 - ] + "vec": "u16" } } ] @@ -6730,6 +6732,10 @@ { "name": "ammInventorySpreadAdjustment", "type": "i8" + }, + { + "name": "referencePriceOffset", + "type": "i32" } ] }, @@ -7525,13 +7531,39 @@ ] }, { - "name": "zeroAmmFieldsPrepMmOracleInfo", + "name": "updateDisableBitflagsMmOracle", "accounts": [ { "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "disable", + "type": "bool" + } + ] + }, + { + "name": "zeroMmOracleFields", + "accounts": [ + { + "name": "admin", + "isMut": false, "isSigner": true }, + { + "name": "state", + "isMut": false, + "isSigner": false + }, { "name": "perpMarket", "isMut": true, @@ -9027,12 +9059,16 @@ "name": "maxInitializeUserFee", "type": "u16" }, + { + "name": "disableBitFlags", + "type": "u8" + }, { "name": "padding", "type": { "array": [ "u8", - 10 + 9 ] } } @@ -10841,7 +10877,7 @@ "type": "u64" }, { - "name": "maxPositionSize", + "name": "mmOracleSlot", "docs": [ "the max base size a single user can have", "precision: BASE_PRECISION" @@ -10929,18 +10965,11 @@ "type": "u32" }, { - "name": "longIntensityCount", - "docs": [ - "the count intensity of long fills against AMM" - ], - "type": "u32" - }, - { - "name": "shortIntensityCount", + "name": "mmOraclePrice", "docs": [ - "the count intensity of short fills against AMM" + "MM oracle price" ], - "type": "u32" + "type": "i64" }, { "name": "maxFillReserveFraction", @@ -11022,7 +11051,7 @@ "type": "i8" }, { - "name": "totalFeeEarnedPerLp", + "name": "mmOracleSequenceId", "type": "u64" }, { @@ -11966,6 +11995,9 @@ }, { "name": "OracleOrderPrice" + }, + { + "name": "UseMMOraclePrice" } ] } @@ -15950,4 +15982,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} +} \ No newline at end of file diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 8cac837f39..cd39f6abe3 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -90,6 +90,7 @@ export * from './oracles/pythPullClient'; export * from './oracles/pythLazerClient'; export * from './oracles/switchboardOnDemandClient'; export * from './oracles/oracleId'; +export * from './oracles/utils'; export * from './swift/swiftOrderSubscriber'; export * from './swift/signedMsgUserAccountSubscriber'; export * from './swift/grpcSignedMsgUserAccountSubscriber'; diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index 855a856239..71f29c9845 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -157,6 +157,11 @@ export function calculateUpdatedAMM( amm: AMM, oraclePriceData: OraclePriceData ): AMM { + if (!oraclePriceData?.fetchedWithMMOracle) { + console.log( + 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing' + ); + } if (amm.curveUpdateIntensity == 0 || oraclePriceData === undefined) { return amm; } @@ -199,6 +204,11 @@ export function calculateUpdatedAMMSpreadReserves( oraclePriceData: OraclePriceData, isPrediction = false ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } { + if (!oraclePriceData?.fetchedWithMMOracle) { + console.log( + 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing' + ); + } const newAmm = calculateUpdatedAMM(amm, oraclePriceData); const [shortReserves, longReserves] = calculateSpreadReserves( newAmm, @@ -227,6 +237,11 @@ export function calculateBidAskPrice( withUpdate = true, isPrediction = false ): [BN, BN] { + if (!oraclePriceData?.fetchedWithMMOracle) { + console.log( + 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing' + ); + } let newAmm: AMM; if (withUpdate) { newAmm = calculateUpdatedAMM(amm, oraclePriceData); diff --git a/sdk/src/oracles/types.ts b/sdk/src/oracles/types.ts index 11deab8532..167dee3af8 100644 --- a/sdk/src/oracles/types.ts +++ b/sdk/src/oracles/types.ts @@ -2,6 +2,12 @@ import { BN } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; import { OracleSource } from '../types'; +export type MMOraclePriceData = { + mmOraclePrice: BN; + mmOracleSlot: BN; + oraclePriceData: OraclePriceData; +}; + export type OraclePriceData = { price: BN; slot: BN; @@ -10,6 +16,7 @@ export type OraclePriceData = { twap?: BN; twapConfidence?: BN; maxPrice?: BN; // pre-launch markets only + fetchedWithMMOracle?: boolean; }; export type OracleInfo = { diff --git a/sdk/src/oracles/utils.ts b/sdk/src/oracles/utils.ts new file mode 100644 index 0000000000..6dfcc36934 --- /dev/null +++ b/sdk/src/oracles/utils.ts @@ -0,0 +1,13 @@ +import { BN } from '@coral-xyz/anchor'; +import { MMOraclePriceData } from './types'; +import { FIVE } from '../constants/numericConstants'; + +export function getOracleConfidenceFromMMOracleData( + mmOracleData: MMOraclePriceData +): BN { + const mmOracleDiffPremium = mmOracleData.mmOraclePrice + .sub(mmOracleData.oraclePriceData.price) + .abs() + .div(FIVE); + return mmOracleData.oraclePriceData.confidence.add(mmOracleDiffPremium); +} diff --git a/sdk/src/tx/utils.ts b/sdk/src/tx/utils.ts index 84b1f4c363..617b3f6ea6 100644 --- a/sdk/src/tx/utils.ts +++ b/sdk/src/tx/utils.ts @@ -6,6 +6,7 @@ import { } from '@solana/web3.js'; export const MAX_TX_BYTE_SIZE = 1232; +export const NATIVE_INSTRUCTION_MAGIC_BYTES = [0xff, 0xff, 0xff, 0xff]; export const isVersionedTransaction = ( tx: Transaction | VersionedTransaction @@ -90,3 +91,12 @@ export const getSizeOfTransaction = ( function getSizeOfCompressedU16(n: number) { return 1 + Number(n >= 128) + Number(n >= 16384); } + +export function createNativeInstructionDiscriminatorBuffer( + discriminator: number +): Uint8Array { + const buffer = new Uint8Array(5); + buffer.set(NATIVE_INSTRUCTION_MAGIC_BYTES, 0); + buffer.set([discriminator], 4); + return buffer; +} diff --git a/sdk/src/types.ts b/sdk/src/types.ts index e58f9467d0..74177626d8 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -952,7 +952,7 @@ export type AMM = { totalFeeMinusDistributions: BN; totalFeeWithdrawn: BN; totalFee: BN; - totalFeeEarnedPerLp: BN; + mmOracleSequenceId: BN; userLpShares: BN; baseAssetAmountWithUnsettledLp: BN; orderStepSize: BN; @@ -997,13 +997,12 @@ export type AMM = { markStd: BN; oracleStd: BN; - longIntensityCount: number; longIntensityVolume: BN; - shortIntensityCount: number; shortIntensityVolume: BN; volume24H: BN; minOrderSize: BN; - maxPositionSize: BN; + mmOraclePrice: BN; + mmOracleSlot: BN; bidBaseAssetReserve: BN; bidQuoteAssetReserve: BN; diff --git a/test-scripts/single-anchor-test.sh b/test-scripts/single-anchor-test.sh index 2775260207..b75e2f22a9 100755 --- a/test-scripts/single-anchor-test.sh +++ b/test-scripts/single-anchor-test.sh @@ -6,7 +6,7 @@ fi export ANCHOR_WALLET=~/.config/solana/id.json -test_files=(referencePriceOffset.ts) +test_files=(admin.ts) for test_file in ${test_files[@]}; do ts-mocha -t 300000 ./tests/${test_file} diff --git a/tests/admin.ts b/tests/admin.ts index d6835f7250..b92fbd496e 100644 --- a/tests/admin.ts +++ b/tests/admin.ts @@ -1,14 +1,16 @@ import * as anchor from '@coral-xyz/anchor'; import { Program } from '@coral-xyz/anchor'; -import { assert } from 'chai'; +import { assert, expect } from 'chai'; import { startAnchor } from 'solana-bankrun'; import { BN, ExchangeStatus, getPythLazerOraclePublicKey, + loadKeypair, OracleGuardRails, OracleSource, TestClient, + Wallet, } from '../sdk/src'; import { decodeName, DEFAULT_MARKET_NAME } from '../sdk/src/userName'; @@ -34,10 +36,12 @@ describe('admin', () => { let usdcMint; + let bankrunContextWrapper: BankrunContextWrapper; + before(async () => { const context = await startAnchor('', [], []); - const bankrunContextWrapper = new BankrunContextWrapper(context); + bankrunContextWrapper = new BankrunContextWrapper(context); bulkAccountLoader = new TestBulkAccountLoader( bankrunContextWrapper.connection, @@ -47,9 +51,13 @@ describe('admin', () => { usdcMint = await mockUSDCMint(bankrunContextWrapper); + const wallet = new Wallet(loadKeypair(process.env.ANCHOR_WALLET)); + //@ts-ignore + await bankrunContextWrapper.fundKeypair(wallet, 10 ** 9); + driftClient = new TestClient({ connection: bankrunContextWrapper.connection.toConnection(), // ugh. - wallet: bankrunContextWrapper.provider.wallet, + wallet, programID: chProgram.programId, opts: { commitment: 'confirmed', @@ -401,6 +409,53 @@ describe('admin', () => { ); }); + it('update MM oracle native', async () => { + const oraclePrice = new BN(100); + const oracleTS = new BN(Date.now()); + await driftClient.updateMmOracleNative(0, oraclePrice, oracleTS); + + let perpMarket = driftClient.getPerpMarketAccount(0); + assert(perpMarket.amm.mmOraclePrice.eq(oraclePrice)); + const slot = (await bankrunContextWrapper.connection.getSlot()).toString(); + expect(perpMarket.amm.mmOracleSlot.toNumber()).to.be.approximately( + +slot, + 1 + ); + assert(perpMarket.amm.mmOracleSequenceId.eq(oracleTS)); + + // Doesnt change if id doesnt increase + await driftClient.updateMmOracleNative(0, oraclePrice.addn(1), oracleTS); + assert(perpMarket.amm.mmOraclePrice.eq(oraclePrice)); + + // Doesnt update if we flip the admin switch + await driftClient.updateDisableBitFlagsMMOracle(true); + try { + await driftClient.updateMmOracleNative(0, oraclePrice, oracleTS); + assert.fail('Should have thrown'); + } catch (e) { + console.log(e.message); + assert(e.message.includes('Program failed to complete')); + } + + // Re-enable and update + await driftClient.updateDisableBitFlagsMMOracle(false); + await driftClient.updateMmOracleNative( + 0, + oraclePrice.addn(2), + oracleTS.addn(1) + ); + perpMarket = driftClient.getPerpMarketAccount(0); + assert(perpMarket.amm.mmOraclePrice.eq(oraclePrice.addn(2))); + assert(perpMarket.amm.mmOracleSequenceId.eq(oracleTS.addn(1))); + }); + + it('update amm adjustment oracle native', async () => { + const ammSpreadAdjustment = 5; + await driftClient.updateAmmSpreadAdjustmentNative(0, ammSpreadAdjustment); + const perpMarket = driftClient.getPerpMarketAccount(0); + assert(perpMarket.amm.ammSpreadAdjustment == ammSpreadAdjustment); + }); + it('Update admin', async () => { const newAdminKey = PublicKey.default; diff --git a/tests/liquidityProvider.ts b/tests/liquidityProvider.ts index c1b0883387..961fdf3956 100644 --- a/tests/liquidityProvider.ts +++ b/tests/liquidityProvider.ts @@ -1609,6 +1609,8 @@ describe('liquidity providing', () => { assert(posAfterSettle.quoteAssetAmount.eq(posAfter.quoteAssetAmount)); }); + return; + it('permissionless lp burn', async () => { return; const lpAmount = new BN(1 * BASE_PRECISION.toNumber()); @@ -1634,8 +1636,6 @@ describe('liquidity providing', () => { // assert(position.lpShares.eq(ZERO)); }); - return; - it('lp gets paid in funding (todo)', async () => { const market = driftClient.getPerpMarketAccount(1); const marketIndex = market.marketIndex; diff --git a/tests/perpLpJit.ts b/tests/perpLpJit.ts index 12acebca50..fce1546a06 100644 --- a/tests/perpLpJit.ts +++ b/tests/perpLpJit.ts @@ -991,7 +991,7 @@ describe('lp jit', () => { assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); let [bid, ask] = calculateBidAskPrice( driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getOracleDataForPerpMarket(marketIndex) + driftClient.getMMOracleDataForPerpMarket(marketIndex) ); console.log(bid.toString(), '/', ask.toString()); console.log('bid:', bid.toString()); @@ -1034,7 +1034,7 @@ describe('lp jit', () => { [bid, ask] = calculateBidAskPrice( driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getOracleDataForPerpMarket(marketIndex) + driftClient.getMMOracleDataForPerpMarket(marketIndex) ); console.log(bid.toString(), '/', ask.toString()); console.log('bid:', bid.toString()); @@ -1068,7 +1068,7 @@ describe('lp jit', () => { [bid, ask] = calculateBidAskPrice( driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getOracleDataForPerpMarket(marketIndex) + driftClient.getMMOracleDataForPerpMarket(marketIndex) ); console.log(bid.toString(), '/', ask.toString()); console.log('bid:', bid.toString()); @@ -1163,7 +1163,7 @@ describe('lp jit', () => { [bid, ask] = calculateBidAskPrice( driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getOracleDataForPerpMarket(marketIndex) + driftClient.getMMOracleDataForPerpMarket(marketIndex) ); console.log(bid.toString(), '/', ask.toString()); console.log('bid:', bid.toString()); diff --git a/tests/prepegMarketOrderBaseAssetAmount.ts b/tests/prepegMarketOrderBaseAssetAmount.ts index e73c64f8b1..a736cd0ef7 100644 --- a/tests/prepegMarketOrderBaseAssetAmount.ts +++ b/tests/prepegMarketOrderBaseAssetAmount.ts @@ -41,10 +41,10 @@ import { mockUserUSDCAccount, mockUSDCMint, setFeedPrice, - getOraclePriceData, initializeQuoteSpotMarket, sleep, } from './testHelpers'; +import { getOraclePriceFromMMOracleData } from '../sdk/src/oracles/utils'; describe('prepeg', () => { const provider = anchor.AnchorProvider.local(undefined, { @@ -207,10 +207,7 @@ describe('prepeg', () => { // await setFeedPrice(anchor.workspace.Pyth, 1.01, solUsd); const curPrice = (await getFeedData(anchor.workspace.Pyth, solUsd)).price; console.log('new oracle price:', curPrice); - const oraclePriceData = await getOraclePriceData( - anchor.workspace.Pyth, - solUsd - ); + const oraclePriceData = driftClient.getMMOracleDataForPerpMarket(0); const position0Before = driftClient.getUserAccount().perpPositions[0]; console.log(position0Before.quoteAssetAmount.eq(ZERO)); @@ -349,11 +346,11 @@ describe('prepeg', () => { const curPrice = (await getFeedData(anchor.workspace.Pyth, solUsd)).price; console.log('new oracle price:', curPrice); - const oraclePriceData = await getOraclePriceData( - anchor.workspace.Pyth, - solUsd + const oraclePriceData = driftClient.getMMOracleDataForPerpMarket(0); + console.log( + 'oraclePriceData', + getOraclePriceFromMMOracleData(oraclePriceData).toNumber() ); - console.log('oraclePriceData', oraclePriceData.price.toNumber()); assert(market0.amm.pegMultiplier.eq(new BN(1000000))); const prepegAMM = calculateUpdatedAMM(market0.amm, oraclePriceData); console.log(prepegAMM.pegMultiplier.toString()); @@ -469,6 +466,7 @@ describe('prepeg', () => { await driftClient.fetchAccounts(); const market = driftClient.getPerpMarketAccount(0); const [bid1, ask1] = calculateBidAskPrice(market.amm, oraclePriceData); + const oraclePrice = getOraclePriceFromMMOracleData(oraclePriceData); console.log( 'after trade bid/ask:', convertToNumber(bid1), @@ -478,8 +476,8 @@ describe('prepeg', () => { convertToNumber(calculateReservePrice(market, oraclePriceData)) ); assert(bid1.lt(ask1)); - assert(ask1.gt(oraclePriceData.price)); - assert(bid1.lt(oraclePriceData.price)); + assert(ask1.gt(oraclePrice)); + assert(bid1.lt(oraclePrice)); console.log('prepegAMM.pegMultiplier:', prepegAMM.pegMultiplier.toString()); console.log( @@ -614,10 +612,7 @@ describe('prepeg', () => { await setFeedPrice(anchor.workspace.Pyth, 1.02234232, solUsd); const curPrice = (await getFeedData(anchor.workspace.Pyth, solUsd)).price; console.log('new oracle price:', curPrice); - const oraclePriceData = await getOraclePriceData( - anchor.workspace.Pyth, - solUsd - ); + const oraclePriceData = driftClient.getMMOracleDataForPerpMarket(0); const [_pctAvgSlippage, _pctMaxSlippage, _entryPrice, newPrice] = calculateTradeSlippage( PositionDirection.SHORT, @@ -698,10 +693,8 @@ describe('prepeg', () => { const curPrice = (await getFeedData(anchor.workspace.Pyth, thisUsd)) .price; console.log('market_index=', i, 'new oracle price:', curPrice); - const oraclePriceData = await getOraclePriceData( - anchor.workspace.Pyth, - thisUsd - ); + const oraclePriceData = driftClient.getMMOracleDataForPerpMarket(i); + const [_pctAvgSlippage, _pctMaxSlippage, _entryPrice, newPrice] = calculateTradeSlippage( PositionDirection.LONG, diff --git a/tests/referencePriceOffset.ts b/tests/referencePriceOffset.ts index 5a9401b8cc..5bfa89007e 100644 --- a/tests/referencePriceOffset.ts +++ b/tests/referencePriceOffset.ts @@ -20,6 +20,7 @@ import { PositionDirection, isVariant, PEG_PRECISION, + EventSubscriber, } from '../sdk/src'; import { initializeQuoteSpotMarket, @@ -61,6 +62,8 @@ describe('Reference Price Offset E2E', () => { let bankrunContextWrapper: BankrunContextWrapper; let bulkAccountLoader: TestBulkAccountLoader; + let eventSubscriber: EventSubscriber; + let adminClient: TestClient; let fillerDriftClient: DriftClient; let usdcMint: Keypair; @@ -109,6 +112,13 @@ describe('Reference Price Offset E2E', () => { 1 ); + eventSubscriber = new EventSubscriber( + bankrunContextWrapper.connection.toConnection(), + // @ts-ignore + program + ); + await eventSubscriber.subscribe(); + usdcMint = await mockUSDCMint(bankrunContextWrapper); // seed SOL-PERP market and oracle accounts @@ -206,6 +216,7 @@ describe('Reference Price Offset E2E', () => { }); afterEach(async () => { + await eventSubscriber.unsubscribe(); await adminClient.unsubscribe(); await fillerDriftClient.unsubscribe(); }); @@ -213,7 +224,7 @@ describe('Reference Price Offset E2E', () => { it('Reference price offset should shift vAMM mid', async () => { await adminClient.fetchAccounts(); - const oracle = adminClient.getOracleDataForPerpMarket(marketIndex); + const oracle = adminClient.getMMOracleDataForPerpMarket(marketIndex); const perpMarket0 = adminClient.getPerpMarketAccount(marketIndex); expect(perpMarket0.amm.curveUpdateIntensity).to.equal(100); @@ -331,6 +342,17 @@ describe('Reference Price Offset E2E', () => { ); expect(oracle.price.toNumber()).to.equal(10118100); + + console.log( + `vAmm mid before offset update: ${convertToNumber( + vAmmMidBeforeOffsetUpdate + )}` + ); + console.log( + `vAmm mid after offset update: ${convertToNumber( + vAmmMidAfterOffsetUpdate + )}` + ); expect(vAmmMidAfterOffsetUpdate.gt(vAmmMidBeforeOffsetUpdate)).to.be.true; // flip reference price more @@ -379,7 +401,7 @@ describe('Reference Price Offset E2E', () => { it('Fills with vAMM should shift vAMM mid', async () => { await adminClient.fetchAccounts(); - const oracle = adminClient.getOracleDataForPerpMarket(marketIndex); + const oracle = adminClient.getMMOracleDataForPerpMarket(marketIndex); const perpMarket0 = adminClient.getPerpMarketAccount(marketIndex); @@ -502,4 +524,106 @@ describe('Reference Price Offset E2E', () => { .true; } }); + + it('Reference price offset should shift vAMM mid from MM oracle if its available', async () => { + const currentOraclePrice = + adminClient.getOracleDataForPerpMarket(marketIndex).price; + const currentOracleSlot = + adminClient.getOracleDataForPerpMarket(marketIndex).slot; + + const perpMarketPre = adminClient.getPerpMarketAccount(marketIndex); + perpMarketPre.amm.referencePriceOffset = 20000; // 0.1% + await overWritePerpMarket( + adminClient, + bankrunContextWrapper, + perpMarketPre.pubkey, + perpMarketPre + ); + await adminClient.updateAMMs([marketIndex]); + + const perpMarket0 = adminClient.getPerpMarketAccount(marketIndex); + const [vBid, vAsk] = calculateBidAskPrice( + perpMarket0.amm, + adminClient.getMMOracleDataForPerpMarket(marketIndex), + true, + false + ); + const vBidNum = convertToNumber(vBid); + const vAskNum = convertToNumber(vAsk); + const spread = (vAskNum - vBidNum) / ((vAskNum + vBidNum) / 2); + + console.log( + `Before ref price: vBid: ${vBidNum}, vAsk: ${vAskNum}, spread: ${ + spread * 10000 + }bps` + ); + console.log( + `Vamm inventory: ${ + -1 * + convertToNumber(perpMarket0.amm.baseAssetAmountWithAmm, BASE_PRECISION) + }` + ); + + // Set the new mm oracle manually + const newOraclePrice = currentOraclePrice.muln(101).divn(100); // 1% higher + perpMarket0.amm.mmOraclePrice = newOraclePrice; + perpMarket0.amm.mmOracleSlot = currentOracleSlot; + await overWritePerpMarket( + adminClient, + bankrunContextWrapper, + perpMarket0.pubkey, + perpMarket0 + ); + const perpMarket1 = adminClient.getPerpMarketAccount(marketIndex); + + const [vBidAfter, vAskAfter] = calculateBidAskPrice( + perpMarket1.amm, + adminClient.getMMOracleDataForPerpMarket(marketIndex), + true, + false + ); + const vBidNumAfter = convertToNumber(vBidAfter); + const vAskNumAfter = convertToNumber(vAskAfter); + const spreadAfter = + (vAskNumAfter - vBidNumAfter) / ((vAskNumAfter + vBidNumAfter) / 2); + console.log( + `After mm offset: vBid: ${vBidNumAfter}, vAsk: ${vAskNumAfter}, spread: ${ + spreadAfter * 10000 + }bps` + ); + console.log( + `Vamm inventory: ${ + -1 * + convertToNumber(perpMarket0.amm.baseAssetAmountWithAmm, BASE_PRECISION) + }` + ); + + expect(vBidNumAfter).to.gt(vBidNum); + expect(vAskNumAfter).to.gt(vAskNum); + + // Try and fill against the vamm + const now = bankrunContextWrapper.connection.getTime(); + const txSig = await placeAndFillVammTrade({ + bankrunContextWrapper, + orderClient: adminClient, + // @ts-ignore + fillerClient: fillerDriftClient, + marketIndex, + baseAssetAmount: BASE_PRECISION.muln(5), + auctionStartPrice: newOraclePrice.muln(101).divn(100), + auctionEndPrice: newOraclePrice.muln(102).divn(100), + oraclePrice: newOraclePrice.muln(102).divn(100), + auctionDuration: 5, + direction: PositionDirection.LONG, + maxTs: new BN(now + 60), + }); + + const events = eventSubscriber.getEventsByTx(txSig); + const fillEvent = events.find((e) => e.eventType == 'OrderActionRecord'); + const fillPrice = fillEvent['takerOrderCumulativeQuoteAssetAmountFilled'] + .mul(PRICE_PRECISION) + .mul(BASE_PRECISION.div(QUOTE_PRECISION)) + .div(fillEvent['takerOrderCumulativeBaseAssetAmountFilled']); + expect(fillPrice.toNumber()).to.be.approximately(vAskAfter.toNumber(), 50); + }); }); diff --git a/tests/testHelpers.ts b/tests/testHelpers.ts index f6a95c468f..df4e742abe 100644 --- a/tests/testHelpers.ts +++ b/tests/testHelpers.ts @@ -1310,8 +1310,8 @@ export async function placeAndFillVammTrade({ direction, maxTs, dumpTxLogs = true, -}: placeAndFillVammTradeParams) { - let tx = null; +}: placeAndFillVammTradeParams): Promise { + let tx: TransactionSignature | null = null; try { tx = await orderClient.placePerpOrder({ orderType: OrderType.LIMIT, @@ -1353,6 +1353,7 @@ export async function placeAndFillVammTrade({ if (dumpTxLogs) { await printTxLogs(bankrunContextWrapper.connection.toConnection(), tx); } + return tx; } catch (e) { console.log('fill failed!'); console.error(e); From d485fa19485a0841f877f5fce85827c1045242f9 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 22:29:13 +0000 Subject: [PATCH 016/216] sdk: release v2.130.0-beta.8 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 065f94c56a..b5ed2f207b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.7 \ No newline at end of file +2.130.0-beta.8 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 2f563f7686..e7db8aa1bf 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.7", + "version": "2.130.0-beta.8", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From f49c1e4201ae0e1ccf3c20f55a3c8f69ccd347e2 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 29 Jul 2025 22:03:36 +0800 Subject: [PATCH 017/216] Improve circular dependencies 1 (#1776) --- .../grpcAccountSubscriber.ts | 4 +- .../webSocketAccountSubscriber.ts | 4 +- .../bulkAccountLoader.ts | 8 ++-- .../customizedCadenceBulkAccountLoader.ts | 2 +- .../testBulkAccountLoader.ts | 0 sdk/src/accounts/bulkUserStatsSubscription.ts | 4 +- sdk/src/accounts/bulkUserSubscription.ts | 4 +- .../grpcDriftClientAccountSubscriber.ts | 18 ++++--- .../pollingDriftClientAccountSubscriber.ts | 20 ++++---- .../webSocketDriftClientAccountSubscriber.ts | 26 +++++----- ...HighLeverageModeConfigAccountSubscriber.ts | 6 +-- ...HighLeverageModeConfigAccountSubscriber.ts | 6 +-- ...grpcInsuranceFundStakeAccountSubscriber.ts | 6 +-- ...lingInsuranceFundStakeAccountSubscriber.ts | 6 +-- ...cketInsuranceFundStakeAccountSubscriber.ts | 6 +-- .../pollingOracleAccountSubscriber.ts | 6 +-- .../grpcProgramAccountSubscriber.ts | 4 +- .../webSocketProgramAccountSubscriber.ts | 2 +- .../pollingTokenAccountSubscriber.ts | 6 +-- sdk/src/accounts/types.ts | 7 --- .../basicUserAccountSubscriber.ts | 8 +++- .../grpcUserAccountSubscriber.ts | 6 +-- .../oneShotUserAccountSubscriber.ts | 4 +- .../pollingUserAccountSubscriber.ts | 8 ++-- .../webSocketUserAccountSubscriber.ts | 6 +-- .../grpcUserStatsAccountSubscriber.ts | 6 +-- .../pollingUserStatsAccountSubscriber.ts | 6 +-- .../webSocketUserStatsAccountSubsriber.ts | 6 +-- .../auctionSubscriber/auctionSubscriber.ts | 2 +- .../auctionSubscriberGrpc.ts | 4 +- sdk/src/{config.ts => config/index.ts} | 13 +++-- sdk/src/config/types.ts | 1 + sdk/src/constants/perpMarkets.ts | 2 +- sdk/src/constants/spotMarkets.ts | 2 +- sdk/src/dlob/DLOB.ts | 3 +- sdk/src/dlob/DLOBNode.ts | 2 +- sdk/src/dlob/NodeList.ts | 8 +--- sdk/src/dlob/utils.ts | 6 +++ sdk/src/driftClient.ts | 8 ++-- sdk/src/driftClientConfig.ts | 4 +- sdk/src/index.ts | 40 ++++++++++------ sdk/src/math/margin.ts | 2 +- sdk/src/openbook/openbookV2Subscriber.ts | 2 +- .../orderSubscriber/WebsocketSubscription.ts | 2 +- sdk/src/orderSubscriber/grpcSubscription.ts | 2 +- sdk/src/phoenix/phoenixSubscriber.ts | 2 +- sdk/src/serum/serumSubscriber.ts | 2 +- sdk/src/serum/types.ts | 2 +- .../grpcSignedMsgUserAccountSubscriber.ts | 2 +- .../swift/signedMsgUserAccountSubscriber.ts | 2 +- sdk/src/swift/swiftOrderSubscriber.ts | 2 +- sdk/src/testClient.ts | 2 +- sdk/src/user.ts | 6 +-- sdk/src/userConfig.ts | 2 +- sdk/src/userMap/PollingSubscription.ts | 6 +-- sdk/src/userMap/WebsocketSubscription.ts | 8 ++-- sdk/src/userMap/events.ts | 7 +++ sdk/src/userMap/grpcSubscription.ts | 10 ++-- sdk/src/userMap/types.ts | 47 +++++++++++++++++++ sdk/src/userMap/userMap.ts | 35 ++------------ sdk/src/userMap/userStatsMap.ts | 4 +- sdk/src/userStats.ts | 6 +-- sdk/src/userStatsConfig.ts | 2 +- ...customizedCadenceBulkAccountLoader.test.ts | 2 +- tests/admin.ts | 2 +- tests/adminDeposit.ts | 2 +- tests/assetTier.ts | 2 +- tests/cancelAllOrders.ts | 2 +- tests/cappedSymFunding.ts | 2 +- tests/curve.ts | 2 +- tests/deleteInitializedSpotMarket.ts | 2 +- tests/depositIntoSpotMarketVault.ts | 2 +- tests/driftClient.ts | 2 +- tests/fillSpot.ts | 2 +- tests/forceUserDelete.ts | 2 +- tests/fuel.ts | 2 +- tests/fuelSweep.ts | 2 +- tests/govStakeDevnet.ts | 2 +- tests/highLeverageMode.ts | 2 +- tests/ifRebalance.ts | 2 +- tests/imbalancePerpPnl.ts | 2 +- tests/insuranceFundStake.ts | 2 +- tests/ksolver.ts | 2 +- tests/liquidateBorrowForPerpPnl.ts | 2 +- tests/liquidateMaxLps.ts | 2 +- tests/liquidatePerp.ts | 2 +- tests/liquidatePerpAndLp.ts | 2 +- tests/liquidatePerpPnlForDeposit.ts | 2 +- tests/liquidatePerpWithFill.ts | 2 +- tests/liquidateSpot.ts | 2 +- tests/liquidateSpotSocialLoss.ts | 2 +- tests/liquidateSpotWithSwap.ts | 2 +- tests/liquidityProvider.ts | 2 +- tests/marketOrder.ts | 2 +- tests/marketOrderBaseAssetAmount.ts | 2 +- tests/maxDeposit.ts | 2 +- tests/maxLeverageOrderParams.ts | 2 +- tests/modifyOrder.ts | 2 +- tests/multipleMakerOrders.ts | 2 +- tests/multipleSpotMakerOrders.ts | 2 +- tests/openbookTest.ts | 2 +- tests/oracleDiffSources.ts | 2 +- tests/oracleFillPriceGuardrails.ts | 2 +- tests/oracleOffsetOrders.ts | 2 +- tests/order.ts | 2 +- tests/ordersWithSpread.ts | 2 +- tests/overwritePerpAccounts.ts | 2 +- tests/pauseDepositWithdraw.ts | 2 +- tests/pauseExchange.ts | 2 +- tests/perpLpJit.ts | 2 +- tests/perpLpRiskMitigation.ts | 2 +- tests/phoenixTest.ts | 2 +- tests/placeAndMakePerp.ts | 2 +- tests/placeAndMakeSignedMsgBankrun.ts | 2 +- tests/placeAndMakeSpotOrder.ts | 2 +- tests/postOnly.ts | 2 +- tests/postOnlyAmmFulfillment.ts | 2 +- tests/prelisting.ts | 2 +- tests/pyth.ts | 2 +- tests/pythLazerBankrun.ts | 2 +- tests/pythPull.ts | 2 +- tests/referencePriceOffset.ts | 2 +- tests/referrer.ts | 2 +- tests/resizeSwiftUserOrderIds.ts | 2 +- tests/roundInFavorBaseAsset.ts | 2 +- tests/serumTest.ts | 2 +- tests/signedMsgWsDelegates.ts | 2 +- tests/spotDepositWithdraw.ts | 2 +- tests/spotDepositWithdraw22.ts | 2 +- tests/spotDepositWithdraw22TransferHooks.ts | 2 +- tests/spotMarketPoolIds.ts | 2 +- tests/spotSwap.ts | 2 +- tests/spotSwap22.ts | 2 +- tests/spotWithdrawUtil100.ts | 2 +- tests/stopLimits.ts | 2 +- tests/subaccounts.ts | 2 +- tests/surgePricing.ts | 2 +- tests/switchOracle.ts | 2 +- tests/switchboardOnDemand.ts | 2 +- tests/switchboardTxCus.ts | 2 +- tests/testHelpers.ts | 2 +- tests/tokenFaucet.ts | 2 +- tests/tradingLP.ts | 2 +- tests/transferPerpPosition.ts | 2 +- tests/transferPools.ts | 2 +- tests/triggerOrders.ts | 2 +- tests/triggerSpotOrder.ts | 2 +- tests/userAccount.ts | 2 +- tests/userDelegate.ts | 2 +- tests/userOrderId.ts | 2 +- tests/whitelist.ts | 2 +- 151 files changed, 333 insertions(+), 288 deletions(-) rename sdk/src/accounts/{ => baseSubscribers}/grpcAccountSubscriber.ts (98%) rename sdk/src/accounts/{ => baseSubscribers}/webSocketAccountSubscriber.ts (99%) rename sdk/src/accounts/{ => bulkAccountLoader}/bulkAccountLoader.ts (96%) rename sdk/src/accounts/{ => bulkAccountLoader}/customizedCadenceBulkAccountLoader.ts (98%) rename sdk/src/accounts/{ => bulkAccountLoader}/testBulkAccountLoader.ts (100%) rename sdk/src/accounts/{ => driftClientAccount}/grpcDriftClientAccountSubscriber.ts (93%) rename sdk/src/accounts/{ => driftClientAccount}/pollingDriftClientAccountSubscriber.ts (97%) rename sdk/src/accounts/{ => driftClientAccount}/webSocketDriftClientAccountSubscriber.ts (96%) rename sdk/src/accounts/{ => highLeverageModeConfigAccount}/pollingHighLeverageModeConfigAccountSubscriber.ts (96%) rename sdk/src/accounts/{ => highLeverageModeConfigAccount}/webSocketHighLeverageModeConfigAccountSubscriber.ts (95%) rename sdk/src/accounts/{ => insuranceFundStakeAccount}/grpcInsuranceFundStakeAccountSubscriber.ts (89%) rename sdk/src/accounts/{ => insuranceFundStakeAccount}/pollingInsuranceFundStakeAccountSubscriber.ts (96%) rename sdk/src/accounts/{ => insuranceFundStakeAccount}/webSocketInsuranceFundStakeAccountSubscriber.ts (95%) rename sdk/src/accounts/{ => oracleAccount}/pollingOracleAccountSubscriber.ts (94%) rename sdk/src/accounts/{ => programAccount}/grpcProgramAccountSubscriber.ts (98%) rename sdk/src/accounts/{ => programAccount}/webSocketProgramAccountSubscriber.ts (99%) rename sdk/src/accounts/{ => tokenAccount}/pollingTokenAccountSubscriber.ts (95%) rename sdk/src/accounts/{ => userAccount}/basicUserAccountSubscriber.ts (92%) rename sdk/src/accounts/{ => userAccount}/grpcUserAccountSubscriber.ts (86%) rename sdk/src/accounts/{ => userAccount}/oneShotUserAccountSubscriber.ts (94%) rename sdk/src/accounts/{ => userAccount}/pollingUserAccountSubscriber.ts (94%) rename sdk/src/accounts/{ => userAccount}/webSocketUserAccountSubscriber.ts (94%) rename sdk/src/accounts/{ => userStatsAccount}/grpcUserStatsAccountSubscriber.ts (87%) rename sdk/src/accounts/{ => userStatsAccount}/pollingUserStatsAccountSubscriber.ts (96%) rename sdk/src/accounts/{ => userStatsAccount}/webSocketUserStatsAccountSubsriber.ts (93%) rename sdk/src/{config.ts => config/index.ts} (96%) create mode 100644 sdk/src/config/types.ts create mode 100644 sdk/src/dlob/utils.ts create mode 100644 sdk/src/userMap/events.ts create mode 100644 sdk/src/userMap/types.ts diff --git a/sdk/src/accounts/grpcAccountSubscriber.ts b/sdk/src/accounts/baseSubscribers/grpcAccountSubscriber.ts similarity index 98% rename from sdk/src/accounts/grpcAccountSubscriber.ts rename to sdk/src/accounts/baseSubscribers/grpcAccountSubscriber.ts index 0ad6ba43cb..53d99602d7 100644 --- a/sdk/src/accounts/grpcAccountSubscriber.ts +++ b/sdk/src/accounts/baseSubscribers/grpcAccountSubscriber.ts @@ -1,4 +1,4 @@ -import { ResubOpts, GrpcConfigs } from './types'; +import { ResubOpts, GrpcConfigs } from '../types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; import * as Buffer from 'buffer'; @@ -11,7 +11,7 @@ import { createClient, SubscribeRequest, SubscribeUpdate, -} from '../isomorphic/grpc'; +} from '../../isomorphic/grpc'; export class grpcAccountSubscriber extends WebSocketAccountSubscriber { private client: Client; diff --git a/sdk/src/accounts/webSocketAccountSubscriber.ts b/sdk/src/accounts/baseSubscribers/webSocketAccountSubscriber.ts similarity index 99% rename from sdk/src/accounts/webSocketAccountSubscriber.ts rename to sdk/src/accounts/baseSubscribers/webSocketAccountSubscriber.ts index deee456166..08198c9a44 100644 --- a/sdk/src/accounts/webSocketAccountSubscriber.ts +++ b/sdk/src/accounts/baseSubscribers/webSocketAccountSubscriber.ts @@ -3,10 +3,10 @@ import { BufferAndSlot, AccountSubscriber, ResubOpts, -} from './types'; +} from '../types'; import { AnchorProvider, Program } from '@coral-xyz/anchor'; import { AccountInfo, Commitment, Context, PublicKey } from '@solana/web3.js'; -import { capitalize } from './utils'; +import { capitalize } from '../utils'; import * as Buffer from 'buffer'; export class WebSocketAccountSubscriber implements AccountSubscriber { diff --git a/sdk/src/accounts/bulkAccountLoader.ts b/sdk/src/accounts/bulkAccountLoader/bulkAccountLoader.ts similarity index 96% rename from sdk/src/accounts/bulkAccountLoader.ts rename to sdk/src/accounts/bulkAccountLoader/bulkAccountLoader.ts index c76d91b1cd..76191c03ea 100644 --- a/sdk/src/accounts/bulkAccountLoader.ts +++ b/sdk/src/accounts/bulkAccountLoader/bulkAccountLoader.ts @@ -1,9 +1,9 @@ import { Commitment, PublicKey } from '@solana/web3.js'; import { v4 as uuidv4 } from 'uuid'; -import { BufferAndSlot } from './types'; -import { promiseTimeout } from '../util/promiseTimeout'; -import { Connection } from '../bankrun/bankrunConnection'; -import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../constants/numericConstants'; +import { BufferAndSlot } from '../types'; +import { promiseTimeout } from '../../util/promiseTimeout'; +import { Connection } from '../../bankrun/bankrunConnection'; +import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../../constants/numericConstants'; export type AccountToLoad = { publicKey: PublicKey; diff --git a/sdk/src/accounts/customizedCadenceBulkAccountLoader.ts b/sdk/src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader.ts similarity index 98% rename from sdk/src/accounts/customizedCadenceBulkAccountLoader.ts rename to sdk/src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader.ts index c870cd6a66..c3774996da 100644 --- a/sdk/src/accounts/customizedCadenceBulkAccountLoader.ts +++ b/sdk/src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader.ts @@ -1,4 +1,4 @@ -import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../constants/numericConstants'; +import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../../constants/numericConstants'; import { BulkAccountLoader } from './bulkAccountLoader'; import { Commitment, Connection, PublicKey } from '@solana/web3.js'; import { v4 as uuidv4 } from 'uuid'; diff --git a/sdk/src/accounts/testBulkAccountLoader.ts b/sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader.ts similarity index 100% rename from sdk/src/accounts/testBulkAccountLoader.ts rename to sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader.ts diff --git a/sdk/src/accounts/bulkUserStatsSubscription.ts b/sdk/src/accounts/bulkUserStatsSubscription.ts index 5dca75611a..c2ebe62877 100644 --- a/sdk/src/accounts/bulkUserStatsSubscription.ts +++ b/sdk/src/accounts/bulkUserStatsSubscription.ts @@ -1,6 +1,6 @@ import { UserStats } from '../userStats'; -import { BulkAccountLoader } from './bulkAccountLoader'; -import { PollingUserStatsAccountSubscriber } from './pollingUserStatsAccountSubscriber'; +import { BulkAccountLoader } from './bulkAccountLoader/bulkAccountLoader'; +import { PollingUserStatsAccountSubscriber } from './userStatsAccount/pollingUserStatsAccountSubscriber'; /** * @param userStats diff --git a/sdk/src/accounts/bulkUserSubscription.ts b/sdk/src/accounts/bulkUserSubscription.ts index 3720486b3b..c0831c63fe 100644 --- a/sdk/src/accounts/bulkUserSubscription.ts +++ b/sdk/src/accounts/bulkUserSubscription.ts @@ -1,6 +1,6 @@ import { User } from '../user'; -import { BulkAccountLoader } from './bulkAccountLoader'; -import { PollingUserAccountSubscriber } from './pollingUserAccountSubscriber'; +import { BulkAccountLoader } from './bulkAccountLoader/bulkAccountLoader'; +import { PollingUserAccountSubscriber } from './userAccount/pollingUserAccountSubscriber'; /** * @param users diff --git a/sdk/src/accounts/grpcDriftClientAccountSubscriber.ts b/sdk/src/accounts/driftClientAccount/grpcDriftClientAccountSubscriber.ts similarity index 93% rename from sdk/src/accounts/grpcDriftClientAccountSubscriber.ts rename to sdk/src/accounts/driftClientAccount/grpcDriftClientAccountSubscriber.ts index 8545b5f029..ac60fda929 100644 --- a/sdk/src/accounts/grpcDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/driftClientAccount/grpcDriftClientAccountSubscriber.ts @@ -1,16 +1,20 @@ import { WebSocketDriftClientAccountSubscriber } from './webSocketDriftClientAccountSubscriber'; -import { OracleInfo, OraclePriceData } from '../oracles/types'; +import { OracleInfo, OraclePriceData } from '../../oracles/types'; import { Program } from '@coral-xyz/anchor'; -import { findAllMarketAndOracles } from '../config'; +import { findAllMarketAndOracles } from '../../config'; import { getDriftStateAccountPublicKey, getPerpMarketPublicKey, getSpotMarketPublicKey, -} from '../addresses/pda'; -import { DelistedMarketSetting, GrpcConfigs, ResubOpts } from './types'; -import { grpcAccountSubscriber } from './grpcAccountSubscriber'; -import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types'; -import { getOracleId } from '../oracles/oracleId'; +} from '../../addresses/pda'; +import { DelistedMarketSetting, GrpcConfigs, ResubOpts } from '../types'; +import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; +import { + PerpMarketAccount, + SpotMarketAccount, + StateAccount, +} from '../../types'; +import { getOracleId } from '../../oracles/oracleId'; export class gprcDriftClientAccountSubscriber extends WebSocketDriftClientAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts b/sdk/src/accounts/driftClientAccount/pollingDriftClientAccountSubscriber.ts similarity index 97% rename from sdk/src/accounts/pollingDriftClientAccountSubscriber.ts rename to sdk/src/accounts/driftClientAccount/pollingDriftClientAccountSubscriber.ts index 18da8df653..6277d47855 100644 --- a/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/driftClientAccount/pollingDriftClientAccountSubscriber.ts @@ -6,7 +6,7 @@ import { DriftClientAccountSubscriber, NotSubscribedError, OraclesToPoll, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; @@ -16,20 +16,20 @@ import { StateAccount, UserAccount, OracleSource, -} from '../types'; +} from '../../types'; import { getDriftStateAccountPublicKey, getPerpMarketPublicKey, getSpotMarketPublicKey, -} from '../addresses/pda'; -import { BulkAccountLoader } from './bulkAccountLoader'; -import { capitalize, findDelistedPerpMarketsAndOracles } from './utils'; +} from '../../addresses/pda'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { capitalize, findDelistedPerpMarketsAndOracles } from '../utils'; import { PublicKey } from '@solana/web3.js'; -import { OracleInfo, OraclePriceData } from '../oracles/types'; -import { OracleClientCache } from '../oracles/oracleClientCache'; -import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient'; -import { findAllMarketAndOracles } from '../config'; -import { getOracleId } from '../oracles/oracleId'; +import { OracleInfo, OraclePriceData } from '../../oracles/types'; +import { OracleClientCache } from '../../oracles/oracleClientCache'; +import { QUOTE_ORACLE_PRICE_DATA } from '../../oracles/quoteAssetOracleClient'; +import { findAllMarketAndOracles } from '../../config'; +import { getOracleId } from '../../oracles/oracleId'; const ORACLE_DEFAULT_ID = getOracleId( PublicKey.default, diff --git a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts b/sdk/src/accounts/driftClientAccount/webSocketDriftClientAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts rename to sdk/src/accounts/driftClientAccount/webSocketDriftClientAccountSubscriber.ts index c49d425796..4d05f4ef2a 100644 --- a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/driftClientAccount/webSocketDriftClientAccountSubscriber.ts @@ -6,8 +6,12 @@ import { DriftClientAccountSubscriber, NotSubscribedError, ResubOpts, -} from './types'; -import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types'; +} from '../types'; +import { + PerpMarketAccount, + SpotMarketAccount, + StateAccount, +} from '../../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; @@ -17,17 +21,17 @@ import { getPerpMarketPublicKeySync, getSpotMarketPublicKey, getSpotMarketPublicKeySync, -} from '../addresses/pda'; -import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; +} from '../../addresses/pda'; +import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { OracleInfo, OraclePriceData } from '../oracles/types'; -import { OracleClientCache } from '../oracles/oracleClientCache'; +import { OracleInfo, OraclePriceData } from '../../oracles/types'; +import { OracleClientCache } from '../../oracles/oracleClientCache'; import * as Buffer from 'buffer'; -import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient'; -import { findAllMarketAndOracles } from '../config'; -import { findDelistedPerpMarketsAndOracles } from './utils'; -import { getOracleId } from '../oracles/oracleId'; -import { OracleSource } from '../types'; +import { QUOTE_ORACLE_PRICE_DATA } from '../../oracles/quoteAssetOracleClient'; +import { findAllMarketAndOracles } from '../../config'; +import { findDelistedPerpMarketsAndOracles } from '../utils'; +import { getOracleId } from '../../oracles/oracleId'; +import { OracleSource } from '../../types'; const ORACLE_DEFAULT_ID = getOracleId( PublicKey.default, diff --git a/sdk/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts b/sdk/src/accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts rename to sdk/src/accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber.ts index 6b7f2a0f65..78009b6f5f 100644 --- a/sdk/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts +++ b/sdk/src/accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, HighLeverageModeConfigAccountEvents, HighLeverageModeConfigAccountSubscriber, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './bulkAccountLoader'; -import { HighLeverageModeConfig } from '../types'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { HighLeverageModeConfig } from '../../types'; export class PollingHighLeverageModeConfigAccountSubscriber implements HighLeverageModeConfigAccountSubscriber diff --git a/sdk/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts b/sdk/src/accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber.ts similarity index 95% rename from sdk/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts rename to sdk/src/accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber.ts index 0742f6b1fa..735535b2d2 100644 --- a/sdk/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts +++ b/sdk/src/accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber.ts @@ -4,13 +4,13 @@ import { NotSubscribedError, HighLeverageModeConfigAccountEvents, HighLeverageModeConfigAccountSubscriber, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; -import { HighLeverageModeConfig } from '../types'; +import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; +import { HighLeverageModeConfig } from '../../types'; export class WebSocketHighLeverageModeConfigAccountSubscriber implements HighLeverageModeConfigAccountSubscriber diff --git a/sdk/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts b/sdk/src/accounts/insuranceFundStakeAccount/grpcInsuranceFundStakeAccountSubscriber.ts similarity index 89% rename from sdk/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts rename to sdk/src/accounts/insuranceFundStakeAccount/grpcInsuranceFundStakeAccountSubscriber.ts index 56aa167b7e..00ceae93fb 100644 --- a/sdk/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +++ b/sdk/src/accounts/insuranceFundStakeAccount/grpcInsuranceFundStakeAccountSubscriber.ts @@ -1,9 +1,9 @@ -import { GrpcConfigs } from './types'; +import { GrpcConfigs } from '../types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; -import { InsuranceFundStake } from '../types'; +import { InsuranceFundStake } from '../../types'; import { WebSocketInsuranceFundStakeAccountSubscriber } from './webSocketInsuranceFundStakeAccountSubscriber'; -import { grpcAccountSubscriber } from './grpcAccountSubscriber'; +import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; export class grpcInsuranceFundStakeAccountSubscriber extends WebSocketInsuranceFundStakeAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts b/sdk/src/accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts rename to sdk/src/accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber.ts index bca1c04ac7..32bf6dc91a 100644 --- a/sdk/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +++ b/sdk/src/accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, InsuranceFundStakeAccountEvents, InsuranceFundStakeAccountSubscriber, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './bulkAccountLoader'; -import { InsuranceFundStake } from '../types'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { InsuranceFundStake } from '../../types'; export class PollingInsuranceFundStakeAccountSubscriber implements InsuranceFundStakeAccountSubscriber diff --git a/sdk/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts b/sdk/src/accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber.ts similarity index 95% rename from sdk/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts rename to sdk/src/accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber.ts index bc8042a7ae..0f0844481c 100644 --- a/sdk/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +++ b/sdk/src/accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber.ts @@ -4,13 +4,13 @@ import { NotSubscribedError, InsuranceFundStakeAccountEvents, InsuranceFundStakeAccountSubscriber, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; -import { InsuranceFundStake } from '../types'; +import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; +import { InsuranceFundStake } from '../../types'; export class WebSocketInsuranceFundStakeAccountSubscriber implements InsuranceFundStakeAccountSubscriber diff --git a/sdk/src/accounts/pollingOracleAccountSubscriber.ts b/sdk/src/accounts/oracleAccount/pollingOracleAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/pollingOracleAccountSubscriber.ts rename to sdk/src/accounts/oracleAccount/pollingOracleAccountSubscriber.ts index bdb6e74343..49ee8c6405 100644 --- a/sdk/src/accounts/pollingOracleAccountSubscriber.ts +++ b/sdk/src/accounts/oracleAccount/pollingOracleAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, OracleEvents, OracleAccountSubscriber, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './bulkAccountLoader'; -import { OracleClient, OraclePriceData } from '../oracles/types'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { OracleClient, OraclePriceData } from '../../oracles/types'; export class PollingOracleAccountSubscriber implements OracleAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/grpcProgramAccountSubscriber.ts b/sdk/src/accounts/programAccount/grpcProgramAccountSubscriber.ts similarity index 98% rename from sdk/src/accounts/grpcProgramAccountSubscriber.ts rename to sdk/src/accounts/programAccount/grpcProgramAccountSubscriber.ts index 73b6b1a077..8cc26fdfcd 100644 --- a/sdk/src/accounts/grpcProgramAccountSubscriber.ts +++ b/sdk/src/accounts/programAccount/grpcProgramAccountSubscriber.ts @@ -1,4 +1,4 @@ -import { ResubOpts, GrpcConfigs } from './types'; +import { ResubOpts, GrpcConfigs } from '../types'; import { Program } from '@coral-xyz/anchor'; import bs58 from 'bs58'; import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; @@ -11,7 +11,7 @@ import { createClient, SubscribeRequest, SubscribeUpdate, -} from '../isomorphic/grpc'; +} from '../../isomorphic/grpc'; export class grpcProgramAccountSubscriber< T, diff --git a/sdk/src/accounts/webSocketProgramAccountSubscriber.ts b/sdk/src/accounts/programAccount/webSocketProgramAccountSubscriber.ts similarity index 99% rename from sdk/src/accounts/webSocketProgramAccountSubscriber.ts rename to sdk/src/accounts/programAccount/webSocketProgramAccountSubscriber.ts index d860477583..63c7111d0f 100644 --- a/sdk/src/accounts/webSocketProgramAccountSubscriber.ts +++ b/sdk/src/accounts/programAccount/webSocketProgramAccountSubscriber.ts @@ -1,4 +1,4 @@ -import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types'; +import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from '../types'; import { AnchorProvider, Program } from '@coral-xyz/anchor'; import { Commitment, diff --git a/sdk/src/accounts/pollingTokenAccountSubscriber.ts b/sdk/src/accounts/tokenAccount/pollingTokenAccountSubscriber.ts similarity index 95% rename from sdk/src/accounts/pollingTokenAccountSubscriber.ts rename to sdk/src/accounts/tokenAccount/pollingTokenAccountSubscriber.ts index 398b462032..37ad965943 100644 --- a/sdk/src/accounts/pollingTokenAccountSubscriber.ts +++ b/sdk/src/accounts/tokenAccount/pollingTokenAccountSubscriber.ts @@ -3,14 +3,14 @@ import { NotSubscribedError, TokenAccountEvents, TokenAccountSubscriber, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './bulkAccountLoader'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; import { Account } from '@solana/spl-token'; -import { parseTokenAccount } from '../token'; +import { parseTokenAccount } from '../../token'; export class PollingTokenAccountSubscriber implements TokenAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index a4b4425253..5891da768f 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -13,7 +13,6 @@ import { EventEmitter } from 'events'; import { Context, PublicKey } from '@solana/web3.js'; import { Account } from '@solana/spl-token'; import { OracleInfo, OraclePriceData } from '../oracles/types'; -import { User } from '../user'; import { ChannelOptions, CommitmentLevel } from '../isomorphic/grpc'; export interface AccountSubscriber { @@ -103,12 +102,6 @@ export interface UserAccountEvents { error: (e: Error) => void; } -export interface UserEvents { - userUpdate: (payload: User) => void; - update: void; - error: (e: Error) => void; -} - export interface UserAccountSubscriber { eventEmitter: StrictEventEmitter; isSubscribed: boolean; diff --git a/sdk/src/accounts/basicUserAccountSubscriber.ts b/sdk/src/accounts/userAccount/basicUserAccountSubscriber.ts similarity index 92% rename from sdk/src/accounts/basicUserAccountSubscriber.ts rename to sdk/src/accounts/userAccount/basicUserAccountSubscriber.ts index c8731f82fa..848840a9f3 100644 --- a/sdk/src/accounts/basicUserAccountSubscriber.ts +++ b/sdk/src/accounts/userAccount/basicUserAccountSubscriber.ts @@ -1,8 +1,12 @@ -import { DataAndSlot, UserAccountEvents, UserAccountSubscriber } from './types'; +import { + DataAndSlot, + UserAccountEvents, + UserAccountSubscriber, +} from '../types'; import { PublicKey } from '@solana/web3.js'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { UserAccount } from '../types'; +import { UserAccount } from '../../types'; /** * Basic implementation of UserAccountSubscriber. It will only take in UserAccount diff --git a/sdk/src/accounts/grpcUserAccountSubscriber.ts b/sdk/src/accounts/userAccount/grpcUserAccountSubscriber.ts similarity index 86% rename from sdk/src/accounts/grpcUserAccountSubscriber.ts rename to sdk/src/accounts/userAccount/grpcUserAccountSubscriber.ts index db3ad1225a..68013565d1 100644 --- a/sdk/src/accounts/grpcUserAccountSubscriber.ts +++ b/sdk/src/accounts/userAccount/grpcUserAccountSubscriber.ts @@ -1,9 +1,9 @@ -import { ResubOpts, GrpcConfigs } from './types'; +import { ResubOpts, GrpcConfigs } from '../types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; -import { UserAccount } from '../types'; +import { UserAccount } from '../../types'; import { WebSocketUserAccountSubscriber } from './webSocketUserAccountSubscriber'; -import { grpcAccountSubscriber } from './grpcAccountSubscriber'; +import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; export class grpcUserAccountSubscriber extends WebSocketUserAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/oneShotUserAccountSubscriber.ts b/sdk/src/accounts/userAccount/oneShotUserAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/oneShotUserAccountSubscriber.ts rename to sdk/src/accounts/userAccount/oneShotUserAccountSubscriber.ts index 263bfa03d6..b901020b0b 100644 --- a/sdk/src/accounts/oneShotUserAccountSubscriber.ts +++ b/sdk/src/accounts/userAccount/oneShotUserAccountSubscriber.ts @@ -1,8 +1,8 @@ import { Commitment, PublicKey } from '@solana/web3.js'; -import { UserAccount } from '../types'; +import { UserAccount } from '../../types'; import { BasicUserAccountSubscriber } from './basicUserAccountSubscriber'; import { Program } from '@coral-xyz/anchor'; -import { UserAccountSubscriber } from './types'; +import { UserAccountSubscriber } from '../types'; /** * Simple implementation of UserAccountSubscriber. It will fetch the UserAccount diff --git a/sdk/src/accounts/pollingUserAccountSubscriber.ts b/sdk/src/accounts/userAccount/pollingUserAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/pollingUserAccountSubscriber.ts rename to sdk/src/accounts/userAccount/pollingUserAccountSubscriber.ts index d5d7bffeaa..1af3f0f74b 100644 --- a/sdk/src/accounts/pollingUserAccountSubscriber.ts +++ b/sdk/src/accounts/userAccount/pollingUserAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, UserAccountEvents, UserAccountSubscriber, -} from './types'; -import { Connection } from '../bankrun/bankrunConnection'; +} from '../types'; +import { Connection } from '../../bankrun/bankrunConnection'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { UserAccount } from '../types'; -import { BulkAccountLoader } from './bulkAccountLoader'; +import { UserAccount } from '../../types'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; export class PollingUserAccountSubscriber implements UserAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/webSocketUserAccountSubscriber.ts b/sdk/src/accounts/userAccount/webSocketUserAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/webSocketUserAccountSubscriber.ts rename to sdk/src/accounts/userAccount/webSocketUserAccountSubscriber.ts index 79d6f9401f..f2ad1fe772 100644 --- a/sdk/src/accounts/webSocketUserAccountSubscriber.ts +++ b/sdk/src/accounts/userAccount/webSocketUserAccountSubscriber.ts @@ -5,13 +5,13 @@ import { UserAccountEvents, UserAccountSubscriber, ResubOpts, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; -import { UserAccount } from '../types'; +import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; +import { UserAccount } from '../../types'; export class WebSocketUserAccountSubscriber implements UserAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/grpcUserStatsAccountSubscriber.ts b/sdk/src/accounts/userStatsAccount/grpcUserStatsAccountSubscriber.ts similarity index 87% rename from sdk/src/accounts/grpcUserStatsAccountSubscriber.ts rename to sdk/src/accounts/userStatsAccount/grpcUserStatsAccountSubscriber.ts index 2998e5884f..46d5abef45 100644 --- a/sdk/src/accounts/grpcUserStatsAccountSubscriber.ts +++ b/sdk/src/accounts/userStatsAccount/grpcUserStatsAccountSubscriber.ts @@ -1,9 +1,9 @@ -import { ResubOpts, GrpcConfigs } from './types'; +import { ResubOpts, GrpcConfigs } from '../types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; -import { UserStatsAccount } from '../types'; +import { UserStatsAccount } from '../../types'; import { WebSocketUserStatsAccountSubscriber } from './webSocketUserStatsAccountSubsriber'; -import { grpcAccountSubscriber } from './grpcAccountSubscriber'; +import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; export class grpcUserStatsAccountSubscriber extends WebSocketUserStatsAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/pollingUserStatsAccountSubscriber.ts b/sdk/src/accounts/userStatsAccount/pollingUserStatsAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/pollingUserStatsAccountSubscriber.ts rename to sdk/src/accounts/userStatsAccount/pollingUserStatsAccountSubscriber.ts index d146c31dc8..7a8cba922b 100644 --- a/sdk/src/accounts/pollingUserStatsAccountSubscriber.ts +++ b/sdk/src/accounts/userStatsAccount/pollingUserStatsAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, UserStatsAccountSubscriber, UserStatsAccountEvents, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { UserStatsAccount } from '../types'; -import { BulkAccountLoader } from './bulkAccountLoader'; +import { UserStatsAccount } from '../../types'; +import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; export class PollingUserStatsAccountSubscriber implements UserStatsAccountSubscriber diff --git a/sdk/src/accounts/webSocketUserStatsAccountSubsriber.ts b/sdk/src/accounts/userStatsAccount/webSocketUserStatsAccountSubsriber.ts similarity index 93% rename from sdk/src/accounts/webSocketUserStatsAccountSubsriber.ts rename to sdk/src/accounts/userStatsAccount/webSocketUserStatsAccountSubsriber.ts index fdc0be5507..29b0e7f1cc 100644 --- a/sdk/src/accounts/webSocketUserStatsAccountSubsriber.ts +++ b/sdk/src/accounts/userStatsAccount/webSocketUserStatsAccountSubsriber.ts @@ -5,13 +5,13 @@ import { UserStatsAccountSubscriber, UserStatsAccountEvents, ResubOpts, -} from './types'; +} from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; -import { UserStatsAccount } from '../types'; +import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; +import { UserStatsAccount } from '../../types'; export class WebSocketUserStatsAccountSubscriber implements UserStatsAccountSubscriber diff --git a/sdk/src/auctionSubscriber/auctionSubscriber.ts b/sdk/src/auctionSubscriber/auctionSubscriber.ts index 8ffc893713..75b35965e1 100644 --- a/sdk/src/auctionSubscriber/auctionSubscriber.ts +++ b/sdk/src/auctionSubscriber/auctionSubscriber.ts @@ -5,7 +5,7 @@ import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserAccount } from '../types'; import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { ResubOpts } from '../accounts/types'; export class AuctionSubscriber { diff --git a/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts b/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts index 75212d376e..ca83f90e49 100644 --- a/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts +++ b/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts @@ -5,9 +5,9 @@ import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserAccount } from '../types'; import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; -import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; export class AuctionSubscriberGrpc { private driftClient: DriftClient; diff --git a/sdk/src/config.ts b/sdk/src/config/index.ts similarity index 96% rename from sdk/src/config.ts rename to sdk/src/config/index.ts index 0ff967b7ad..8231f130c7 100644 --- a/sdk/src/config.ts +++ b/sdk/src/config/index.ts @@ -1,24 +1,25 @@ import { ConfirmOptions, PublicKey } from '@solana/web3.js'; -import { PerpMarketAccount, SpotMarketAccount } from './types'; +import { PerpMarketAccount, SpotMarketAccount } from '../types'; import { DevnetPerpMarkets, MainnetPerpMarkets, PerpMarketConfig, PerpMarkets, -} from './constants/perpMarkets'; +} from '../constants/perpMarkets'; import { SpotMarketConfig, SpotMarkets, DevnetSpotMarkets, MainnetSpotMarkets, -} from './constants/spotMarkets'; -import { OracleInfo } from './oracles/types'; +} from '../constants/spotMarkets'; +import { OracleInfo } from '../oracles/types'; import { Program, ProgramAccount } from '@coral-xyz/anchor'; import { ON_DEMAND_DEVNET_PID, ON_DEMAND_MAINNET_PID, } from '@switchboard-xyz/on-demand'; -import { getOracleId } from './oracles/oracleId'; +import { getOracleId } from '../oracles/oracleId'; +import { DriftEnv } from './types'; type DriftConfig = { ENV: DriftEnv; @@ -41,8 +42,6 @@ type DriftConfig = { SB_ON_DEMAND_PID: PublicKey; }; -export type DriftEnv = 'devnet' | 'mainnet-beta'; - export const DRIFT_PROGRAM_ID = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH'; export const DRIFT_ORACLE_RECEIVER_ID = 'G6EoTTTgpkNBtVXo96EQp2m6uwwVh2Kt6YidjkmQqoha'; diff --git a/sdk/src/config/types.ts b/sdk/src/config/types.ts new file mode 100644 index 0000000000..392e884514 --- /dev/null +++ b/sdk/src/config/types.ts @@ -0,0 +1 @@ +export type DriftEnv = 'devnet' | 'mainnet-beta'; diff --git a/sdk/src/constants/perpMarkets.ts b/sdk/src/constants/perpMarkets.ts index d23acbe648..97a0bb2d20 100644 --- a/sdk/src/constants/perpMarkets.ts +++ b/sdk/src/constants/perpMarkets.ts @@ -1,6 +1,6 @@ import { PublicKey } from '@solana/web3.js'; import { OracleSource } from '../types'; -import { DriftEnv } from '../config'; +import { DriftEnv } from '../config/types'; export type PerpMarketConfig = { fullName?: string; diff --git a/sdk/src/constants/spotMarkets.ts b/sdk/src/constants/spotMarkets.ts index a732ea2270..c6a99df420 100644 --- a/sdk/src/constants/spotMarkets.ts +++ b/sdk/src/constants/spotMarkets.ts @@ -11,7 +11,7 @@ import { } from './numericConstants'; import { OracleSource } from '../types'; import { BN } from '@coral-xyz/anchor'; -import { DriftEnv } from '../config'; +import { DriftEnv } from '../config/types'; export type SpotMarketConfig = { symbol: string; diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 64109658c3..043831fc25 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -1,4 +1,5 @@ -import { getOrderSignature, NodeList } from './NodeList'; +import { NodeList } from './NodeList'; +import { getOrderSignature } from './utils'; import { BN } from '@coral-xyz/anchor'; import { BASE_PRECISION, diff --git a/sdk/src/dlob/DLOBNode.ts b/sdk/src/dlob/DLOBNode.ts index 4b7717e7ce..5d97deb622 100644 --- a/sdk/src/dlob/DLOBNode.ts +++ b/sdk/src/dlob/DLOBNode.ts @@ -8,7 +8,7 @@ import { getLimitPrice } from '../math/orders'; import { isVariant, Order, ProtectedMakerParams } from '../types'; import { OraclePriceData } from '../oracles/types'; import { convertToNumber } from '../math/conversion'; -import { getOrderSignature } from './NodeList'; +import { getOrderSignature } from './utils'; export interface DLOBNode { getPrice(oraclePriceData: OraclePriceData, slot: number): BN; diff --git a/sdk/src/dlob/NodeList.ts b/sdk/src/dlob/NodeList.ts index ddb483f14a..0fd3afe1c4 100644 --- a/sdk/src/dlob/NodeList.ts +++ b/sdk/src/dlob/NodeList.ts @@ -5,16 +5,10 @@ import { ProtectedMakerParams, } from '../types'; import { createNode, DLOBNode, DLOBNodeMap } from './DLOBNode'; +import { getOrderSignature } from './utils'; export type SortDirection = 'asc' | 'desc'; -export function getOrderSignature( - orderId: number, - userAccount: string -): string { - return `${userAccount.toString()}-${orderId.toString()}`; -} - export interface DLOBNodeGenerator { getGenerator(): Generator; } diff --git a/sdk/src/dlob/utils.ts b/sdk/src/dlob/utils.ts new file mode 100644 index 0000000000..736f4f68ef --- /dev/null +++ b/sdk/src/dlob/utils.ts @@ -0,0 +1,6 @@ +export function getOrderSignature( + orderId: number, + userAccount: string +): string { + return `${userAccount.toString()}-${orderId.toString()}`; +} diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index e8db6a24f8..0c5e21243f 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -133,8 +133,8 @@ import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance'; import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName'; import { OraclePriceData } from './oracles/types'; import { DriftClientConfig } from './driftClientConfig'; -import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; -import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; +import { PollingDriftClientAccountSubscriber } from './accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; +import { WebSocketDriftClientAccountSubscriber } from './accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; import { RetryTxSender } from './tx/retryTxSender'; import { User } from './user'; import { UserSubscriptionConfig } from './userConfig'; @@ -143,9 +143,9 @@ import { DRIFT_ORACLE_RECEIVER_ID, DEFAULT_CONFIRMATION_OPTS, DRIFT_PROGRAM_ID, - DriftEnv, PYTH_LAZER_STORAGE_ACCOUNT_KEY, } from './config'; +import { DriftEnv } from './config/types'; import { WRAPPED_SOL_MINT } from './constants/spotMarkets'; import { UserStats } from './userStats'; import { isSpotPositionAvailable } from './math/spotPosition'; @@ -184,7 +184,7 @@ import { } from './tx/utils'; import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json'; import { asV0Tx, PullFeed, AnchorUtils } from '@switchboard-xyz/on-demand'; -import { gprcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber'; +import { gprcDriftClientAccountSubscriber } from './accounts/driftClientAccount/grpcDriftClientAccountSubscriber'; import nacl from 'tweetnacl'; import { Slothash } from './slot/SlothashSubscriber'; import { getOracleId } from './oracles/oracleId'; diff --git a/sdk/src/driftClientConfig.ts b/sdk/src/driftClientConfig.ts index e833bd54e4..07201c51d5 100644 --- a/sdk/src/driftClientConfig.ts +++ b/sdk/src/driftClientConfig.ts @@ -7,8 +7,8 @@ import { } from '@solana/web3.js'; import { IWallet, TxParams } from './types'; import { OracleInfo } from './oracles/types'; -import { BulkAccountLoader } from './accounts/bulkAccountLoader'; -import { DriftEnv } from './config'; +import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; +import { DriftEnv } from './config/types'; import { TxSender } from './tx/types'; import { TxHandler, TxHandlerConfig } from './tx/txHandler'; import { DelistedMarketSetting, GrpcConfigs } from './accounts/types'; diff --git a/sdk/src/index.ts b/sdk/src/index.ts index cd39f6abe3..fdf9157024 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -9,22 +9,22 @@ export * from './oracles/strictOraclePrice'; export * from './types'; export * from './constants/perpMarkets'; export * from './accounts/fetch'; -export * from './accounts/webSocketDriftClientAccountSubscriber'; -export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber'; -export * from './accounts/webSocketHighLeverageModeConfigAccountSubscriber'; -export * from './accounts/bulkAccountLoader'; +export * from './accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; +export * from './accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber'; +export * from './accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber'; +export * from './accounts/bulkAccountLoader/bulkAccountLoader'; export * from './accounts/bulkUserSubscription'; export * from './accounts/bulkUserStatsSubscription'; -export { CustomizedCadenceBulkAccountLoader } from './accounts/customizedCadenceBulkAccountLoader'; -export * from './accounts/pollingDriftClientAccountSubscriber'; -export * from './accounts/pollingOracleAccountSubscriber'; -export * from './accounts/pollingTokenAccountSubscriber'; -export * from './accounts/pollingUserAccountSubscriber'; -export * from './accounts/pollingUserStatsAccountSubscriber'; -export * from './accounts/pollingInsuranceFundStakeAccountSubscriber'; -export * from './accounts/pollingHighLeverageModeConfigAccountSubscriber'; -export * from './accounts/basicUserAccountSubscriber'; -export * from './accounts/oneShotUserAccountSubscriber'; +export { CustomizedCadenceBulkAccountLoader } from './accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader'; +export * from './accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; +export * from './accounts/oracleAccount/pollingOracleAccountSubscriber'; +export * from './accounts/tokenAccount/pollingTokenAccountSubscriber'; +export * from './accounts/userAccount/pollingUserAccountSubscriber'; +export * from './accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; +export * from './accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber'; +export * from './accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber'; +export * from './accounts/userAccount/basicUserAccountSubscriber'; +export * from './accounts/userAccount/oneShotUserAccountSubscriber'; export * from './accounts/types'; export * from './addresses/pda'; export * from './adminClient'; @@ -76,7 +76,11 @@ export * from './keypair'; export * from './types'; export * from './math/utils'; export * from './math/fuel'; + +/** Config */ export * from './config'; +export * from './config/types'; + export * from './constants/numericConstants'; export * from './serum/serumSubscriber'; export * from './serum/serumFulfillmentConfigMap'; @@ -109,16 +113,24 @@ export * from './util/pythOracleUtils'; export * from './math/spotBalance'; export * from './constants/spotMarkets'; export * from './driftClientConfig'; + +/** DLOB */ export * from './dlob/DLOB'; export * from './dlob/DLOBNode'; export * from './dlob/NodeList'; export * from './dlob/DLOBSubscriber'; export * from './dlob/types'; export * from './dlob/orderBookLevels'; +export * from './dlob/utils'; + +/** UserMap */ export * from './userMap/userMap'; export * from './userMap/referrerMap'; export * from './userMap/userStatsMap'; export * from './userMap/userMapConfig'; +export * from './userMap/events'; +export * from './userMap/types'; + export * from './math/bankruptcy'; export * from './orderSubscriber'; export * from './orderSubscriber/types'; diff --git a/sdk/src/math/margin.ts b/sdk/src/math/margin.ts index cba701015e..63832805e4 100644 --- a/sdk/src/math/margin.ts +++ b/sdk/src/math/margin.ts @@ -16,7 +16,7 @@ import { OraclePriceData } from '../oracles/types'; import { calculateMarketMarginRatio } from './market'; import { calculateScaledInitialAssetWeight } from './spotBalance'; import { DriftClient } from '../driftClient'; -import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber'; +import { OneShotUserAccountSubscriber } from '../accounts/userAccount/oneShotUserAccountSubscriber'; import { PerpMarketAccount, PerpPosition, diff --git a/sdk/src/openbook/openbookV2Subscriber.ts b/sdk/src/openbook/openbookV2Subscriber.ts index 39348252ca..696862dfae 100644 --- a/sdk/src/openbook/openbookV2Subscriber.ts +++ b/sdk/src/openbook/openbookV2Subscriber.ts @@ -1,5 +1,5 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; import { PRICE_PRECISION } from '../constants/numericConstants'; import { AnchorProvider, BN, Idl, Program, Wallet } from '@coral-xyz/anchor'; import { L2Level, L2OrderBookGenerator } from '../dlob/orderBookLevels'; diff --git a/sdk/src/orderSubscriber/WebsocketSubscription.ts b/sdk/src/orderSubscriber/WebsocketSubscription.ts index ade8efc6ae..cb305eaa77 100644 --- a/sdk/src/orderSubscriber/WebsocketSubscription.ts +++ b/sdk/src/orderSubscriber/WebsocketSubscription.ts @@ -1,6 +1,6 @@ import { OrderSubscriber } from './OrderSubscriber'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 41101435ab..b681a34ce3 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -1,6 +1,6 @@ import { Context, PublicKey } from '@solana/web3.js'; import { Buffer } from 'buffer'; -import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; diff --git a/sdk/src/phoenix/phoenixSubscriber.ts b/sdk/src/phoenix/phoenixSubscriber.ts index aafcc43190..8a729de09c 100644 --- a/sdk/src/phoenix/phoenixSubscriber.ts +++ b/sdk/src/phoenix/phoenixSubscriber.ts @@ -1,5 +1,5 @@ import { Connection, PublicKey, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; import { Client, deserializeClockData, diff --git a/sdk/src/serum/serumSubscriber.ts b/sdk/src/serum/serumSubscriber.ts index 9806afea9b..f2b0d81a56 100644 --- a/sdk/src/serum/serumSubscriber.ts +++ b/sdk/src/serum/serumSubscriber.ts @@ -1,5 +1,5 @@ import { Connection, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; import { Market, Orderbook } from '@project-serum/serum'; import { SerumMarketSubscriberConfig } from './types'; import { BN } from '@coral-xyz/anchor'; diff --git a/sdk/src/serum/types.ts b/sdk/src/serum/types.ts index d42b37b0c6..88e0c0b910 100644 --- a/sdk/src/serum/types.ts +++ b/sdk/src/serum/types.ts @@ -1,5 +1,5 @@ import { Connection, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; export type SerumMarketSubscriberConfig = { connection: Connection; diff --git a/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts b/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts index ece3be3b92..c2ab64dd59 100644 --- a/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts +++ b/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts @@ -1,5 +1,5 @@ import { Commitment, Context, PublicKey } from '@solana/web3.js'; -import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { SignedMsgUserOrdersAccount } from '../types'; import { getSignedMsgUserOrdersFilter } from '../memcmp'; diff --git a/sdk/src/swift/signedMsgUserAccountSubscriber.ts b/sdk/src/swift/signedMsgUserAccountSubscriber.ts index 6575b5b309..840a936ef0 100644 --- a/sdk/src/swift/signedMsgUserAccountSubscriber.ts +++ b/sdk/src/swift/signedMsgUserAccountSubscriber.ts @@ -1,5 +1,5 @@ import { getSignedMsgUserOrdersFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { SignedMsgOrderId, SignedMsgUserOrdersAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; diff --git a/sdk/src/swift/swiftOrderSubscriber.ts b/sdk/src/swift/swiftOrderSubscriber.ts index bab3032795..dbc6ff28bd 100644 --- a/sdk/src/swift/swiftOrderSubscriber.ts +++ b/sdk/src/swift/swiftOrderSubscriber.ts @@ -3,7 +3,7 @@ import { MainnetPerpMarkets, } from '../constants/perpMarkets'; import { DriftClient } from '../driftClient'; -import { DriftEnv } from '../config'; +import { DriftEnv } from '../config/types'; import { getUserAccountPublicKey, getUserStatsAccountPublicKey, diff --git a/sdk/src/testClient.ts b/sdk/src/testClient.ts index 33af3b4831..9c73f3a6b5 100644 --- a/sdk/src/testClient.ts +++ b/sdk/src/testClient.ts @@ -1,7 +1,7 @@ import { AdminClient } from './adminClient'; import { ConfirmOptions, Signer, Transaction } from '@solana/web3.js'; import { TxSigAndSlot } from './tx/types'; -import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; +import { PollingDriftClientAccountSubscriber } from './accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; import { DriftClientConfig } from './driftClientConfig'; export class TestClient extends AdminClient { diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 215d0450fd..8e3f80769a 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -92,8 +92,8 @@ import { } from './math/margin'; import { OraclePriceData } from './oracles/types'; import { UserConfig } from './userConfig'; -import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber'; -import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber'; +import { PollingUserAccountSubscriber } from './accounts/userAccount/pollingUserAccountSubscriber'; +import { WebSocketUserAccountSubscriber } from './accounts/userAccount/webSocketUserAccountSubscriber'; import { calculateWeightedTokenValue, getWorstCaseTokenAmounts, @@ -107,7 +107,7 @@ import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers'; import { StrictOraclePrice } from './oracles/strictOraclePrice'; import { calculateSpotFuelBonus, calculatePerpFuelBonus } from './math/fuel'; -import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber'; +import { grpcUserAccountSubscriber } from './accounts/userAccount/grpcUserAccountSubscriber'; export class User { driftClient: DriftClient; diff --git a/sdk/src/userConfig.ts b/sdk/src/userConfig.ts index f575ea2b58..004d90c659 100644 --- a/sdk/src/userConfig.ts +++ b/sdk/src/userConfig.ts @@ -1,6 +1,6 @@ import { DriftClient } from './driftClient'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './accounts/bulkAccountLoader'; +import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; import { GrpcConfigs, UserAccountSubscriber } from './accounts/types'; export type UserConfig = { diff --git a/sdk/src/userMap/PollingSubscription.ts b/sdk/src/userMap/PollingSubscription.ts index e398e61e08..45d01b4ed3 100644 --- a/sdk/src/userMap/PollingSubscription.ts +++ b/sdk/src/userMap/PollingSubscription.ts @@ -1,7 +1,7 @@ -import { UserMap } from './userMap'; +import { UserMapInterface } from './types'; export class PollingSubscription { - private userMap: UserMap; + private userMap: UserMapInterface; private frequency: number; private skipInitialLoad: boolean; @@ -12,7 +12,7 @@ export class PollingSubscription { frequency, skipInitialLoad = false, }: { - userMap: UserMap; + userMap: UserMapInterface; frequency: number; skipInitialLoad?: boolean; includeIdle?: boolean; diff --git a/sdk/src/userMap/WebsocketSubscription.ts b/sdk/src/userMap/WebsocketSubscription.ts index f689cfb7d7..41fef10d5a 100644 --- a/sdk/src/userMap/WebsocketSubscription.ts +++ b/sdk/src/userMap/WebsocketSubscription.ts @@ -1,12 +1,12 @@ -import { UserMap } from './userMap'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; +import { UserMapInterface } from './types'; export class WebsocketSubscription { - private userMap: UserMap; + private userMap: UserMapInterface; private commitment: Commitment; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; @@ -25,7 +25,7 @@ export class WebsocketSubscription { decodeFn, additionalFilters = undefined, }: { - userMap: UserMap; + userMap: UserMapInterface; commitment: Commitment; skipInitialLoad?: boolean; resubOpts?: ResubOpts; diff --git a/sdk/src/userMap/events.ts b/sdk/src/userMap/events.ts new file mode 100644 index 0000000000..e3e60e3d26 --- /dev/null +++ b/sdk/src/userMap/events.ts @@ -0,0 +1,7 @@ +import { User } from '../user'; + +export interface UserEvents { + userUpdate: (payload: User) => void; + update: void; + error: (e: Error) => void; +} diff --git a/sdk/src/userMap/grpcSubscription.ts b/sdk/src/userMap/grpcSubscription.ts index a39f8a543c..dcedabafeb 100644 --- a/sdk/src/userMap/grpcSubscription.ts +++ b/sdk/src/userMap/grpcSubscription.ts @@ -1,14 +1,14 @@ -import { UserMap } from './userMap'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; -import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; +import { UserMapInterface } from './types'; export class grpcSubscription { private grpcConfigs: GrpcConfigs; - private userMap: UserMap; + private userMap: UserMapInterface; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; private includeIdle?: boolean; @@ -27,7 +27,7 @@ export class grpcSubscription { additionalFilters = undefined, }: { grpcConfigs: GrpcConfigs; - userMap: UserMap; + userMap: UserMapInterface; skipInitialLoad?: boolean; resubOpts?: ResubOpts; includeIdle?: boolean; diff --git a/sdk/src/userMap/types.ts b/sdk/src/userMap/types.ts new file mode 100644 index 0000000000..77f5cdc1be --- /dev/null +++ b/sdk/src/userMap/types.ts @@ -0,0 +1,47 @@ +import { UserSubscriptionConfig } from '../userConfig'; +import { EventEmitter } from 'events'; +import { StrictEventEmitter } from 'strict-event-emitter-types'; +import { User } from '../user'; +import { UserEvents } from './events'; +import { OrderRecord, UserAccount } from '../types'; +import { PublicKey } from '@solana/web3.js'; +import { DataAndSlot } from '../accounts/types'; +import { DriftClient } from '../driftClient'; + +export interface UserMapInterface { + eventEmitter: StrictEventEmitter; + driftClient: DriftClient; + subscribe(): Promise; + unsubscribe(): Promise; + addPubkey( + userAccountPublicKey: PublicKey, + userAccount?: UserAccount, + slot?: number, + accountSubscription?: UserSubscriptionConfig + ): Promise; + has(key: string): boolean; + get(key: string): User | undefined; + getWithSlot(key: string): DataAndSlot | undefined; + mustGet( + key: string, + accountSubscription?: UserSubscriptionConfig + ): Promise; + mustGetWithSlot( + key: string, + accountSubscription?: UserSubscriptionConfig + ): Promise>; + getUserAuthority(key: string): PublicKey | undefined; + updateWithOrderRecord(record: OrderRecord): Promise; + values(): IterableIterator; + valuesWithSlot(): IterableIterator>; + entries(): IterableIterator<[string, User]>; + entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; + sync(): Promise; + updateUserAccount( + key: string, + userAccount: UserAccount, + slot: number + ): Promise; + updateLatestSlot(slot: number): void; + getSlot(): number; +} diff --git a/sdk/src/userMap/userMap.ts b/sdk/src/userMap/userMap.ts index f022be058a..3f76690475 100644 --- a/sdk/src/userMap/userMap.ts +++ b/sdk/src/userMap/userMap.ts @@ -16,8 +16,8 @@ import { import { WrappedEvent } from '../events/types'; import { DLOB } from '../dlob/DLOB'; import { UserSubscriptionConfig } from '../userConfig'; -import { DataAndSlot, UserEvents } from '../accounts/types'; -import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber'; +import { DataAndSlot } from '../accounts/types'; +import { OneShotUserAccountSubscriber } from '../accounts/userAccount/oneShotUserAccountSubscriber'; import { ProtectMakerParamsMap } from '../dlob/types'; import { @@ -45,38 +45,11 @@ import { decodeUser } from '../decode/user'; import { grpcSubscription } from './grpcSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; +import { UserEvents } from './events'; +import { UserMapInterface } from './types'; const MAX_USER_ACCOUNT_SIZE_BYTES = 4376; -export interface UserMapInterface { - eventEmitter: StrictEventEmitter; - subscribe(): Promise; - unsubscribe(): Promise; - addPubkey( - userAccountPublicKey: PublicKey, - userAccount?: UserAccount, - slot?: number, - accountSubscription?: UserSubscriptionConfig - ): Promise; - has(key: string): boolean; - get(key: string): User | undefined; - getWithSlot(key: string): DataAndSlot | undefined; - mustGet( - key: string, - accountSubscription?: UserSubscriptionConfig - ): Promise; - mustGetWithSlot( - key: string, - accountSubscription?: UserSubscriptionConfig - ): Promise>; - getUserAuthority(key: string): PublicKey | undefined; - updateWithOrderRecord(record: OrderRecord): Promise; - values(): IterableIterator; - valuesWithSlot(): IterableIterator>; - entries(): IterableIterator<[string, User]>; - entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; -} - export class UserMap implements UserMapInterface { private userMap = new Map>(); driftClient: DriftClient; diff --git a/sdk/src/userMap/userStatsMap.ts b/sdk/src/userMap/userStatsMap.ts index 8e9abee863..074ce3eedc 100644 --- a/sdk/src/userMap/userStatsMap.ts +++ b/sdk/src/userMap/userStatsMap.ts @@ -14,8 +14,8 @@ import { } from '../types'; import { UserStats } from '../userStats'; import { WrappedEvent } from '../events/types'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; -import { PollingUserStatsAccountSubscriber } from '../accounts/pollingUserStatsAccountSubscriber'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; +import { PollingUserStatsAccountSubscriber } from '../accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; import { SyncConfig } from './userMapConfig'; import { getUserStatsFilter } from '../memcmp'; import { PublicKey } from '@solana/web3.js'; diff --git a/sdk/src/userStats.ts b/sdk/src/userStats.ts index 8fdb30d2bf..017fd87db4 100644 --- a/sdk/src/userStats.ts +++ b/sdk/src/userStats.ts @@ -2,14 +2,14 @@ import { DriftClient } from './driftClient'; import { PublicKey } from '@solana/web3.js'; import { DataAndSlot, UserStatsAccountSubscriber } from './accounts/types'; import { UserStatsConfig } from './userStatsConfig'; -import { PollingUserStatsAccountSubscriber } from './accounts/pollingUserStatsAccountSubscriber'; -import { WebSocketUserStatsAccountSubscriber } from './accounts/webSocketUserStatsAccountSubsriber'; +import { PollingUserStatsAccountSubscriber } from './accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; +import { WebSocketUserStatsAccountSubscriber } from './accounts/userStatsAccount/webSocketUserStatsAccountSubsriber'; import { ReferrerInfo, SpotMarketAccount, UserStatsAccount } from './types'; import { getUserAccountPublicKeySync, getUserStatsAccountPublicKey, } from './addresses/pda'; -import { grpcUserStatsAccountSubscriber } from './accounts/grpcUserStatsAccountSubscriber'; +import { grpcUserStatsAccountSubscriber } from './accounts/userStatsAccount/grpcUserStatsAccountSubscriber'; import { FUEL_START_TS } from './constants/numericConstants'; import { ZERO } from './constants/numericConstants'; import { diff --git a/sdk/src/userStatsConfig.ts b/sdk/src/userStatsConfig.ts index 693de80e85..b3bfbc87b4 100644 --- a/sdk/src/userStatsConfig.ts +++ b/sdk/src/userStatsConfig.ts @@ -1,6 +1,6 @@ import { DriftClient } from './driftClient'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './accounts/bulkAccountLoader'; +import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; import { GrpcConfigs } from './accounts/types'; export type UserStatsConfig = { diff --git a/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts b/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts index 9058ead1de..e032259e14 100644 --- a/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +++ b/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts @@ -1,5 +1,5 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { CustomizedCadenceBulkAccountLoader } from '../../src/accounts/customizedCadenceBulkAccountLoader'; +import { CustomizedCadenceBulkAccountLoader } from '../../src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader'; import { expect } from 'chai'; describe('CustomizedCadenceBulkAccountLoader', () => { diff --git a/tests/admin.ts b/tests/admin.ts index b92fbd496e..d6d754d953 100644 --- a/tests/admin.ts +++ b/tests/admin.ts @@ -25,7 +25,7 @@ import { BankrunContextWrapper, Connection, } from '../sdk/src/bankrun/bankrunConnection'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; describe('admin', () => { const chProgram = anchor.workspace.Drift as Program; diff --git a/tests/adminDeposit.ts b/tests/adminDeposit.ts index 2eee301d8c..004a2669e4 100644 --- a/tests/adminDeposit.ts +++ b/tests/adminDeposit.ts @@ -19,7 +19,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; dotenv.config(); diff --git a/tests/assetTier.ts b/tests/assetTier.ts index 0c1797b4ba..b5f6ae4293 100644 --- a/tests/assetTier.ts +++ b/tests/assetTier.ts @@ -37,7 +37,7 @@ import { // sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { ContractTier } from '../sdk'; diff --git a/tests/cancelAllOrders.ts b/tests/cancelAllOrders.ts index d3cd08fcdb..4a10f61255 100644 --- a/tests/cancelAllOrders.ts +++ b/tests/cancelAllOrders.ts @@ -19,7 +19,7 @@ import { mockOracleNoProgram, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { isVariant } from '../sdk'; diff --git a/tests/cappedSymFunding.ts b/tests/cappedSymFunding.ts index a95fa58e1e..a1c1350e2f 100644 --- a/tests/cappedSymFunding.ts +++ b/tests/cappedSymFunding.ts @@ -36,7 +36,7 @@ import { Program } from '@coral-xyz/anchor'; import { Keypair, PublicKey } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function updateFundingRateHelper( diff --git a/tests/curve.ts b/tests/curve.ts index ff47909549..97f3e4a641 100644 --- a/tests/curve.ts +++ b/tests/curve.ts @@ -28,7 +28,7 @@ import { setFeedPriceNoProgram, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('AMM Curve', () => { diff --git a/tests/deleteInitializedSpotMarket.ts b/tests/deleteInitializedSpotMarket.ts index f66267e3e8..60cee45fb8 100644 --- a/tests/deleteInitializedSpotMarket.ts +++ b/tests/deleteInitializedSpotMarket.ts @@ -23,7 +23,7 @@ import { } from '../sdk'; import { PublicKey } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max deposit', () => { diff --git a/tests/depositIntoSpotMarketVault.ts b/tests/depositIntoSpotMarketVault.ts index 476d984613..5ef2b4cea1 100644 --- a/tests/depositIntoSpotMarketVault.ts +++ b/tests/depositIntoSpotMarketVault.ts @@ -24,7 +24,7 @@ import { } from './testHelpers'; import { SPOT_MARKET_BALANCE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/driftClient.ts b/tests/driftClient.ts index 6271269e39..1f29bc9bc9 100644 --- a/tests/driftClient.ts +++ b/tests/driftClient.ts @@ -32,7 +32,7 @@ import { sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('drift client', () => { diff --git a/tests/fillSpot.ts b/tests/fillSpot.ts index 2b141d1254..cbdef680d8 100644 --- a/tests/fillSpot.ts +++ b/tests/fillSpot.ts @@ -27,7 +27,7 @@ import { } from './testHelpers'; import { MARGIN_PRECISION, PostOnlyParams, ReferrerInfo, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('place and fill spot order', () => { diff --git a/tests/forceUserDelete.ts b/tests/forceUserDelete.ts index d224e6c7ec..e13b2efa84 100644 --- a/tests/forceUserDelete.ts +++ b/tests/forceUserDelete.ts @@ -26,7 +26,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { QUOTE_PRECISION, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/fuel.ts b/tests/fuel.ts index 4514836014..c15ce110ac 100644 --- a/tests/fuel.ts +++ b/tests/fuel.ts @@ -32,7 +32,7 @@ import { import { QUOTE_PRECISION, calculatePerpFuelBonus } from '../sdk/src'; import { MARGIN_PRECISION, PostOnlyParams, ReferrerInfo, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe("fuelin'", () => { diff --git a/tests/fuelSweep.ts b/tests/fuelSweep.ts index a6890c45d8..b38062c8ea 100644 --- a/tests/fuelSweep.ts +++ b/tests/fuelSweep.ts @@ -21,7 +21,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; dotenv.config(); diff --git a/tests/govStakeDevnet.ts b/tests/govStakeDevnet.ts index bd7de44653..6eb41ee14f 100644 --- a/tests/govStakeDevnet.ts +++ b/tests/govStakeDevnet.ts @@ -12,7 +12,7 @@ import { } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { diff --git a/tests/highLeverageMode.ts b/tests/highLeverageMode.ts index 148c41ce92..70b5b48269 100644 --- a/tests/highLeverageMode.ts +++ b/tests/highLeverageMode.ts @@ -33,7 +33,7 @@ import { PERCENTAGE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max leverage order params', () => { diff --git a/tests/ifRebalance.ts b/tests/ifRebalance.ts index dcf4373dbf..14d6135d2b 100644 --- a/tests/ifRebalance.ts +++ b/tests/ifRebalance.ts @@ -39,7 +39,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { DexInstructions, Market, OpenOrders } from '@project-serum/serum'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot swap', () => { diff --git a/tests/imbalancePerpPnl.ts b/tests/imbalancePerpPnl.ts index 84fbf8dd4b..7486effc5a 100644 --- a/tests/imbalancePerpPnl.ts +++ b/tests/imbalancePerpPnl.ts @@ -47,7 +47,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function depositToFeePoolFromIF( diff --git a/tests/insuranceFundStake.ts b/tests/insuranceFundStake.ts index c90a1795ce..1ee2b8593d 100644 --- a/tests/insuranceFundStake.ts +++ b/tests/insuranceFundStake.ts @@ -43,7 +43,7 @@ import { } from './testHelpers'; import { ContractTier, PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper, asBN, diff --git a/tests/ksolver.ts b/tests/ksolver.ts index d3c1d338fc..5db4c24709 100644 --- a/tests/ksolver.ts +++ b/tests/ksolver.ts @@ -24,7 +24,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('AMM Curve', () => { diff --git a/tests/liquidateBorrowForPerpPnl.ts b/tests/liquidateBorrowForPerpPnl.ts index eab00eae13..f884140e3c 100644 --- a/tests/liquidateBorrowForPerpPnl.ts +++ b/tests/liquidateBorrowForPerpPnl.ts @@ -31,7 +31,7 @@ import { } from './testHelpers'; import { isVariant, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate borrow for perp pnl', () => { diff --git a/tests/liquidateMaxLps.ts b/tests/liquidateMaxLps.ts index 90d4c72190..30ee5b985c 100644 --- a/tests/liquidateMaxLps.ts +++ b/tests/liquidateMaxLps.ts @@ -24,7 +24,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max lp liq', () => { diff --git a/tests/liquidatePerp.ts b/tests/liquidatePerp.ts index ed4156bdbc..2df9deb212 100644 --- a/tests/liquidatePerp.ts +++ b/tests/liquidatePerp.ts @@ -31,7 +31,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp (no open orders)', () => { diff --git a/tests/liquidatePerpAndLp.ts b/tests/liquidatePerpAndLp.ts index 26edfcf242..c09de10331 100644 --- a/tests/liquidatePerpAndLp.ts +++ b/tests/liquidatePerpAndLp.ts @@ -33,7 +33,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp and lp', () => { diff --git a/tests/liquidatePerpPnlForDeposit.ts b/tests/liquidatePerpPnlForDeposit.ts index cb42c23adb..65c38af0a1 100644 --- a/tests/liquidatePerpPnlForDeposit.ts +++ b/tests/liquidatePerpPnlForDeposit.ts @@ -36,7 +36,7 @@ import { UserStatus, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp pnl for deposit', () => { diff --git a/tests/liquidatePerpWithFill.ts b/tests/liquidatePerpWithFill.ts index f4d0b61cd3..ed19633acb 100644 --- a/tests/liquidatePerpWithFill.ts +++ b/tests/liquidatePerpWithFill.ts @@ -28,7 +28,7 @@ import { } from './testHelpers'; import { OrderType, PERCENTAGE_PRECISION, PerpOperation } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp (no open orders)', () => { diff --git a/tests/liquidateSpot.ts b/tests/liquidateSpot.ts index 9b30c4bb27..e56dc6b736 100644 --- a/tests/liquidateSpot.ts +++ b/tests/liquidateSpot.ts @@ -34,7 +34,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate spot', () => { diff --git a/tests/liquidateSpotSocialLoss.ts b/tests/liquidateSpotSocialLoss.ts index 0695db6b18..ca49933deb 100644 --- a/tests/liquidateSpotSocialLoss.ts +++ b/tests/liquidateSpotSocialLoss.ts @@ -29,7 +29,7 @@ import { } from './testHelpers'; import { isVariant, UserStatus, PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate spot w/ social loss', () => { diff --git a/tests/liquidateSpotWithSwap.ts b/tests/liquidateSpotWithSwap.ts index f6966324ee..47abd36266 100644 --- a/tests/liquidateSpotWithSwap.ts +++ b/tests/liquidateSpotWithSwap.ts @@ -34,7 +34,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { DexInstructions, Market, OpenOrders } from '@project-serum/serum'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot swap', () => { diff --git a/tests/liquidityProvider.ts b/tests/liquidityProvider.ts index 961fdf3956..10e09b9b41 100644 --- a/tests/liquidityProvider.ts +++ b/tests/liquidityProvider.ts @@ -37,7 +37,7 @@ import { } from './testHelpers'; import { PerpPosition } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function adjustOraclePostSwap(baa, swapDirection, market, context) { diff --git a/tests/marketOrder.ts b/tests/marketOrder.ts index f7303296f2..5b9d6a2d95 100644 --- a/tests/marketOrder.ts +++ b/tests/marketOrder.ts @@ -35,7 +35,7 @@ import { getAssociatedTokenAddressSync, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('market order', () => { diff --git a/tests/marketOrderBaseAssetAmount.ts b/tests/marketOrderBaseAssetAmount.ts index e541222f97..f4494cbfa9 100644 --- a/tests/marketOrderBaseAssetAmount.ts +++ b/tests/marketOrderBaseAssetAmount.ts @@ -15,7 +15,7 @@ import { mockOracleNoProgram, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('market orders', () => { diff --git a/tests/maxDeposit.ts b/tests/maxDeposit.ts index 626ff2fdec..57ed8d5559 100644 --- a/tests/maxDeposit.ts +++ b/tests/maxDeposit.ts @@ -12,7 +12,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max deposit', () => { diff --git a/tests/maxLeverageOrderParams.ts b/tests/maxLeverageOrderParams.ts index 4695105240..be8e08e954 100644 --- a/tests/maxLeverageOrderParams.ts +++ b/tests/maxLeverageOrderParams.ts @@ -31,7 +31,7 @@ import { PERCENTAGE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max leverage order params', () => { diff --git a/tests/modifyOrder.ts b/tests/modifyOrder.ts index 9f67eea3f2..a1f4ed90fc 100644 --- a/tests/modifyOrder.ts +++ b/tests/modifyOrder.ts @@ -20,7 +20,7 @@ import { } from './testHelpers'; import { OrderType, TWO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('modify orders', () => { diff --git a/tests/multipleMakerOrders.ts b/tests/multipleMakerOrders.ts index af5473510a..21aca163e5 100644 --- a/tests/multipleMakerOrders.ts +++ b/tests/multipleMakerOrders.ts @@ -26,7 +26,7 @@ import { } from './testHelpers'; import { ContractTier, MARGIN_PRECISION, OrderType } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('multiple maker orders', () => { diff --git a/tests/multipleSpotMakerOrders.ts b/tests/multipleSpotMakerOrders.ts index 411e79e474..f4bf461e5f 100644 --- a/tests/multipleSpotMakerOrders.ts +++ b/tests/multipleSpotMakerOrders.ts @@ -29,7 +29,7 @@ import { MARGIN_PRECISION, OrderType, SpotOperation } from '../sdk/src'; import { LAMPORTS_PER_SOL } from '@solana/web3.js'; import { ContractTier } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('multiple maker orders', () => { diff --git a/tests/openbookTest.ts b/tests/openbookTest.ts index 3d57b25d99..16e49c6c5f 100644 --- a/tests/openbookTest.ts +++ b/tests/openbookTest.ts @@ -12,7 +12,7 @@ import { } from '../sdk/src'; import openbookIDL from '../sdk/src/idl/openbook.json'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { createOpenOrdersAccount, diff --git a/tests/oracleDiffSources.ts b/tests/oracleDiffSources.ts index 3971f60370..e432fc0851 100644 --- a/tests/oracleDiffSources.ts +++ b/tests/oracleDiffSources.ts @@ -28,7 +28,7 @@ import { } from './testHelpers'; // import { PRICE_PRECISION, PEG_PRECISION, Wallet, DriftClient } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('oracle diff sources', () => { diff --git a/tests/oracleFillPriceGuardrails.ts b/tests/oracleFillPriceGuardrails.ts index dc0b5758ca..c6215322ba 100644 --- a/tests/oracleFillPriceGuardrails.ts +++ b/tests/oracleFillPriceGuardrails.ts @@ -31,7 +31,7 @@ import { PostOnlyParams, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('oracle fill guardrails', () => { diff --git a/tests/oracleOffsetOrders.ts b/tests/oracleOffsetOrders.ts index 2f498c2e5d..0037353532 100644 --- a/tests/oracleOffsetOrders.ts +++ b/tests/oracleOffsetOrders.ts @@ -30,7 +30,7 @@ import { } from './testHelpers'; import { calculateEntryPrice, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('oracle offset', () => { diff --git a/tests/order.ts b/tests/order.ts index a7e8f1b45d..d603a268c1 100644 --- a/tests/order.ts +++ b/tests/order.ts @@ -47,7 +47,7 @@ import { ZERO, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; const enumsAreEqual = ( diff --git a/tests/ordersWithSpread.ts b/tests/ordersWithSpread.ts index b3d82dc518..e7b589bb27 100644 --- a/tests/ordersWithSpread.ts +++ b/tests/ordersWithSpread.ts @@ -34,7 +34,7 @@ import { PEG_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('amm spread: market order', () => { diff --git a/tests/overwritePerpAccounts.ts b/tests/overwritePerpAccounts.ts index 989558f399..fdbf3cac3a 100644 --- a/tests/overwritePerpAccounts.ts +++ b/tests/overwritePerpAccounts.ts @@ -18,7 +18,7 @@ import { overWritePerpMarket, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { diff --git a/tests/pauseDepositWithdraw.ts b/tests/pauseDepositWithdraw.ts index 830cf6d5d3..5360f124d0 100644 --- a/tests/pauseDepositWithdraw.ts +++ b/tests/pauseDepositWithdraw.ts @@ -36,7 +36,7 @@ import { SpotOperation, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw 22', () => { diff --git a/tests/pauseExchange.ts b/tests/pauseExchange.ts index ab2d24d013..ddc0329a2e 100644 --- a/tests/pauseExchange.ts +++ b/tests/pauseExchange.ts @@ -19,7 +19,7 @@ import { initializeQuoteSpotMarket, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('Pause exchange', () => { diff --git a/tests/perpLpJit.ts b/tests/perpLpJit.ts index fce1546a06..6cd4aa53a8 100644 --- a/tests/perpLpJit.ts +++ b/tests/perpLpJit.ts @@ -36,7 +36,7 @@ import { // sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; let lastOrderRecordsLength = 0; diff --git a/tests/perpLpRiskMitigation.ts b/tests/perpLpRiskMitigation.ts index 12b8037f31..22c8ecd0c3 100644 --- a/tests/perpLpRiskMitigation.ts +++ b/tests/perpLpRiskMitigation.ts @@ -35,7 +35,7 @@ import { // sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function adjustOraclePostSwap(baa, swapDirection, market, context) { diff --git a/tests/phoenixTest.ts b/tests/phoenixTest.ts index 5d1bf8edbc..c8a0cbc9f3 100644 --- a/tests/phoenixTest.ts +++ b/tests/phoenixTest.ts @@ -45,7 +45,7 @@ import { deserializeMarketData, TokenConfig } from '@ellipsis-labs/phoenix-sdk'; import * as Phoenix from '@ellipsis-labs/phoenix-sdk'; import { assert } from 'chai'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { BankrunProvider } from 'anchor-bankrun'; import { diff --git a/tests/placeAndMakePerp.ts b/tests/placeAndMakePerp.ts index efdea60451..1b25409f71 100644 --- a/tests/placeAndMakePerp.ts +++ b/tests/placeAndMakePerp.ts @@ -27,7 +27,7 @@ import { } from './testHelpers'; import { PEG_PRECISION, PerpOperation, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('place and make perp order', () => { diff --git a/tests/placeAndMakeSignedMsgBankrun.ts b/tests/placeAndMakeSignedMsgBankrun.ts index 173e1364c4..5e948e36fb 100644 --- a/tests/placeAndMakeSignedMsgBankrun.ts +++ b/tests/placeAndMakeSignedMsgBankrun.ts @@ -62,7 +62,7 @@ import { PostOnlyParams, } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { nanoid } from 'nanoid'; diff --git a/tests/placeAndMakeSpotOrder.ts b/tests/placeAndMakeSpotOrder.ts index b5ec4cca97..22f4da8428 100644 --- a/tests/placeAndMakeSpotOrder.ts +++ b/tests/placeAndMakeSpotOrder.ts @@ -28,7 +28,7 @@ import { } from './testHelpers'; import { MARGIN_PRECISION, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('place and make spot order', () => { diff --git a/tests/postOnly.ts b/tests/postOnly.ts index f680dedbaa..b9ae5e95b2 100644 --- a/tests/postOnly.ts +++ b/tests/postOnly.ts @@ -33,7 +33,7 @@ import { ZERO, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('post only', () => { diff --git a/tests/postOnlyAmmFulfillment.ts b/tests/postOnlyAmmFulfillment.ts index 388f0fc595..085177f220 100644 --- a/tests/postOnlyAmmFulfillment.ts +++ b/tests/postOnlyAmmFulfillment.ts @@ -33,7 +33,7 @@ import { } from './testHelpers'; import { convertToNumber, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('post only maker order w/ amm fulfillments', () => { diff --git a/tests/prelisting.ts b/tests/prelisting.ts index 92f7a510af..e26889d75b 100644 --- a/tests/prelisting.ts +++ b/tests/prelisting.ts @@ -28,7 +28,7 @@ import { PostOnlyParams, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('prelisting', () => { diff --git a/tests/pyth.ts b/tests/pyth.ts index d437bf60e8..c9a066f020 100644 --- a/tests/pyth.ts +++ b/tests/pyth.ts @@ -29,7 +29,7 @@ import { QUOTE_SPOT_MARKET_INDEX, } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function updateFundingRateHelper( diff --git a/tests/pythLazerBankrun.ts b/tests/pythLazerBankrun.ts index 7e1721bea9..fd0b976209 100644 --- a/tests/pythLazerBankrun.ts +++ b/tests/pythLazerBankrun.ts @@ -12,7 +12,7 @@ import { getPythLazerOraclePublicKey, isVariant, } from '../sdk/src'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { startAnchor } from 'solana-bankrun'; import { AccountInfo, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; diff --git a/tests/pythPull.ts b/tests/pythPull.ts index 5118651af6..14f333ce12 100644 --- a/tests/pythPull.ts +++ b/tests/pythPull.ts @@ -5,7 +5,7 @@ import { TestClient, getPythPullOraclePublicKey, } from '../sdk/src'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { startAnchor } from 'solana-bankrun'; import { AccountInfo, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; diff --git a/tests/referencePriceOffset.ts b/tests/referencePriceOffset.ts index 5bfa89007e..5e3b910150 100644 --- a/tests/referencePriceOffset.ts +++ b/tests/referencePriceOffset.ts @@ -30,7 +30,7 @@ import { placeAndFillVammTrade, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { diff --git a/tests/referrer.ts b/tests/referrer.ts index a310bcd24a..e712b2c98f 100644 --- a/tests/referrer.ts +++ b/tests/referrer.ts @@ -31,7 +31,7 @@ import { } from '../sdk/src'; import { decodeName } from '../sdk/src/userName'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('referrer', () => { diff --git a/tests/resizeSwiftUserOrderIds.ts b/tests/resizeSwiftUserOrderIds.ts index e6ffa6d425..2aaf0a9e69 100644 --- a/tests/resizeSwiftUserOrderIds.ts +++ b/tests/resizeSwiftUserOrderIds.ts @@ -25,7 +25,7 @@ import { } from './testHelpers'; import { PEG_PRECISION } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; dotenv.config(); diff --git a/tests/roundInFavorBaseAsset.ts b/tests/roundInFavorBaseAsset.ts index c1a3b7dec0..5e57443d66 100644 --- a/tests/roundInFavorBaseAsset.ts +++ b/tests/roundInFavorBaseAsset.ts @@ -22,7 +22,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('round in favor', () => { diff --git a/tests/serumTest.ts b/tests/serumTest.ts index 7ede64540d..c683edb2bb 100644 --- a/tests/serumTest.ts +++ b/tests/serumTest.ts @@ -33,7 +33,7 @@ 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 { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('serum spot market', () => { diff --git a/tests/signedMsgWsDelegates.ts b/tests/signedMsgWsDelegates.ts index 990be3bb75..90f9227a70 100644 --- a/tests/signedMsgWsDelegates.ts +++ b/tests/signedMsgWsDelegates.ts @@ -20,7 +20,7 @@ import { import { mockOracleNoProgram } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { PYTH_STORAGE_DATA } from './pythLazerData'; diff --git a/tests/spotDepositWithdraw.ts b/tests/spotDepositWithdraw.ts index 71b412b9b6..f9565bf604 100644 --- a/tests/spotDepositWithdraw.ts +++ b/tests/spotDepositWithdraw.ts @@ -41,7 +41,7 @@ import { PRICE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/spotDepositWithdraw22.ts b/tests/spotDepositWithdraw22.ts index 5bcd5cf5f1..339fff0953 100644 --- a/tests/spotDepositWithdraw22.ts +++ b/tests/spotDepositWithdraw22.ts @@ -41,7 +41,7 @@ import { PRICE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw 22', () => { diff --git a/tests/spotDepositWithdraw22TransferHooks.ts b/tests/spotDepositWithdraw22TransferHooks.ts index f01043172e..6466886fe7 100644 --- a/tests/spotDepositWithdraw22TransferHooks.ts +++ b/tests/spotDepositWithdraw22TransferHooks.ts @@ -51,7 +51,7 @@ import { resolveExtraAccountMeta, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { initializeExtraAccountMetaList } from './splTransferHookClient'; diff --git a/tests/spotMarketPoolIds.ts b/tests/spotMarketPoolIds.ts index 1395856599..1b2ce5c087 100644 --- a/tests/spotMarketPoolIds.ts +++ b/tests/spotMarketPoolIds.ts @@ -25,7 +25,7 @@ import { } from './testHelpers'; import { QUOTE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/spotSwap.ts b/tests/spotSwap.ts index f60331cc4f..5aaa985c30 100644 --- a/tests/spotSwap.ts +++ b/tests/spotSwap.ts @@ -40,7 +40,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { DexInstructions, Market, OpenOrders } from '@project-serum/serum'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { DRIFT_PROGRAM_ID } from '../sdk/src'; diff --git a/tests/spotSwap22.ts b/tests/spotSwap22.ts index a876a74504..de9b72a350 100644 --- a/tests/spotSwap22.ts +++ b/tests/spotSwap22.ts @@ -36,7 +36,7 @@ import { createTransferInstruction, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { DRIFT_PROGRAM_ID } from '../sdk/src'; diff --git a/tests/spotWithdrawUtil100.ts b/tests/spotWithdrawUtil100.ts index 25cf31c10d..17568581fb 100644 --- a/tests/spotWithdrawUtil100.ts +++ b/tests/spotWithdrawUtil100.ts @@ -46,7 +46,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { ContractTier } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('test function when spot market at >= 100% util', () => { diff --git a/tests/stopLimits.ts b/tests/stopLimits.ts index 3bcae9d15a..2edd036a7b 100644 --- a/tests/stopLimits.ts +++ b/tests/stopLimits.ts @@ -33,7 +33,7 @@ import { getAssociatedTokenAddressSync, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('stop limit', () => { diff --git a/tests/subaccounts.ts b/tests/subaccounts.ts index 2f6f5c5cd2..79aac79eba 100644 --- a/tests/subaccounts.ts +++ b/tests/subaccounts.ts @@ -30,7 +30,7 @@ import { } from '../sdk'; import { PublicKey } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('subaccounts', () => { diff --git a/tests/surgePricing.ts b/tests/surgePricing.ts index 4402201060..26f19bfaac 100644 --- a/tests/surgePricing.ts +++ b/tests/surgePricing.ts @@ -26,7 +26,7 @@ import { import { QUOTE_PRECISION, getUserAccountPublicKey } from '../sdk/src'; import { calculateInitUserFee } from '../sdk/src/math/state'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('surge pricing', () => { diff --git a/tests/switchOracle.ts b/tests/switchOracle.ts index 7cd425e8c4..6980a712ec 100644 --- a/tests/switchOracle.ts +++ b/tests/switchOracle.ts @@ -25,7 +25,7 @@ import { } from './testHelpers'; import { PRICE_PRECISION, PEG_PRECISION, Wallet, DriftClient } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('switch oracles', () => { diff --git a/tests/switchboardOnDemand.ts b/tests/switchboardOnDemand.ts index 81e04d65d2..278294639e 100644 --- a/tests/switchboardOnDemand.ts +++ b/tests/switchboardOnDemand.ts @@ -1,7 +1,7 @@ import * as anchor from '@coral-xyz/anchor'; import { Program } from '@coral-xyz/anchor'; import { AccountInfo, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { initializeQuoteSpotMarket, mockUSDCMint } from './testHelpers'; import { OracleSource, TestClient } from '../sdk/src'; diff --git a/tests/switchboardTxCus.ts b/tests/switchboardTxCus.ts index fab886314d..abb4ffb23c 100644 --- a/tests/switchboardTxCus.ts +++ b/tests/switchboardTxCus.ts @@ -30,7 +30,7 @@ import { PostOnlyParams, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('switchboard place orders cus', () => { diff --git a/tests/testHelpers.ts b/tests/testHelpers.ts index df4e742abe..45d777eb99 100644 --- a/tests/testHelpers.ts +++ b/tests/testHelpers.ts @@ -58,7 +58,7 @@ import { BankrunConnection, } from '../sdk/src/bankrun/bankrunConnection'; import pythIDL from '../sdk/src/idl/pyth.json'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; export async function mockOracle( price: number = 50 * 10e7, diff --git a/tests/tokenFaucet.ts b/tests/tokenFaucet.ts index c18cf15699..a9afed75d4 100644 --- a/tests/tokenFaucet.ts +++ b/tests/tokenFaucet.ts @@ -12,7 +12,7 @@ import { unpackMint, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('token faucet', () => { diff --git a/tests/tradingLP.ts b/tests/tradingLP.ts index 565178183e..8755c2095b 100644 --- a/tests/tradingLP.ts +++ b/tests/tradingLP.ts @@ -21,7 +21,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function createNewUser( diff --git a/tests/transferPerpPosition.ts b/tests/transferPerpPosition.ts index d2471a94d9..0171ce29e0 100644 --- a/tests/transferPerpPosition.ts +++ b/tests/transferPerpPosition.ts @@ -21,7 +21,7 @@ import { } from './testHelpers'; import { BASE_PRECISION, OracleSource, PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; function getOpenInterest(driftClient: TestClient, marketIndex: number) { diff --git a/tests/transferPools.ts b/tests/transferPools.ts index a79d863601..08632c7e7c 100644 --- a/tests/transferPools.ts +++ b/tests/transferPools.ts @@ -26,7 +26,7 @@ import { } from './testHelpers'; import { QUOTE_PRECISION, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { NATIVE_MINT } from '@solana/spl-token'; diff --git a/tests/triggerOrders.ts b/tests/triggerOrders.ts index e3eb196c84..85785e2740 100644 --- a/tests/triggerOrders.ts +++ b/tests/triggerOrders.ts @@ -35,7 +35,7 @@ import { ZERO, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('trigger orders', () => { diff --git a/tests/triggerSpotOrder.ts b/tests/triggerSpotOrder.ts index 0f9277e2c3..a8808c2106 100644 --- a/tests/triggerSpotOrder.ts +++ b/tests/triggerSpotOrder.ts @@ -32,7 +32,7 @@ import { PERCENTAGE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('trigger orders', () => { diff --git a/tests/userAccount.ts b/tests/userAccount.ts index 96384ba98a..6f2f00e26b 100644 --- a/tests/userAccount.ts +++ b/tests/userAccount.ts @@ -30,7 +30,7 @@ import { AMM_RESERVE_PRECISION, } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('User Account', () => { diff --git a/tests/userDelegate.ts b/tests/userDelegate.ts index 80dafcb539..4ed2660898 100644 --- a/tests/userDelegate.ts +++ b/tests/userDelegate.ts @@ -24,7 +24,7 @@ import { import { assert } from 'chai'; import { Keypair } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('user delegate', () => { diff --git a/tests/userOrderId.ts b/tests/userOrderId.ts index 9ed86fd4f4..7840e056d6 100644 --- a/tests/userOrderId.ts +++ b/tests/userOrderId.ts @@ -24,7 +24,7 @@ import { } from './testHelpers'; import { ContractTier, ExchangeStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('user order id', () => { diff --git a/tests/whitelist.ts b/tests/whitelist.ts index 66c5f15ead..f7bb7b3c3e 100644 --- a/tests/whitelist.ts +++ b/tests/whitelist.ts @@ -28,7 +28,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('whitelist', () => { From 8b8b824f5e41ff1976248803f121b37cd3c0b4fc Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:08:53 +0000 Subject: [PATCH 018/216] sdk: release v2.130.0-beta.9 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b5ed2f207b..2603f83184 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.8 \ No newline at end of file +2.130.0-beta.9 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index e7db8aa1bf..a9b6073488 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.8", + "version": "2.130.0-beta.9", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 24e12752ef7775c35483f336294a6704439230db Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 29 Jul 2025 22:58:14 +0800 Subject: [PATCH 019/216] Improve Circular Dependencies 2 (#1778) --- bun.lock | 1072 ++++++++ sdk/src/dlob/DLOB.ts | 72 +- sdk/src/dlob/DLOBSubscriber.ts | 9 +- sdk/src/dlob/orderBookLevels.ts | 3 +- sdk/src/dlob/types.ts | 432 +++- .../{driftClient.ts => driftClient/index.ts} | 94 +- sdk/src/driftClient/types.ts | 2204 +++++++++++++++++ sdk/src/events/types.ts | 2 +- sdk/src/math/amm.ts | 2 +- sdk/src/math/insurance.ts | 3 +- sdk/src/math/market.ts | 4 +- sdk/src/math/oracles.ts | 16 +- sdk/src/math/orders.ts | 9 +- sdk/src/math/utils.ts | 8 + sdk/src/orderSubscriber/OrderSubscriber.ts | 15 +- .../orderSubscriber/PollingSubscription.ts | 6 +- .../orderSubscriber/WebsocketSubscription.ts | 6 +- sdk/src/orderSubscriber/grpcSubscription.ts | 6 +- sdk/src/orderSubscriber/types.ts | 45 +- sdk/src/swift/swiftOrderSubscriber.ts | 6 +- sdk/src/{user.ts => user/index.ts} | 69 +- sdk/src/user/types.ts | 730 ++++++ sdk/src/userMap/PollingSubscription.ts | 6 +- sdk/src/userMap/WebsocketSubscription.ts | 6 +- sdk/src/userMap/grpcSubscription.ts | 6 +- sdk/src/userMap/types.ts | 77 +- sdk/src/userMap/userMap.ts | 4 +- sdk/src/{userStats.ts => userStats/index.ts} | 29 +- sdk/src/userStats/types.ts | 23 + 29 files changed, 4745 insertions(+), 219 deletions(-) create mode 100644 bun.lock rename sdk/src/{driftClient.ts => driftClient/index.ts} (99%) create mode 100644 sdk/src/driftClient/types.ts rename sdk/src/{user.ts => user/index.ts} (98%) create mode 100644 sdk/src/user/types.ts rename sdk/src/{userStats.ts => userStats/index.ts} (83%) create mode 100644 sdk/src/userStats/types.ts diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000000..5f7ade575e --- /dev/null +++ b/bun.lock @@ -0,0 +1,1072 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "dependencies": { + "@ellipsis-labs/phoenix-sdk": "1.4.2", + "@pythnetwork/pyth-solana-receiver": "0.8.0", + "@switchboard-xyz/common": "3.0.14", + "@switchboard-xyz/on-demand": "2.4.1", + "anchor-bankrun": "0.3.0", + "chai-bn": "0.2.2", + "csvtojson": "2.0.10", + "dotenv": "16.4.5", + "json2csv": "5.0.7", + "nanoid": "3.3.4", + "rpc-websockets": "7.5.1", + "solana-bankrun": "0.3.0", + "zstddec": "0.1.0", + }, + "devDependencies": { + "@coral-xyz/anchor": "0.29.0", + "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1", + "@project-serum/common": "0.0.1-beta.3", + "@project-serum/serum": "0.13.65", + "@pythnetwork/client": "2.21.0", + "@pythnetwork/price-service-client": "1.9.0", + "@solana/spl-token": "0.4.13", + "@solana/web3.js": "1.73.2", + "@types/bn.js": "5.1.6", + "@types/chai": "5.0.0", + "@types/mocha": "8.2.3", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", + "chai": "4.4.1", + "eslint": "8.57.0", + "eslint-config-prettier": "8.3.0", + "eslint-plugin-prettier": "3.4.0", + "husky": "7.0.4", + "prettier": "3.0.1", + "typedoc": "0.23.23", + "typescript": "5.4.5", + }, + }, + }, + "packages": { + "@babel/runtime": ["@babel/runtime@7.28.2", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="], + + "@coral-xyz/anchor": ["@coral-xyz/anchor@0.29.0", "", { "dependencies": { "@coral-xyz/borsh": "^0.29.0", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA=="], + + "@coral-xyz/anchor-30": ["@coral-xyz/anchor@0.30.1", "", { "dependencies": { "@coral-xyz/anchor-errors": "^0.30.1", "@coral-xyz/borsh": "^0.30.1", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ=="], + + "@coral-xyz/anchor-errors": ["@coral-xyz/anchor-errors@0.30.1", "", {}, "sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ=="], + + "@coral-xyz/borsh": ["@coral-xyz/borsh@0.29.0", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ=="], + + "@ellipsis-labs/phoenix-sdk": ["@ellipsis-labs/phoenix-sdk@1.4.2", "", { "dependencies": { "@metaplex-foundation/beet": "^0.7.1", "@metaplex-foundation/rustbin": "^0.3.1", "@metaplex-foundation/solita": "^0.12.2", "@solana/spl-token": "^0.3.7", "@types/node": "^18.11.13", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^5.0.0" } }, "sha512-7Rf2aWHZwuLX8jcrNSRUDf2aHuBnBzsDBN4GzClTdJYVGo4uQzf9ixju5J3apZ+xkQ6qvrEVYOXtogdgOhJFvw=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + + "@eslint/js": ["@eslint/js@8.57.0", "", {}, "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g=="], + + "@grpc/grpc-js": ["@grpc/grpc-js@1.13.4", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg=="], + + "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + + "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.11.14", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], + + "@isaacs/ttlcache": ["@isaacs/ttlcache@1.4.1", "", {}, "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="], + + "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], + + "@metaplex-foundation/beet": ["@metaplex-foundation/beet@0.7.2", "", { "dependencies": { "ansicolors": "^0.3.2", "assert": "^2.1.0", "bn.js": "^5.2.0", "debug": "^4.3.3" } }, "sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg=="], + + "@metaplex-foundation/beet-solana": ["@metaplex-foundation/beet-solana@0.3.1", "", { "dependencies": { "@metaplex-foundation/beet": ">=0.1.0", "@solana/web3.js": "^1.56.2", "bs58": "^5.0.0", "debug": "^4.3.4" } }, "sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g=="], + + "@metaplex-foundation/rustbin": ["@metaplex-foundation/rustbin@0.3.5", "", { "dependencies": { "debug": "^4.3.3", "semver": "^7.3.7", "text-table": "^0.2.0", "toml": "^3.0.0" } }, "sha512-m0wkRBEQB/8krwMwKBvFugufZtYwMXiGHud2cTDAv+aGXK4M90y0Hx67/wpu+AqqoQfdV8VM9YezUOHKD+Z5kA=="], + + "@metaplex-foundation/solita": ["@metaplex-foundation/solita@0.12.2", "", { "dependencies": { "@metaplex-foundation/beet": "^0.4.0", "@metaplex-foundation/beet-solana": "^0.3.0", "@metaplex-foundation/rustbin": "^0.3.0", "@solana/web3.js": "^1.36.0", "camelcase": "^6.2.1", "debug": "^4.3.3", "js-sha256": "^0.9.0", "prettier": "^2.5.1", "snake-case": "^3.0.4", "spok": "^1.4.3" }, "bin": { "solita": "dist/src/cli/solita.js" } }, "sha512-oczMfE43NNHWweSqhXPTkQBUbap/aAiwjDQw8zLKNnd/J8sXr/0+rKcN5yJIEgcHeKRkp90eTqkmt2WepQc8yw=="], + + "@noble/curves": ["@noble/curves@1.9.5", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-IHiC8xU74NLKg7gNmwMbUVtqqZy9OWKphTAChESCgsXI5NTK6n3ewOFXrj4Dxal/Ml8D3msbPIHfpHLwv50Q2w=="], + + "@noble/ed25519": ["@noble/ed25519@1.7.5", "", {}, "sha512-xuS0nwRMQBvSxDa7UxMb61xTiH3MxTgUfhyPUALVIe0FlOAz4sjELwyDRyUvqeEYfRSG9qNjFIycqLZppg4RSA=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@noble/secp256k1": ["@noble/secp256k1@1.7.2", "", {}, "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@project-serum/anchor": ["@project-serum/anchor@0.11.1", "", { "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" } }, "sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA=="], + + "@project-serum/borsh": ["@project-serum/borsh@0.2.5", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.2.0" } }, "sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q=="], + + "@project-serum/common": ["@project-serum/common@0.0.1-beta.3", "", { "dependencies": { "@project-serum/serum": "^0.13.21", "bn.js": "^5.1.2", "superstruct": "0.8.3" }, "peerDependencies": { "@solana/web3.js": "^0.87.1" } }, "sha512-gnQE/eUydTtto5okCgLWj1M97R9RRPJqnhKklikYI7jP/pnNhDmngSXC/dmfzED2GXSJEIKNIlxVw1k+E2Aw3w=="], + + "@project-serum/serum": ["@project-serum/serum@0.13.65", "", { "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" } }, "sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + + "@pythnetwork/client": ["@pythnetwork/client@2.21.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@coral-xyz/borsh": "^0.28.0", "buffer": "^6.0.1" }, "peerDependencies": { "@solana/web3.js": "^1.30.2" } }, "sha512-jqUuPLuVKRNUsZfwLuvK/MwnJ3LIrIxBNoz43xt0fjvVuH5QyTlz51ek76CkeKfCbomGKe41Vq7bvn8aqWVOGA=="], + + "@pythnetwork/price-service-client": ["@pythnetwork/price-service-client@1.9.0", "", { "dependencies": { "@pythnetwork/price-service-sdk": "*", "@types/ws": "^8.5.3", "axios": "^1.5.1", "axios-retry": "^3.8.0", "isomorphic-ws": "^4.0.1", "ts-log": "^2.2.4", "ws": "^8.6.0" } }, "sha512-SLm3IFcfmy9iMqHeT4Ih6qMNZhJEefY14T9yTlpsH2D/FE5+BaGGnfcexUifVlfH6M7mwRC4hEFdNvZ6ebZjJg=="], + + "@pythnetwork/price-service-sdk": ["@pythnetwork/price-service-sdk@1.8.0", "", { "dependencies": { "bn.js": "^5.2.1" } }, "sha512-tFZ1thj3Zja06DzPIX2dEWSi7kIfIyqreoywvw5NQ3Z1pl5OJHQGMEhxt6Li3UCGSp2ooYZS9wl8/8XfrfrNSA=="], + + "@pythnetwork/pyth-solana-receiver": ["@pythnetwork/pyth-solana-receiver@0.8.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@noble/hashes": "^1.4.0", "@pythnetwork/price-service-sdk": ">=1.6.0", "@pythnetwork/solana-utils": "*", "@solana/web3.js": "^1.90.0" } }, "sha512-5lhLtggAqsiHtffTPM8vcKJmhBdxzidBmiNNUlqPyg9XmhZ4Z+roY0dfzluEoX5xer9rEA1ThsBpX0bG1DRIGA=="], + + "@pythnetwork/solana-utils": ["@pythnetwork/solana-utils@0.4.5", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@solana/web3.js": "^1.90.0", "bs58": "^5.0.0", "jito-ts": "^3.0.1", "ts-log": "^2.2.7" } }, "sha512-NoLdC2rRAx9a66L0hSOAGt6Wj/YxfnKkw+mbb7Tn/Nn1du4HyShi41DiN6B+2XXqnMthNGbf9FSHvj4NXXABvA=="], + + "@solana/buffer-layout": ["@solana/buffer-layout@4.0.1", "", { "dependencies": { "buffer": "~6.0.3" } }, "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA=="], + + "@solana/buffer-layout-utils": ["@solana/buffer-layout-utils@0.2.0", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/web3.js": "^1.32.0", "bigint-buffer": "^1.1.5", "bignumber.js": "^9.0.1" } }, "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g=="], + + "@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], + + "@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + + "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + + "@solana/codecs-numbers": ["@solana/codecs-numbers@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg=="], + + "@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + + "@solana/errors": ["@solana/errors@2.3.0", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^14.0.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ=="], + + "@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + + "@solana/spl-token": ["@solana/spl-token@0.4.13", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-group": "^0.0.7", "@solana/spl-token-metadata": "^0.1.6", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.95.5" } }, "sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w=="], + + "@solana/spl-token-group": ["@solana/spl-token-group@0.0.7", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug=="], + + "@solana/spl-token-metadata": ["@solana/spl-token-metadata@0.1.6", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA=="], + + "@solana/web3.js": ["@solana/web3.js@1.73.2", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/ed25519": "^1.7.0", "@noble/hashes": "^1.1.2", "@noble/secp256k1": "^1.6.3", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.1", "fast-stable-stringify": "^1.0.0", "jayson": "^3.4.4", "node-fetch": "2", "rpc-websockets": "^7.5.0", "superstruct": "^0.14.2" } }, "sha512-9WACF8W4Nstj7xiDw3Oom22QmrhBh0VyZyZ7JvvG3gOxLWLlX3hvm5nPVJOGcCE/9fFavBbCUb5A6CIuvMGdoA=="], + + "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + + "@switchboard-xyz/common": ["@switchboard-xyz/common@3.0.14", "", { "dependencies": { "@solana/web3.js": "^1.98.0", "axios": "^1.8.3", "big.js": "^6.2.2", "bn.js": "^5.2.1", "bs58": "^6.0.0", "buffer": "^6.0.3", "decimal.js": "^10.4.3", "js-sha256": "^0.11.0", "protobufjs": "^7.4.0", "yaml": "^2.6.1" } }, "sha512-LpxzEywO0DjPYIgPzQYkf32C7agwW4YRsPN6BcIvYrw0iJdDMtPZ3SQfIGHLSlD1fwvn2KLUYuGaKegeq4aBTw=="], + + "@switchboard-xyz/on-demand": ["@switchboard-xyz/on-demand@2.4.1", "", { "dependencies": { "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1", "@isaacs/ttlcache": "^1.4.1", "@switchboard-xyz/common": ">=3.0.0", "axios": "^1.8.3", "bs58": "^6.0.0", "buffer": "^6.0.3", "js-yaml": "^4.1.0" } }, "sha512-eSlBp+c8lxpcSgh0/2xK8OaLHPziTSZlcs8V96gZGdiCJz1KgWJRNE1qnIJDOwaGdFecZdwcmajfQRtLRLED3w=="], + + "@types/bn.js": ["@types/bn.js@5.1.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w=="], + + "@types/chai": ["@types/chai@5.0.0", "", {}, "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mocha": ["@types/mocha@8.2.3", "", {}, "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw=="], + + "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], + + "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], + + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" } }, "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@6.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "anchor-bankrun": ["anchor-bankrun@0.3.0", "", { "peerDependencies": { "@coral-xyz/anchor": "^0.28.0", "@solana/web3.js": "^1.78.4", "solana-bankrun": "^0.2.0" } }, "sha512-PYBW5fWX+iGicIS5MIM/omhk1tQPUc0ELAnI/IkLKQJ6d75De/CQRh8MF2bU/TgGyFi6zEel80wUe3uRol9RrQ=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + + "assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="], + + "assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axios": ["axios@1.11.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA=="], + + "axios-retry": ["axios-retry@3.9.1", "", { "dependencies": { "@babel/runtime": "^7.15.4", "is-retry-allowed": "^2.2.0" } }, "sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "big.js": ["big.js@6.2.2", "", {}, "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ=="], + + "bigint-buffer": ["bigint-buffer@1.1.5", "", { "dependencies": { "bindings": "^1.3.0" } }, "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + + "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], + + "bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], + + "borsh": ["borsh@0.7.0", "", { "dependencies": { "bn.js": "^5.2.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" } }, "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "buffer-layout": ["buffer-layout@1.2.2", "", {}, "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA=="], + + "bufferutil": ["bufferutil@4.0.9", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + + "chai": ["chai@4.4.1", "", { "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.0.8" } }, "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g=="], + + "chai-bn": ["chai-bn@0.2.2", "", { "peerDependencies": { "bn.js": "^4.11.0", "chai": "^4.0.0" } }, "sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "check-error": ["check-error@1.0.3", "", { "dependencies": { "get-func-name": "^2.0.2" } }, "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "crypto-hash": ["crypto-hash@1.3.0", "", {}, "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg=="], + + "csvtojson": ["csvtojson@2.0.10", "", { "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.3", "strip-bom": "^2.0.0" }, "bin": { "csvtojson": "./bin/csvtojson" } }, "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "delay": ["delay@5.0.0", "", {}, "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + + "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], + + "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], + + "dotenv": ["dotenv@16.4.5", "", {}, "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es6-promise": ["es6-promise@4.2.8", "", {}, "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="], + + "es6-promisify": ["es6-promisify@5.0.0", "", { "dependencies": { "es6-promise": "^4.0.3" } }, "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.0", "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ=="], + + "eslint-config-prettier": ["eslint-config-prettier@8.3.0", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew=="], + + "eslint-plugin-prettier": ["eslint-plugin-prettier@3.4.0", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0" }, "peerDependencies": { "eslint": ">=5.0.0", "prettier": ">=1.13.0" } }, "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw=="], + + "eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + + "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-stable-stringify": ["fast-stable-stringify@1.0.0", "", {}, "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag=="], + + "fastestsmallesttextencoderdecoder": ["fastestsmallesttextencoderdecoder@1.0.22", "", {}, "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], + + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find": ["find@0.3.0", "", { "dependencies": { "traverse-chain": "~0.1.0" } }, "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw=="], + + "find-process": ["find-process@1.4.11", "", { "dependencies": { "chalk": "~4.1.2", "commander": "^12.1.0", "loglevel": "^1.9.2" }, "bin": { "find-process": "bin/find-process.js" } }, "sha512-mAOh9gGk9WZ4ip5UjV0o6Vb4SrfnAmtsFNzkMRH9HQiFXVQnDyQFrSHTK5UoG6E+KV+s+cIznbtwpfN41l2nFA=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + + "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + + "husky": ["husky@7.0.4", "", { "bin": { "husky": "lib/bin.js" } }, "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-retry-allowed": ["is-retry-allowed@2.2.0", "", {}, "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-utf8": ["is-utf8@0.2.1", "", {}, "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "isomorphic-ws": ["isomorphic-ws@4.0.1", "", { "peerDependencies": { "ws": "*" } }, "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="], + + "jayson": ["jayson@3.7.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "JSONStream": "^1.3.5", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.20", "uuid": "^8.3.2", "ws": "^7.4.5" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ=="], + + "jito-ts": ["jito-ts@3.0.1", "", { "dependencies": { "@grpc/grpc-js": "^1.8.13", "@noble/ed25519": "^1.7.1", "@solana/web3.js": "~1.77.3", "agentkeepalive": "^4.3.0", "dotenv": "^16.0.3", "jayson": "^4.0.0", "node-fetch": "^2.6.7", "superstruct": "^1.0.3" } }, "sha512-TSofF7KqcwyaWGjPaSYC8RDoNBY1TPRNBHdrw24bdIi7mQ5bFEDdYK3D//llw/ml8YDvcZlgd644WxhjLTS9yg=="], + + "js-sha256": ["js-sha256@0.11.1", "", {}, "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + + "json2csv": ["json2csv@5.0.7", "", { "dependencies": { "commander": "^6.1.0", "jsonparse": "^1.3.1", "lodash.get": "^4.4.2" }, "bin": { "json2csv": "bin/json2csv.js" } }, "sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA=="], + + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], + + "lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "loupe": ["loupe@2.3.7", "", { "dependencies": { "get-func-name": "^2.0.1" } }, "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA=="], + + "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], + + "lunr": ["lunr@2.3.9", "", {}, "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="], + + "marked": ["marked@4.3.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.4", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], + + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + + "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "pathval": ["pathval@1.1.1", "", {}, "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.0.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ=="], + + "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], + + "protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "rpc-websockets": ["rpc-websockets@7.5.1", "", { "dependencies": { "@babel/runtime": "^7.17.2", "eventemitter3": "^4.0.7", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shiki": ["shiki@0.11.1", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", "vscode-textmate": "^6.0.0" } }, "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA=="], + + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], + + "solana-bankrun": ["solana-bankrun@0.3.0", "", { "dependencies": { "@solana/web3.js": "^1.68.0", "bs58": "^4.0.1" }, "optionalDependencies": { "solana-bankrun-darwin-arm64": "0.3.0", "solana-bankrun-darwin-universal": "0.3.0", "solana-bankrun-darwin-x64": "0.3.0", "solana-bankrun-linux-x64-gnu": "0.3.0", "solana-bankrun-linux-x64-musl": "0.3.0" } }, "sha512-YkH7sa8TB/AoRPzG17CXJtYsRIQHEkEqGLz1Vwc13taXhDBkjO7z6NI5JYw7n0ybRymDHwMYTc7sd+5J40TyVQ=="], + + "solana-bankrun-darwin-arm64": ["solana-bankrun-darwin-arm64@0.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+NbDncf0U6l3knuacRBiqpjZ2DSp+5lZaAU518gH7/x6qubbui/d000STaIBK+uNTPBS/AL/bCN+7PkXqmA3lA=="], + + "solana-bankrun-darwin-universal": ["solana-bankrun-darwin-universal@0.3.0", "", { "os": "darwin" }, "sha512-1/F0xdMa4qvc5o6z16FCCbZ5jbdvKvxpx5kyPcMWRiRPwyvi+zltMxciPAYMlg3wslQqGz88uFhrBEzq2eTumQ=="], + + "solana-bankrun-darwin-x64": ["solana-bankrun-darwin-x64@0.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U6CANjkmMl+lgNA7UH0GKs5V7LtVIUDzJBZefGGqLfqUNv3EjA/PrrToM0hAOWJgkxSwdz6zW+p5sw5FmnbXtg=="], + + "solana-bankrun-linux-x64-gnu": ["solana-bankrun-linux-x64-gnu@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-qJSkCFs0k2n4XtTnyxGMiZsuqO2TiqTYgWjQ+3mZhGNUAMys/Vq8bd7/SyBm6RR7EfVuRXRxZvh+F8oKZ77V4w=="], + + "solana-bankrun-linux-x64-musl": ["solana-bankrun-linux-x64-musl@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-xsS2CS2xb1Sw4ivNXM0gPz/qpW9BX0neSvt/pnok5L330Nu9xlTnKAY8FhzzqOP9P9sJlGRM787Y6d0yYwt6xQ=="], + + "spok": ["spok@1.5.5", "", { "dependencies": { "ansicolors": "~0.3.2", "find-process": "^1.4.7" } }, "sha512-IrJIXY54sCNFASyHPOY+jEirkiJ26JDqsGiI0Dvhwcnkl0PEWi1PSsrkYql0rzDw8LFVTcA7rdUCAJdE2HE+2Q=="], + + "stream-chain": ["stream-chain@2.2.5", "", {}, "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="], + + "stream-json": ["stream-json@1.9.1", "", { "dependencies": { "stream-chain": "^2.2.5" } }, "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@2.0.0", "", { "dependencies": { "is-utf8": "^0.2.0" } }, "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], + + "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], + + "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], + + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "traverse-chain": ["traverse-chain@0.1.0", "", {}, "sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg=="], + + "ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], + + "ts-log": ["ts-log@2.2.7", "", {}, "sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-detect": ["type-detect@4.1.0", "", {}, "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="], + + "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], + + "typedoc": ["typedoc@0.23.23", "", { "dependencies": { "lunr": "^2.3.9", "marked": "^4.2.4", "minimatch": "^5.1.1", "shiki": "^0.11.1" }, "peerDependencies": { "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-cg1YQWj+/BU6wq74iott513U16fbrPCbyYs04PHZgvoKJIc6EY4xNobyDZh4KMfRGW8Yjv6wwIzQyoqopKOUGw=="], + + "typescript": ["typescript@5.4.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="], + + "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="], + + "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], + + "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + + "vscode-oniguruma": ["vscode-oniguruma@1.7.0", "", {}, "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA=="], + + "vscode-textmate": ["vscode-textmate@6.0.0", "", {}, "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ=="], + + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zstddec": ["zstddec@0.1.0", "", {}, "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="], + + "@coral-xyz/anchor-30/@coral-xyz/borsh": ["@coral-xyz/borsh@0.30.1", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ=="], + + "@ellipsis-labs/phoenix-sdk/@solana/spl-token": ["@solana/spl-token@0.3.11", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-metadata": "^0.1.2", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.88.0" } }, "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ=="], + + "@ellipsis-labs/phoenix-sdk/@types/node": ["@types/node@18.19.121", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ=="], + + "@ellipsis-labs/phoenix-sdk/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@metaplex-foundation/beet-solana/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], + + "@metaplex-foundation/solita/@metaplex-foundation/beet": ["@metaplex-foundation/beet@0.4.0", "", { "dependencies": { "ansicolors": "^0.3.2", "bn.js": "^5.2.0", "debug": "^4.3.3" } }, "sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA=="], + + "@metaplex-foundation/solita/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@metaplex-foundation/solita/js-sha256": ["js-sha256@0.9.0", "", {}, "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="], + + "@metaplex-foundation/solita/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + + "@project-serum/anchor/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@project-serum/anchor/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], + + "@project-serum/anchor/js-sha256": ["js-sha256@0.9.0", "", {}, "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="], + + "@project-serum/common/superstruct": ["superstruct@0.8.3", "", { "dependencies": { "kind-of": "^6.0.2", "tiny-invariant": "^1.0.6" } }, "sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww=="], + + "@project-serum/serum/@solana/spl-token": ["@solana/spl-token@0.1.8", "", { "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" } }, "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ=="], + + "@pythnetwork/client/@coral-xyz/borsh": ["@coral-xyz/borsh@0.28.0", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@pythnetwork/solana-utils/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@pythnetwork/solana-utils/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/codecs-data-structures/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/codecs-numbers/@solana/codecs-core": ["@solana/codecs-core@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw=="], + + "@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "@solana/errors/commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], + + "@solana/options/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/web3.js/buffer": ["buffer@6.0.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ=="], + + "@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], + + "@switchboard-xyz/common/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@switchboard-xyz/common/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], + + "@switchboard-xyz/on-demand/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "find-process/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "jito-ts/@solana/web3.js": ["@solana/web3.js@1.77.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.3.0", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", "node-fetch": "^2.6.7", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } }, "sha512-XdN0Lh4jdY7J8FYMyucxCwzn6Ga2Sr1DHDWRbqVzk7ZPmmpSPOVWHzO67X1cVT+jNi1D6gZi2tgjHgDPuj6e9Q=="], + + "jito-ts/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "jito-ts/superstruct": ["superstruct@1.0.4", "", {}, "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ=="], + + "solana-bankrun/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "typedoc/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + + "@ellipsis-labs/phoenix-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@ellipsis-labs/phoenix-sdk/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@metaplex-foundation/beet-solana/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@metaplex-foundation/solita/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@metaplex-foundation/solita/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@project-serum/anchor/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@project-serum/anchor/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@project-serum/anchor/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], + + "@project-serum/serum/@solana/spl-token/dotenv": ["dotenv@10.0.0", "", {}, "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@pythnetwork/solana-utils/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@pythnetwork/solana-utils/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@pythnetwork/solana-utils/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@pythnetwork/solana-utils/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@solana/buffer-layout-utils/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@solana/buffer-layout-utils/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@solana/codecs-core/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/codecs-data-structures/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/codecs-strings/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/options/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@switchboard-xyz/common/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@switchboard-xyz/common/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@switchboard-xyz/common/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@switchboard-xyz/common/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], + + "@switchboard-xyz/on-demand/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "jito-ts/@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], + + "jito-ts/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "jito-ts/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "jito-ts/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "jito-ts/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "solana-bankrun/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "solana-bankrun/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "solana-bankrun/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "typedoc/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@metaplex-foundation/solita/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@project-serum/anchor/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@project-serum/anchor/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@project-serum/anchor/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@project-serum/anchor/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@project-serum/anchor/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@pythnetwork/solana-utils/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@solana/codecs/@solana/codecs-numbers/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@switchboard-xyz/common/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "jito-ts/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "solana-bankrun/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "solana-bankrun/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "solana-bankrun/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "solana-bankrun/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "solana-bankrun/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@project-serum/anchor/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "solana-bankrun/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], + } +} diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 043831fc25..0993304494 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -9,7 +9,7 @@ import { ZERO, } from '../constants/numericConstants'; import { decodeName } from '../userName'; -import { DLOBNode, DLOBNodeType, TriggerOrderNode } from './DLOBNode'; +import { DLOBNode, DLOBNodeType } from './DLOBNode'; import { DriftClient } from '../driftClient'; import { getLimitPrice, @@ -33,9 +33,18 @@ import { } from '../types'; import { isUserProtectedMaker } from '../math/userStatus'; import { OraclePriceData } from '../oracles/types'; -import { ProtectMakerParamsMap } from './types'; +import { + DLOBFilterFcn, + DLOBOrders, + IDLOB, + MarketNodeLists, + NodeToFill, + NodeToTrigger, + OrderBookCallback, + ProtectMakerParamsMap, +} from './types'; import { SlotSubscriber } from '../slot/SlotSubscriber'; -import { UserMap } from '../userMap/userMap'; +import { IUserMap } from '../userMap/types'; import { PublicKey } from '@solana/web3.js'; import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus'; import { @@ -50,59 +59,6 @@ import { import { isFallbackAvailableLiquiditySource } from '../math/auction'; import { convertToNumber } from '../math/conversion'; -export type DLOBOrder = { user: PublicKey; order: Order }; -export type DLOBOrders = DLOBOrder[]; - -export type MarketNodeLists = { - restingLimit: { - ask: NodeList<'restingLimit'>; - bid: NodeList<'restingLimit'>; - }; - floatingLimit: { - ask: NodeList<'floatingLimit'>; - bid: NodeList<'floatingLimit'>; - }; - protectedFloatingLimit: { - ask: NodeList<'protectedFloatingLimit'>; - bid: NodeList<'protectedFloatingLimit'>; - }; - takingLimit: { - ask: NodeList<'takingLimit'>; - bid: NodeList<'takingLimit'>; - }; - market: { - ask: NodeList<'market'>; - bid: NodeList<'market'>; - }; - trigger: { - above: NodeList<'trigger'>; - below: NodeList<'trigger'>; - }; - signedMsg: { - ask: NodeList<'signedMsg'>; - bid: NodeList<'signedMsg'>; - }; -}; - -type OrderBookCallback = () => void; - -/** - * Receives a DLOBNode and is expected to return true if the node should - * be taken into account when generating, or false otherwise. - * - * Currently used in functions that rely on getBestNode - */ -export type DLOBFilterFcn = (node: DLOBNode) => boolean; - -export type NodeToFill = { - node: DLOBNode; - makerNodes: DLOBNode[]; -}; - -export type NodeToTrigger = { - node: TriggerOrderNode; -}; - const SUPPORTED_ORDER_TYPES = [ 'market', 'limit', @@ -111,7 +67,7 @@ const SUPPORTED_ORDER_TYPES = [ 'oracle', ]; -export class DLOB { +export class DLOB implements IDLOB { openOrders = new Map>(); orderLists = new Map>(); maxSlotForRestingLimitOrders = 0; @@ -166,7 +122,7 @@ export class DLOB { * @returns a promise that resolves when the DLOB is initialized */ public async initFromUserMap( - userMap: UserMap, + userMap: IUserMap, slot: number ): Promise { if (this.initialized) { diff --git a/sdk/src/dlob/DLOBSubscriber.ts b/sdk/src/dlob/DLOBSubscriber.ts index 3ea6aa8dcd..58a8b5e5a9 100644 --- a/sdk/src/dlob/DLOBSubscriber.ts +++ b/sdk/src/dlob/DLOBSubscriber.ts @@ -5,10 +5,10 @@ import { DLOBSource, DLOBSubscriberEvents, DLOBSubscriptionConfig, + IDLOB, ProtectMakerParamsMap, SlotSource, } from './types'; -import { DriftClient } from '../driftClient'; import { isVariant, MarketType } from '../types'; import { DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS, @@ -19,14 +19,15 @@ import { L3OrderBook, } from './orderBookLevels'; import { getProtectedMakerParamsMap } from '../math/protectedMakerParams'; +import { IDriftClient } from '../driftClient/types'; export class DLOBSubscriber { - driftClient: DriftClient; + driftClient: IDriftClient; dlobSource: DLOBSource; slotSource: SlotSource; updateFrequency: number; intervalId?: ReturnType; - dlob: DLOB; + dlob: IDLOB; public eventEmitter: StrictEventEmitter; protectedMakerView: boolean; constructor(config: DLOBSubscriptionConfig) { @@ -69,7 +70,7 @@ export class DLOBSubscriber { ); } - public getDLOB(): DLOB { + public getDLOB(): IDLOB { return this.dlob; } diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index ac0fc5a86d..2de983eb81 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -24,7 +24,8 @@ import { } from '../types'; import { OraclePriceData } from '../oracles/types'; import { PublicKey } from '@solana/web3.js'; -import { standardizeBaseAssetAmount, standardizePrice } from '../math/orders'; +import { standardizePrice } from '../math/orders'; +import { standardizeBaseAssetAmount } from '../math/utils'; type liquiditySource = | 'serum' diff --git a/sdk/src/dlob/types.ts b/sdk/src/dlob/types.ts index 6b3c158436..d2757e40df 100644 --- a/sdk/src/dlob/types.ts +++ b/sdk/src/dlob/types.ts @@ -1,10 +1,29 @@ -import { DLOB } from './DLOB'; -import { DriftClient } from '../driftClient'; -import { ProtectedMakerParams } from '../types'; +import { + MarketType, + Order, + PerpMarketAccount, + PositionDirection, + ProtectedMakerParams, + SpotMarketAccount, + StateAccount, +} from '../types'; import { MarketTypeStr } from '../types'; +import { PublicKey } from '@solana/web3.js'; +import { DLOBNode, DLOBNodeType, TriggerOrderNode } from './DLOBNode'; +import { BN } from '@coral-xyz/anchor'; +import { OraclePriceData } from '../oracles/types'; +import { NodeList } from './NodeList'; +import { SlotSubscriber } from '../slot/SlotSubscriber'; +import { + L2OrderBook, + L2OrderBookGenerator, + L3OrderBook, +} from './orderBookLevels'; +import { IDriftClient } from '../driftClient/types'; +import { IUserMap } from '../userMap/types'; export type DLOBSubscriptionConfig = { - driftClient: DriftClient; + driftClient: IDriftClient; dlobSource: DLOBSource; slotSource: SlotSource; updateFrequency: number; @@ -12,7 +31,7 @@ export type DLOBSubscriptionConfig = { }; export interface DLOBSubscriberEvents { - update: (dlob: DLOB) => void; + update: (dlob: IDLOB) => void; error: (e: Error) => void; } @@ -20,7 +39,7 @@ export interface DLOBSource { getDLOB( slot: number, protectedMakerParamsMap?: ProtectMakerParamsMap - ): Promise; + ): Promise; } export interface SlotSource { @@ -30,3 +49,404 @@ export interface SlotSource { export type ProtectMakerParamsMap = { [marketType in MarketTypeStr]: Map; }; + +export type DLOBOrder = { user: PublicKey; order: Order }; +export type DLOBOrders = DLOBOrder[]; + +export type MarketNodeLists = { + restingLimit: { + ask: NodeList<'restingLimit'>; + bid: NodeList<'restingLimit'>; + }; + floatingLimit: { + ask: NodeList<'floatingLimit'>; + bid: NodeList<'floatingLimit'>; + }; + protectedFloatingLimit: { + ask: NodeList<'protectedFloatingLimit'>; + bid: NodeList<'protectedFloatingLimit'>; + }; + takingLimit: { + ask: NodeList<'takingLimit'>; + bid: NodeList<'takingLimit'>; + }; + market: { + ask: NodeList<'market'>; + bid: NodeList<'market'>; + }; + trigger: { + above: NodeList<'trigger'>; + below: NodeList<'trigger'>; + }; + signedMsg: { + ask: NodeList<'signedMsg'>; + bid: NodeList<'signedMsg'>; + }; +}; + +export type OrderBookCallback = () => void; + +/** + * Receives a DLOBNode and is expected to return true if the node should + * be taken into account when generating, or false otherwise. + * + * Currently used in functions that rely on getBestNode + */ +export type DLOBFilterFcn = (node: DLOBNode) => boolean; + +export type NodeToFill = { + node: DLOBNode; + makerNodes: DLOBNode[]; +}; + +export type NodeToTrigger = { + node: TriggerOrderNode; +}; + +export interface IDLOB { + // Properties + openOrders: Map>; + orderLists: Map>; + maxSlotForRestingLimitOrders: number; + initialized: boolean; + protectedMakerParamsMap: ProtectMakerParamsMap; + + // Methods + clear(): void; + + /** + * initializes a new DLOB instance + * + * @returns a promise that resolves when the DLOB is initialized + */ + initFromUserMap(userMap: IUserMap, slot: number): Promise; + + insertOrder( + order: Order, + userAccount: string, + slot: number, + isUserProtectedMaker: boolean, + onInsert?: OrderBookCallback + ): void; + + insertSignedMsgOrder( + order: Order, + userAccount: string, + isUserProtectedMaker: boolean, + onInsert?: OrderBookCallback + ): void; + + addOrderList(marketType: MarketTypeStr, marketIndex: number): void; + + delete( + order: Order, + userAccount: PublicKey, + slot: number, + isUserProtectedMaker: boolean, + onDelete?: OrderBookCallback + ): void; + + getListForOnChainOrder( + order: Order, + slot: number, + isProtectedMaker: boolean + ): NodeList | undefined; + + updateRestingLimitOrders(slot: number): void; + + updateRestingLimitOrdersForMarketType( + slot: number, + marketTypeStr: MarketTypeStr + ): void; + + getOrder(orderId: number, userAccount: PublicKey): Order | undefined; + + findNodesToFill( + marketIndex: number, + fallbackBid: BN | undefined, + fallbackAsk: BN | undefined, + slot: number, + ts: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + stateAccount: StateAccount, + marketAccount: PerpMarketAccount | SpotMarketAccount + ): NodeToFill[]; + + getMakerRebate( + marketType: MarketType, + stateAccount: StateAccount, + marketAccount: PerpMarketAccount | SpotMarketAccount + ): { makerRebateNumerator: number; makerRebateDenominator: number }; + + mergeNodesToFill( + restingLimitOrderNodesToFill: NodeToFill[], + takingOrderNodesToFill: NodeToFill[] + ): NodeToFill[]; + + findRestingLimitOrderNodesToFill( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + isAmmPaused: boolean, + minAuctionDuration: number, + makerRebateNumerator: number, + makerRebateDenominator: number, + fallbackAsk: BN | undefined, + fallbackBid: BN | undefined + ): NodeToFill[]; + + findTakingNodesToFill( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + isAmmPaused: boolean, + minAuctionDuration: number, + fallbackAsk: BN | undefined, + fallbackBid?: BN | undefined + ): NodeToFill[]; + + findTakingNodesCrossingMakerNodes( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + takerNodeGenerator: Generator, + makerNodeGeneratorFn: ( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData + ) => Generator, + doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean + ): NodeToFill[]; + + findNodesCrossingFallbackLiquidity( + marketType: MarketType, + slot: number, + oraclePriceData: OraclePriceData, + nodeGenerator: Generator, + doesCross: (nodePrice: BN | undefined) => boolean, + minAuctionDuration: number + ): NodeToFill[]; + + findExpiredNodesToFill( + marketIndex: number, + ts: number, + marketType: MarketType, + slot?: BN + ): NodeToFill[]; + + getTakingBids( + marketIndex: number, + marketType: MarketType, + slot: number, + oraclePriceData: OraclePriceData, + filterFcn?: DLOBFilterFcn + ): Generator; + + getTakingAsks( + marketIndex: number, + marketType: MarketType, + slot: number, + oraclePriceData: OraclePriceData, + filterFcn?: DLOBFilterFcn + ): Generator; + + getRestingLimitAsks( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + filterFcn?: DLOBFilterFcn + ): Generator; + + getRestingLimitBids( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + filterFcn?: DLOBFilterFcn + ): Generator; + + /** + * This will look at both the taking and resting limit asks + * @param marketIndex + * @param fallbackAsk + * @param slot + * @param marketType + * @param oraclePriceData + * @param filterFcn + */ + getAsks( + marketIndex: number, + fallbackAsk: BN | undefined, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + filterFcn?: DLOBFilterFcn + ): Generator; + + /** + * This will look at both the taking and resting limit bids + * @param marketIndex + * @param fallbackBid + * @param slot + * @param marketType + * @param oraclePriceData + * @param filterFcn + */ + getBids( + marketIndex: number, + fallbackBid: BN | undefined, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData, + filterFcn?: DLOBFilterFcn + ): Generator; + + findCrossingRestingLimitOrders( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData + ): NodeToFill[]; + + determineMakerAndTaker( + askNode: DLOBNode, + bidNode: DLOBNode + ): { takerNode: DLOBNode; makerNode: DLOBNode } | undefined; + + getBestAsk( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData + ): BN | undefined; + + getBestBid( + marketIndex: number, + slot: number, + marketType: MarketType, + oraclePriceData: OraclePriceData + ): BN | undefined; + + getStopLosses( + marketIndex: number, + marketType: MarketType, + direction: PositionDirection + ): Generator; + + getStopLossMarkets( + marketIndex: number, + marketType: MarketType, + direction: PositionDirection + ): Generator; + + getStopLossLimits( + marketIndex: number, + marketType: MarketType, + direction: PositionDirection + ): Generator; + + getTakeProfits( + marketIndex: number, + marketType: MarketType, + direction: PositionDirection + ): Generator; + + getTakeProfitMarkets( + marketIndex: number, + marketType: MarketType, + direction: PositionDirection + ): Generator; + + getTakeProfitLimits( + marketIndex: number, + marketType: MarketType, + direction: PositionDirection + ): Generator; + + findNodesToTrigger( + marketIndex: number, + slot: number, + oraclePrice: BN, + marketType: MarketType, + stateAccount: StateAccount + ): NodeToTrigger[]; + + printTop( + driftClient: IDriftClient, + slotSubscriber: SlotSubscriber, + marketIndex: number, + marketType: MarketType + ): void; + + getDLOBOrders(): DLOBOrders; + + getNodeLists(): Generator>; + + /** + * Get an L2 view of the order book for a given market. + * + * @param marketIndex + * @param marketType + * @param slot + * @param oraclePriceData + * @param depth how many levels of the order book to return + * @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber} + */ + getL2(params: { + marketIndex: number; + marketType: MarketType; + slot: number; + oraclePriceData: OraclePriceData; + depth: number; + fallbackL2Generators?: L2OrderBookGenerator[]; + }): L2OrderBook; + + /** + * Get an L3 view of the order book for a given market. Does not include fallback liquidity sources + * + * @param marketIndex + * @param marketType + * @param slot + * @param oraclePriceData + */ + getL3(params: { + marketIndex: number; + marketType: MarketType; + slot: number; + oraclePriceData: OraclePriceData; + }): L3OrderBook; + + /** + * + * @param param.marketIndex the index of the market + * @param param.marketType the type of the market + * @param param.baseAmount the base amount in to estimate + * @param param.orderDirection the direction of the trade + * @param param.slot current slot for estimating dlob node price + * @param param.oraclePriceData the oracle price data + * @returns the estimated quote amount filled: QUOTE_PRECISION + */ + estimateFillWithExactBaseAmount(params: { + marketIndex: number; + marketType: MarketType; + baseAmount: BN; + orderDirection: PositionDirection; + slot: number; + oraclePriceData: OraclePriceData; + }): BN; + + getBestMakers(params: { + marketIndex: number; + marketType: MarketType; + direction: PositionDirection; + slot: number; + oraclePriceData: OraclePriceData; + numMakers: number; + }): PublicKey[]; +} diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient/index.ts similarity index 99% rename from sdk/src/driftClient.ts rename to sdk/src/driftClient/index.ts index 0c5e21243f..a8dec642d3 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient/index.ts @@ -64,8 +64,8 @@ import { ProtectedMakerModeConfig, SignedMsgOrderParamsDelegateMessage, TokenProgramFlag, -} from './types'; -import driftIDL from './idl/drift.json'; +} from '../types'; +import driftIDL from '../idl/drift.json'; import { AccountMeta, @@ -87,7 +87,7 @@ import { VersionedTransaction, } from '@solana/web3.js'; -import { TokenFaucet } from './tokenFaucet'; +import { TokenFaucet } from '../tokenFaucet'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; import { @@ -112,14 +112,14 @@ import { getUserStatsAccountPublicKey, getSignedMsgWsDelegatesAccountPublicKey, getIfRebalanceConfigPublicKey, -} from './addresses/pda'; +} from '../addresses/pda'; import { DataAndSlot, DelistedMarketSetting, DriftClientAccountEvents, DriftClientAccountSubscriber, -} from './accounts/types'; -import { TxSender, TxSigAndSlot } from './tx/types'; +} from '../accounts/types'; +import { TxSender, TxSigAndSlot } from '../tx/types'; import { BASE_PRECISION, GOV_SPOT_MARKET_INDEX, @@ -127,44 +127,45 @@ import { QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, ZERO, -} from './constants/numericConstants'; -import { findDirectionToClose, positionIsAvailable } from './math/position'; -import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance'; -import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName'; -import { OraclePriceData } from './oracles/types'; -import { DriftClientConfig } from './driftClientConfig'; -import { PollingDriftClientAccountSubscriber } from './accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; -import { WebSocketDriftClientAccountSubscriber } from './accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; -import { RetryTxSender } from './tx/retryTxSender'; -import { User } from './user'; -import { UserSubscriptionConfig } from './userConfig'; +} from '../constants/numericConstants'; +import { findDirectionToClose, positionIsAvailable } from '../math/position'; +import { getSignedTokenAmount, getTokenAmount } from '../math/spotBalance'; +import { decodeName, DEFAULT_USER_NAME, encodeName } from '../userName'; +import { OraclePriceData } from '../oracles/types'; +import { DriftClientConfig } from '../driftClientConfig'; +import { PollingDriftClientAccountSubscriber } from '../accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; +import { WebSocketDriftClientAccountSubscriber } from '../accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; +import { RetryTxSender } from '../tx/retryTxSender'; +import { User } from '../user'; +import { UserSubscriptionConfig } from '../userConfig'; import { configs, DRIFT_ORACLE_RECEIVER_ID, DEFAULT_CONFIRMATION_OPTS, DRIFT_PROGRAM_ID, PYTH_LAZER_STORAGE_ACCOUNT_KEY, -} from './config'; -import { DriftEnv } from './config/types'; -import { WRAPPED_SOL_MINT } from './constants/spotMarkets'; -import { UserStats } from './userStats'; -import { isSpotPositionAvailable } from './math/spotPosition'; -import { calculateMarketMaxAvailableInsurance } from './math/market'; -import { fetchUserStatsAccount } from './accounts/fetch'; -import { castNumberToSpotPrecision } from './math/spotMarket'; +} from '../config'; +import { DriftEnv } from '../config/types'; +import { WRAPPED_SOL_MINT } from '../constants/spotMarkets'; +import { IUserStats } from '../userStats/types'; +import { UserStats } from '../userStats'; +import { isSpotPositionAvailable } from '../math/spotPosition'; +import { calculateMarketMaxAvailableInsurance } from '../math/market'; +import { fetchUserStatsAccount } from '../accounts/fetch'; +import { castNumberToSpotPrecision } from '../math/spotMarket'; import { JupiterClient, QuoteResponse, SwapMode, -} from './jupiter/jupiterClient'; -import { getNonIdleUserFilter } from './memcmp'; -import { UserStatsSubscriptionConfig } from './userStatsConfig'; -import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade'; -import { getOrderParams, isUpdateHighLeverageMode } from './orderParams'; -import { numberToSafeBN } from './math/utils'; -import { TransactionParamProcessor } from './tx/txParamProcessor'; -import { isOracleValid, trimVaaSignatures } from './math/oracles'; -import { TxHandler } from './tx/txHandler'; +} from '../jupiter/jupiterClient'; +import { getNonIdleUserFilter } from '../memcmp'; +import { UserStatsSubscriptionConfig } from '../userStatsConfig'; +import { getMarinadeDepositIx, getMarinadeFinanceProgram } from '../marinade'; +import { getOrderParams, isUpdateHighLeverageMode } from '../orderParams'; +import { numberToSafeBN } from '../math/utils'; +import { TransactionParamProcessor } from '../tx/txParamProcessor'; +import { isOracleValid, trimVaaSignatures } from '../math/oracles'; +import { TxHandler } from '../tx/txHandler'; import { DEFAULT_RECEIVER_PROGRAM_ID, wormholeCoreBridgeIdl, @@ -176,21 +177,22 @@ import { } from '@pythnetwork/pyth-solana-receiver/lib/address'; import { WormholeCoreBridgeSolana } from '@pythnetwork/pyth-solana-receiver/lib/idl/wormhole_core_bridge_solana'; import { PythSolanaReceiver } from '@pythnetwork/pyth-solana-receiver/lib/idl/pyth_solana_receiver'; -import { getFeedIdUint8Array, trimFeedId } from './util/pythOracleUtils'; -import { createMinimalEd25519VerifyIx } from './util/ed25519Utils'; +import { getFeedIdUint8Array, trimFeedId } from '../util/pythOracleUtils'; +import { createMinimalEd25519VerifyIx } from '../util/ed25519Utils'; import { createNativeInstructionDiscriminatorBuffer, isVersionedTransaction, -} from './tx/utils'; -import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json'; +} from '../tx/utils'; +import pythSolanaReceiverIdl from '../idl/pyth_solana_receiver.json'; import { asV0Tx, PullFeed, AnchorUtils } from '@switchboard-xyz/on-demand'; -import { gprcDriftClientAccountSubscriber } from './accounts/driftClientAccount/grpcDriftClientAccountSubscriber'; +import { gprcDriftClientAccountSubscriber } from '../accounts/driftClientAccount/grpcDriftClientAccountSubscriber'; import nacl from 'tweetnacl'; -import { Slothash } from './slot/SlothashSubscriber'; -import { getOracleId } from './oracles/oracleId'; -import { SignedMsgOrderParams } from './types'; +import { Slothash } from '../slot/SlothashSubscriber'; +import { getOracleId } from '../oracles/oracleId'; +import { SignedMsgOrderParams } from '../types'; import { sha256 } from '@noble/hashes/sha256'; -import { getOracleConfidenceFromMMOracleData } from './oracles/utils'; +import { getOracleConfidenceFromMMOracleData } from '../oracles/utils'; +import { IDriftClient } from './types'; type RemainingAccountParams = { userAccounts: UserAccount[]; @@ -205,7 +207,7 @@ type RemainingAccountParams = { * # DriftClient * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more. */ -export class DriftClient { +export class DriftClient implements IDriftClient { connection: Connection; wallet: IWallet; public program: Program; @@ -214,7 +216,7 @@ export class DriftClient { opts?: ConfirmOptions; useHotWalletAdmin?: boolean; users = new Map(); - userStats?: UserStats; + userStats?: IUserStats; activeSubAccountId: number; userAccountSubscriptionConfig: UserSubscriptionConfig; userStatsAccountSubscriptionConfig: UserStatsSubscriptionConfig; @@ -2048,7 +2050,7 @@ export class DriftClient { ); } - public getUserStats(): UserStats { + public getUserStats(): IUserStats { return this.userStats; } diff --git a/sdk/src/driftClient/types.ts b/sdk/src/driftClient/types.ts new file mode 100644 index 0000000000..c78c6b19fb --- /dev/null +++ b/sdk/src/driftClient/types.ts @@ -0,0 +1,2204 @@ +import { AnchorProvider, BN, Program, ProgramAccount } from '@coral-xyz/anchor'; +import { Program as Program30, Idl as Idl30 } from '@coral-xyz/anchor-30'; +import { + AddressLookupTableAccount, + BlockhashWithExpiryBlockHeight, + ConfirmOptions, + Connection, + PublicKey, + Signer, + TransactionInstruction, + TransactionSignature, + TransactionVersion, + Transaction, + VersionedTransaction, + AccountMeta, + Keypair, +} from '@solana/web3.js'; +import { + DriftClientMetricsEvents, + HighLeverageModeConfig, + IWallet, + MakerInfo, + MarketType, + OpenbookV2FulfillmentConfigAccount, + OptionalOrderParams, + OracleSource, + Order, + OrderParams, + PerpMarketAccount, + PerpMarketExtendedInfo, + PhoenixV1FulfillmentConfigAccount, + PlaceAndTakeOrderSuccessCondition, + PositionDirection, + ReferrerInfo, + ReferrerNameAccount, + SerumV3FulfillmentConfigAccount, + SettlePnlMode, + SpotMarketAccount, + SpotPosition, + StateAccount, + SignedMsgOrderParamsMessage, + TakerInfo, + TxParams, + UserAccount, + UserStatsAccount, + ProtectedMakerModeConfig, + SignedMsgOrderParamsDelegateMessage, + SignedMsgOrderParams, + SwapReduceOnly, + OrderTriggerCondition, + ModifyOrderPolicy, +} from '../types'; +import { EventEmitter } from 'events'; +import StrictEventEmitter from 'strict-event-emitter-types'; +import { + DataAndSlot, + DriftClientAccountEvents, + DriftClientAccountSubscriber, +} from '../accounts/types'; +import { TxSender, TxSigAndSlot } from '../tx/types'; +import { OraclePriceData } from '../oracles/types'; +import { User } from '../user'; +import { UserSubscriptionConfig } from '../userConfig'; +import { DriftEnv } from '../config/types'; +import { IUserStats } from '../userStats/types'; +import { UserStatsSubscriptionConfig } from '../userStatsConfig'; +import { PythSolanaReceiver } from '@pythnetwork/pyth-solana-receiver/lib/idl/pyth_solana_receiver'; +import { WormholeCoreBridgeSolana } from '@pythnetwork/pyth-solana-receiver/lib/idl/wormhole_core_bridge_solana'; +import { Slothash } from '../slot/SlothashSubscriber'; +import { TokenFaucet } from '../tokenFaucet'; +import { + JupiterClient, + QuoteResponse, + SwapMode, +} from '../jupiter/jupiterClient'; +import { TxHandler } from '../tx/txHandler'; + +type RemainingAccountParams = { + userAccounts: UserAccount[]; + writablePerpMarketIndexes?: number[]; + writableSpotMarketIndexes?: number[]; + readablePerpMarketIndex?: number | number[]; + readableSpotMarketIndexes?: number[]; + useMarketLastSlotCache?: boolean; +}; + +export interface IDriftClient { + // Properties + connection: Connection; + wallet: IWallet; + program: Program; + provider: AnchorProvider; + env: DriftEnv; + opts?: ConfirmOptions; + useHotWalletAdmin?: boolean; + users: Map; + userStats?: IUserStats; + activeSubAccountId: number; + userAccountSubscriptionConfig: UserSubscriptionConfig; + userStatsAccountSubscriptionConfig: UserStatsSubscriptionConfig; + accountSubscriber: DriftClientAccountSubscriber; + eventEmitter: StrictEventEmitter; + metricsEventEmitter: StrictEventEmitter< + EventEmitter, + DriftClientMetricsEvents + >; + txSender: TxSender; + perpMarketLastSlotCache: Map; + spotMarketLastSlotCache: Map; + mustIncludePerpMarketIndexes: Set; + mustIncludeSpotMarketIndexes: Set; + authority: PublicKey; + /** @deprecated use marketLookupTables */ + marketLookupTable: PublicKey; + /** @deprecated use lookupTableAccounts */ + lookupTableAccount: AddressLookupTableAccount; + marketLookupTables: PublicKey[]; + lookupTableAccounts: AddressLookupTableAccount[]; + includeDelegates?: boolean; + authoritySubAccountMap?: Map; + skipLoadUsers?: boolean; + txVersion: TransactionVersion; + txParams: TxParams; + enableMetricsEvents?: boolean; + receiverProgram?: Program; + wormholeProgram?: Program; + sbOnDemandProgramdId: PublicKey; + sbOnDemandProgram?: Program30; + sbProgramFeedConfigs?: Map; + statePublicKey?: PublicKey; + signerPublicKey?: PublicKey; + + get isSubscribed(): boolean; + set isSubscribed(val: boolean); + + // Methods + getUserMapKey(subAccountId: number, authority: PublicKey): string; + createUser( + subAccountId: number, + accountSubscriptionConfig: UserSubscriptionConfig, + authority?: PublicKey + ): User; + subscribe(): Promise; + subscribeUsers(): Promise[]; + + /** + * Forces the accountSubscriber to fetch account updates from rpc + */ + fetchAccounts(): Promise; + + unsubscribe(): Promise; + unsubscribeUsers(): Promise[]; + getStatePublicKey(): Promise; + getSignerPublicKey(): PublicKey; + getStateAccount(): StateAccount; + + /** + * Forces a fetch to rpc before returning accounts. Useful for anchor tests. + */ + forceGetStateAccount(): Promise; + + getPerpMarketAccount(marketIndex: number): PerpMarketAccount | undefined; + + /** + * Forces a fetch to rpc before returning accounts. Useful for anchor tests. + * @param marketIndex + */ + forceGetPerpMarketAccount( + marketIndex: number + ): Promise; + + getPerpMarketAccounts(): PerpMarketAccount[]; + getSpotMarketAccount(marketIndex: number): SpotMarketAccount | undefined; + + /** + * Forces a fetch to rpc before returning accounts. Useful for anchor tests. + * @param marketIndex + */ + forceGetSpotMarketAccount( + marketIndex: number + ): Promise; + + getSpotMarketAccounts(): SpotMarketAccount[]; + getQuoteSpotMarketAccount(): SpotMarketAccount; + getOraclePriceDataAndSlot( + oraclePublicKey: PublicKey, + oracleSource: OracleSource + ): DataAndSlot | undefined; + getSerumV3FulfillmentConfig( + serumMarket: PublicKey + ): Promise; + getSerumV3FulfillmentConfigs(): Promise; + getPhoenixV1FulfillmentConfig( + phoenixMarket: PublicKey + ): Promise; + getPhoenixV1FulfillmentConfigs(): Promise< + PhoenixV1FulfillmentConfigAccount[] + >; + getOpenbookV2FulfillmentConfig( + openbookMarket: PublicKey + ): Promise; + getOpenbookV2FulfillmentConfigs(): Promise< + OpenbookV2FulfillmentConfigAccount[] + >; + + /** @deprecated use fetchAllLookupTableAccounts() */ + fetchMarketLookupTableAccount(): Promise; + + fetchAllLookupTableAccounts(): Promise; + + /** + * Update the wallet to use for drift transactions and linked user account + * @param newWallet + * @param subAccountIds + * @param activeSubAccountId + * @param includeDelegates + */ + updateWallet( + newWallet: IWallet, + subAccountIds?: number[], + activeSubAccountId?: number, + includeDelegates?: boolean, + authoritySubaccountMap?: Map + ): Promise; + + /** + * Update the subscribed accounts to a given authority, while leaving the + * connected wallet intact. This allows a user to emulate another user's + * account on the UI and sign permissionless transactions with their own wallet. + * @param emulateAuthority + */ + emulateAccount(emulateAuthority: PublicKey): Promise; + + switchActiveUser(subAccountId: number, authority?: PublicKey): Promise; + addUser( + subAccountId: number, + authority?: PublicKey, + userAccount?: UserAccount + ): Promise; + + /** + * Adds and subscribes to users based on params set by the constructor or by updateWallet. + */ + addAndSubscribeToUsers(authority?: PublicKey): Promise; + + /** + * Returns the instructions to initialize a user account and the public key of the user account. + * @param subAccountId + * @param name + * @param referrerInfo + * @returns [instructions, userAccountPublicKey] + */ + getInitializeUserAccountIxs( + subAccountId?: number, + name?: string, + referrerInfo?: ReferrerInfo, + poolId?: number + ): Promise<[TransactionInstruction[], PublicKey]>; + + /** + * Initializes a user account and returns the transaction signature and the public key of the user account. + * @param subAccountId + * @param name + * @param referrerInfo + * @param txParams + * @returns [transactionSignature, userAccountPublicKey] + */ + initializeUserAccount( + subAccountId?: number, + name?: string, + referrerInfo?: ReferrerInfo, + txParams?: TxParams + ): Promise<[TransactionSignature, PublicKey]>; + + getInitializeUserStatsIx(): Promise; + initializeSignedMsgUserOrders( + authority: PublicKey, + numOrders: number, + txParams?: TxParams + ): Promise<[TransactionSignature, PublicKey]>; + getInitializeSignedMsgUserOrdersAccountIx( + authority: PublicKey, + numOrders: number + ): Promise<[PublicKey, TransactionInstruction]>; + resizeSignedMsgUserOrders( + authority: PublicKey, + numOrders: number, + userSubaccountId?: number, + txParams?: TxParams + ): Promise; + getResizeSignedMsgUserOrdersInstruction( + authority: PublicKey, + numOrders: number, + userSubaccountId?: number + ): Promise; + initializeSignedMsgWsDelegatesAccount( + authority: PublicKey, + delegates?: PublicKey[], + txParams?: TxParams + ): Promise; + getInitializeSignedMsgWsDelegatesAccountIx( + authority: PublicKey, + delegates?: PublicKey[] + ): Promise; + addSignedMsgWsDelegate( + authority: PublicKey, + delegate: PublicKey, + txParams?: TxParams + ): Promise; + getAddSignedMsgWsDelegateIx( + authority: PublicKey, + delegate: PublicKey + ): Promise; + removeSignedMsgWsDelegate( + authority: PublicKey, + delegate: PublicKey, + txParams?: TxParams + ): Promise; + getRemoveSignedMsgWsDelegateIx( + authority: PublicKey, + delegate: PublicKey + ): Promise; + initializeFuelOverflow(authority?: PublicKey): Promise; + getInitializeFuelOverflowIx( + authority?: PublicKey + ): Promise; + sweepFuel(authority?: PublicKey): Promise; + getSweepFuelIx(authority?: PublicKey): Promise; + getNextSubAccountId(): Promise; + initializeReferrerName(name: string): Promise; + updateUserName( + name: string, + subAccountId?: number + ): Promise; + updateUserCustomMarginRatio( + updates: { marginRatio: number; subAccountId: number }[], + txParams?: TxParams + ): Promise; + getUpdateUserCustomMarginRatioIx( + marginRatio: number, + subAccountId?: number + ): Promise; + getUpdateUserMarginTradingEnabledIx( + marginTradingEnabled: boolean, + subAccountId?: number, + userAccountPublicKey?: PublicKey + ): Promise; + updateUserMarginTradingEnabled( + updates: { marginTradingEnabled: boolean; subAccountId: number }[] + ): Promise; + updateUserDelegate( + delegate: PublicKey, + subAccountId?: number + ): Promise; + updateUserAdvancedLp( + updates: { advancedLp: boolean; subAccountId: number }[] + ): Promise; + getUpdateAdvancedDlpIx( + advancedLp: boolean, + subAccountId: number + ): Promise; + updateUserReduceOnly( + updates: { reduceOnly: boolean; subAccountId: number }[] + ): Promise; + getUpdateUserReduceOnlyIx( + reduceOnly: boolean, + subAccountId: number + ): Promise; + updateUserPoolId( + updates: { poolId: number; subAccountId: number }[] + ): Promise; + getUpdateUserPoolIdIx( + poolId: number, + subAccountId: number + ): Promise; + fetchAllUserAccounts( + includeIdle?: boolean + ): Promise[]>; + getUserAccountsForDelegate(delegate: PublicKey): Promise; + getUserAccountsAndAddressesForAuthority( + authority: PublicKey + ): Promise[]>; + getUserAccountsForAuthority(authority: PublicKey): Promise; + getReferredUserStatsAccountsByReferrer( + referrer: PublicKey + ): Promise; + getReferrerNameAccountsForAuthority( + authority: PublicKey + ): Promise; + deleteUser( + subAccountId?: number, + txParams?: TxParams + ): Promise; + getUserDeletionIx( + userAccountPublicKey: PublicKey + ): Promise; + forceDeleteUser( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + txParams?: TxParams + ): Promise; + getForceDeleteUserIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount + ): Promise; + deleteSignedMsgUserOrders(txParams?: TxParams): Promise; + getSignedMsgUserOrdersDeletionIx( + authority: PublicKey + ): Promise; + + /** + * Checks if a SignedMsg User Orders account exists for the given authority. + * The account pubkey is derived using the program ID and authority as seeds. + * Makes an RPC call to check if the account exists on-chain. + * + * @param authority The authority public key to check for + * @returns Promise that resolves to true if the account exists, false otherwise + */ + isSignedMsgUserOrdersAccountInitialized( + authority: PublicKey + ): Promise; + + reclaimRent( + subAccountId?: number, + txParams?: TxParams + ): Promise; + getReclaimRentIx( + userAccountPublicKey: PublicKey + ): Promise; + getUser(subAccountId?: number, authority?: PublicKey): User; + hasUser(subAccountId?: number, authority?: PublicKey): boolean; + getUsers(): User[]; + getUserStats(): IUserStats; + fetchReferrerNameAccount( + name: string + ): Promise; + getUserStatsAccountPublicKey(): PublicKey; + getUserAccountPublicKey( + subAccountId?: number, + authority?: PublicKey + ): Promise; + getUserAccount( + subAccountId?: number, + authority?: PublicKey + ): UserAccount | undefined; + + /** + * Forces a fetch to rpc before returning accounts. Useful for anchor tests. + * @param subAccountId + */ + forceGetUserAccount( + subAccountId?: number, + authority?: PublicKey + ): Promise; + + getUserAccountAndSlot( + subAccountId?: number, + authority?: PublicKey + ): DataAndSlot | undefined; + getSpotPosition( + marketIndex: number, + subAccountId?: number + ): SpotPosition | undefined; + getQuoteAssetTokenAmount(): BN; + + /** + * Returns the token amount for a given market. The spot market precision is based on the token mint decimals. + * Positive if it is a deposit, negative if it is a borrow. + * @param marketIndex + */ + getTokenAmount(marketIndex: number): BN; + + /** + * Converts an amount to the spot precision for a given market. The spot market precision is based on the token mint decimals. + * @param marketIndex + * @param amount + */ + convertToSpotPrecision(marketIndex: number, amount: BN | number): BN; + + /** + * Converts an amount to the perp precision. The perp market precision is {@link BASE_PRECISION} (1e9). + * @param amount + */ + convertToPerpPrecision(amount: BN | number): BN; + + /** + * Converts an amount to the price precision. The perp market precision is {@link PRICE_PRECISION} (1e6). + * @param amount + */ + convertToPricePrecision(amount: BN | number): BN; + + /** + * Each drift instruction must include perp and sport market accounts in the ix remaining accounts. + * Use this function to force a subset of markets to be included in the remaining accounts for every ix + * + * @param perpMarketIndexes + * @param spotMarketIndexes + */ + mustIncludeMarketsInIx(params: { + perpMarketIndexes: number[]; + spotMarketIndexes: number[]; + }): void; + + getRemainingAccounts(params: RemainingAccountParams): AccountMeta[]; + addPerpMarketToRemainingAccountMaps( + marketIndex: number, + writable: boolean, + oracleAccountMap: Map, + spotMarketAccountMap: Map, + perpMarketAccountMap: Map + ): void; + addSpotMarketToRemainingAccountMaps( + marketIndex: number, + writable: boolean, + oracleAccountMap: Map, + spotMarketAccountMap: Map + ): void; + getRemainingAccountMapsForUsers(userAccounts: UserAccount[]): { + oracleAccountMap: Map; + spotMarketAccountMap: Map; + perpMarketAccountMap: Map; + }; + getOrder(orderId: number, subAccountId?: number): Order | undefined; + getOrderByUserId( + userOrderId: number, + subAccountId?: number + ): Order | undefined; + + /** + * Get the associated token address for the given spot market + * @param marketIndex + * @param useNative + * @param tokenProgram + */ + getAssociatedTokenAccount( + marketIndex: number, + useNative?: boolean, + tokenProgram?: PublicKey + ): Promise; + + createAssociatedTokenAccountIdempotentInstruction( + account: PublicKey, + payer: PublicKey, + owner: PublicKey, + mint: PublicKey, + tokenProgram?: PublicKey + ): TransactionInstruction; + getDepositTxnIx( + amount: BN, + marketIndex: number, + associatedTokenAccount: PublicKey, + subAccountId?: number, + reduceOnly?: boolean + ): Promise; + createDepositTxn( + amount: BN, + marketIndex: number, + associatedTokenAccount: PublicKey, + subAccountId?: number, + reduceOnly?: boolean, + txParams?: TxParams, + initSwiftAccount?: boolean + ): Promise; + + /** + * Deposit funds into the given spot market + * + * @param amount to deposit + * @param marketIndex spot market index to deposit into + * @param associatedTokenAccount can be the wallet public key if using native sol + * @param subAccountId subaccountId to deposit + * @param reduceOnly if true, deposit must not increase account risk + */ + deposit( + amount: BN, + marketIndex: number, + associatedTokenAccount: PublicKey, + subAccountId?: number, + reduceOnly?: boolean, + txParams?: TxParams, + initSwiftAccount?: boolean + ): Promise; + + getDepositInstruction( + amount: BN, + marketIndex: number, + userTokenAccount: PublicKey, + subAccountId?: number, + reduceOnly?: boolean, + userInitialized?: boolean + ): Promise; + getWrappedSolAccountCreationIxs( + amount: BN, + includeRent?: boolean + ): Promise<{ + ixs: TransactionInstruction[]; + /** @deprecated - this array is always going to be empty, in the current implementation */ signers: Signer[]; + pubkey: PublicKey; + }>; + getTokenProgramForSpotMarket(spotMarketAccount: SpotMarketAccount): PublicKey; + isToken2022(spotMarketAccount: SpotMarketAccount): boolean; + isTransferHook(spotMarketAccount: SpotMarketAccount): boolean; + addTokenMintToRemainingAccounts( + spotMarketAccount: SpotMarketAccount, + remainingAccounts: AccountMeta[] + ): void; + addExtraAccountMetasToRemainingAccounts( + mint: PublicKey, + remainingAccounts: AccountMeta[] + ): Promise; + getAssociatedTokenAccountCreationIx( + tokenMintAddress: PublicKey, + associatedTokenAddress: PublicKey, + tokenProgram: PublicKey + ): TransactionInstruction; + createInitializeUserAccountAndDepositCollateralIxs( + amount: BN, + userTokenAccount: PublicKey, + marketIndex?: number, + subAccountId?: number, + name?: string, + fromSubAccountId?: number, + referrerInfo?: ReferrerInfo, + donateAmount?: BN, + customMaxMarginRatio?: number, + poolId?: number + ): Promise<{ + ixs: TransactionInstruction[]; + userAccountPublicKey: PublicKey; + }>; + createInitializeUserAccountAndDepositCollateral( + amount: BN, + userTokenAccount: PublicKey, + marketIndex?: number, + subAccountId?: number, + name?: string, + fromSubAccountId?: number, + referrerInfo?: ReferrerInfo, + donateAmount?: BN, + txParams?: TxParams, + customMaxMarginRatio?: number, + poolId?: number + ): Promise<[Transaction | VersionedTransaction, PublicKey]>; + + /** + * Creates the User account for a user, and deposits some initial collateral + * @param amount + * @param userTokenAccount + * @param marketIndex + * @param subAccountId + * @param name + * @param fromSubAccountId + * @param referrerInfo + * @param donateAmount + * @param txParams + * @returns + */ + initializeUserAccountAndDepositCollateral( + amount: BN, + userTokenAccount: PublicKey, + marketIndex?: number, + subAccountId?: number, + name?: string, + fromSubAccountId?: number, + referrerInfo?: ReferrerInfo, + donateAmount?: BN, + txParams?: TxParams, + customMaxMarginRatio?: number, + poolId?: number + ): Promise<[TransactionSignature, PublicKey]>; + + initializeUserAccountForDevnet( + subAccountId?: number, + name?: string, + marketIndex?: number, + tokenFaucet?: TokenFaucet, + amount?: BN, + referrerInfo?: ReferrerInfo, + txParams?: TxParams + ): Promise<[TransactionSignature, PublicKey]>; + getWithdrawalIxs( + amount: BN, + marketIndex: number, + associatedTokenAddress: PublicKey, + reduceOnly?: boolean, + subAccountId?: number, + updateFuel?: boolean + ): Promise; + + /** + * Withdraws from a user account. If deposit doesn't already exist, creates a borrow + * @param amount + * @param marketIndex + * @param associatedTokenAddress - the token account to withdraw to. can be the wallet public key if using native sol + * @param reduceOnly + */ + withdraw( + amount: BN, + marketIndex: number, + associatedTokenAddress: PublicKey, + reduceOnly?: boolean, + subAccountId?: number, + txParams?: TxParams, + updateFuel?: boolean + ): Promise; + + withdrawAllDustPositions( + subAccountId?: number, + txParams?: TxParams, + opts?: { dustPositionCountCallback?: (count: number) => void } + ): Promise; + getWithdrawIx( + amount: BN, + marketIndex: number, + userTokenAccount: PublicKey, + reduceOnly?: boolean, + subAccountId?: number + ): Promise; + + /** + * Withdraws from the fromSubAccount and deposits into the toSubAccount + * @param amount + * @param marketIndex + * @param fromSubAccountId + * @param toSubAccountId + * @param txParams + */ + transferDeposit( + amount: BN, + marketIndex: number, + fromSubAccountId: number, + toSubAccountId: number, + txParams?: TxParams + ): Promise; + + getTransferDepositIx( + amount: BN, + marketIndex: number, + fromSubAccountId: number, + toSubAccountId: number + ): Promise; + transferPools( + depositFromMarketIndex: number, + depositToMarketIndex: number, + borrowFromMarketIndex: number, + borrowToMarketIndex: number, + depositAmount: BN | undefined, + borrowAmount: BN | undefined, + fromSubAccountId: number, + toSubAccountId: number, + txParams?: TxParams + ): Promise; + getTransferPoolsIx( + depositFromMarketIndex: number, + depositToMarketIndex: number, + borrowFromMarketIndex: number, + borrowToMarketIndex: number, + depositAmount: BN | undefined, + borrowAmount: BN | undefined, + fromSubAccountId: number, + toSubAccountId: number, + isToNewSubAccount?: boolean + ): Promise; + transferPerpPosition( + fromSubAccountId: number, + toSubAccountId: number, + marketIndex: number, + amount: BN, + txParams?: TxParams + ): Promise; + getTransferPerpPositionIx( + fromSubAccountId: number, + toSubAccountId: number, + marketIndex: number, + amount: BN + ): Promise; + updateSpotMarketCumulativeInterest( + marketIndex: number, + txParams?: TxParams + ): Promise; + updateSpotMarketCumulativeInterestIx( + marketIndex: number + ): Promise; + settleLP( + settleeUserAccountPublicKey: PublicKey, + marketIndex: number, + txParams?: TxParams + ): Promise; + settleLPIx( + settleeUserAccountPublicKey: PublicKey, + marketIndex: number + ): Promise; + removePerpLpShares( + marketIndex: number, + sharesToBurn?: BN, + txParams?: TxParams, + subAccountId?: number + ): Promise; + removePerpLpSharesInExpiringMarket( + marketIndex: number, + userAccountPublicKey: PublicKey, + sharesToBurn?: BN, + txParams?: TxParams + ): Promise; + getRemovePerpLpSharesInExpiringMarket( + marketIndex: number, + userAccountPublicKey: PublicKey, + sharesToBurn?: BN + ): Promise; + getRemovePerpLpSharesIx( + marketIndex: number, + sharesToBurn?: BN, + subAccountId?: number + ): Promise; + addPerpLpShares( + amount: BN, + marketIndex: number, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getAddPerpLpSharesIx( + amount: BN, + marketIndex: number, + subAccountId?: number + ): Promise; + getQuoteValuePerLpShare(marketIndex: number): BN; + + /** + * @deprecated use {@link placePerpOrder} or {@link placeAndTakePerpOrder} instead + */ + openPosition( + direction: PositionDirection, + amount: BN, + marketIndex: number, + limitPrice?: BN, + subAccountId?: number + ): Promise; + + sendSignedTx( + tx: Transaction | VersionedTransaction, + opts?: ConfirmOptions + ): Promise; + prepareMarketOrderTxs( + orderParams: OptionalOrderParams, + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + makerInfo?: MakerInfo | MakerInfo[], + txParams?: TxParams, + bracketOrdersParams?: OptionalOrderParams[], + referrerInfo?: ReferrerInfo, + cancelExistingOrders?: boolean, + settlePnl?: boolean + ): Promise<{ + cancelExistingOrdersTx?: Transaction | VersionedTransaction; + settlePnlTx?: Transaction | VersionedTransaction; + fillTx?: Transaction | VersionedTransaction; + marketOrderTx: Transaction | VersionedTransaction; + }>; + + /** + * Sends a market order and returns a signed tx which can fill the order against the vamm, which the caller can use to fill their own order if required. + * @param orderParams + * @param userAccountPublicKey + * @param userAccount + * @param makerInfo + * @param txParams + * @param bracketOrdersParams + * @param cancelExistingOrders - Builds and returns an extra transaciton to cancel the existing orders in the same perp market. Intended use is to auto-cancel TP/SL orders when closing a position. Ignored if orderParams.marketType is not MarketType.PERP + * @returns + */ + sendMarketOrderAndGetSignedFillTx( + orderParams: OptionalOrderParams, + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + makerInfo?: MakerInfo | MakerInfo[], + txParams?: TxParams, + bracketOrdersParams?: OptionalOrderParams[], + referrerInfo?: ReferrerInfo, + cancelExistingOrders?: boolean, + settlePnl?: boolean + ): Promise<{ + txSig: TransactionSignature; + signedFillTx?: Transaction; + signedCancelExistingOrdersTx?: Transaction; + signedSettlePnlTx?: Transaction; + }>; + + placePerpOrder( + orderParams: OptionalOrderParams, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getPlacePerpOrderIx( + orderParams: OptionalOrderParams, + subAccountId?: number, + depositToTradeArgs?: { + isMakingNewAccount: boolean; + depositMarketIndex: number; + } + ): Promise; + updateAMMs( + marketIndexes: number[], + txParams?: TxParams + ): Promise; + getUpdateAMMsIx(marketIndexes: number[]): Promise; + settleExpiredMarket( + marketIndex: number, + txParams?: TxParams + ): Promise; + getSettleExpiredMarketIx( + marketIndex: number + ): Promise; + settleExpiredMarketPoolsToRevenuePool( + marketIndex: number, + txParams?: TxParams + ): Promise; + getSettleExpiredMarketPoolsToRevenuePoolIx( + perpMarketIndex: number + ): Promise; + cancelOrder( + orderId?: number, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getCancelOrderIx( + orderId?: number, + subAccountId?: number + ): Promise; + cancelOrderByUserId( + userOrderId: number, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getCancelOrderByUserIdIx( + userOrderId: number, + subAccountId?: number + ): Promise; + cancelOrdersByIds( + orderIds?: number[], + txParams?: TxParams, + subAccountId?: number + ): Promise; + getCancelOrdersByIdsIx( + orderIds?: number[], + subAccountId?: number + ): Promise; + cancelOrders( + marketType?: MarketType, + marketIndex?: number, + direction?: PositionDirection, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getCancelOrdersIx( + marketType: MarketType | null, + marketIndex: number | null, + direction: PositionDirection | null, + subAccountId?: number + ): Promise; + cancelAndPlaceOrders( + cancelOrderParams: { + marketType?: MarketType; + marketIndex?: number; + direction?: PositionDirection; + }, + placeOrderParams: OrderParams[], + txParams?: TxParams, + subAccountId?: number + ): Promise; + placeOrders( + params: OrderParams[], + txParams?: TxParams, + subAccountId?: number, + optionalIxs?: TransactionInstruction[] + ): Promise; + preparePlaceOrdersTx( + params: OrderParams[], + txParams?: TxParams, + subAccountId?: number, + optionalIxs?: TransactionInstruction[] + ): Promise<{ + placeOrdersTx: Transaction | VersionedTransaction; + }>; + getPlaceOrdersIx( + params: OptionalOrderParams[], + subAccountId?: number + ): Promise; + fillPerpOrder( + userAccountPublicKey: PublicKey, + user: UserAccount, + order?: Pick, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + txParams?: TxParams, + fillerSubAccountId?: number, + fillerAuthority?: PublicKey + ): Promise; + getFillPerpOrderIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + order: Pick, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + fillerSubAccountId?: number, + isSignedMsg?: boolean, + fillerAuthority?: PublicKey + ): Promise; + getRevertFillIx(fillerPublicKey?: PublicKey): Promise; + placeSpotOrder( + orderParams: OptionalOrderParams, + txParams?: TxParams, + subAccountId?: number + ): Promise; + preparePlaceSpotOrderTx( + orderParams: OptionalOrderParams, + txParams?: TxParams, + subAccountId?: number + ): Promise<{ + placeSpotOrderTx: Transaction | VersionedTransaction; + }>; + getPlaceSpotOrderIx( + orderParams: OptionalOrderParams, + subAccountId?: number + ): Promise; + fillSpotOrder( + userAccountPublicKey: PublicKey, + user: UserAccount, + order?: Pick, + fulfillmentConfig?: + | SerumV3FulfillmentConfigAccount + | PhoenixV1FulfillmentConfigAccount + | OpenbookV2FulfillmentConfigAccount, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + txParams?: TxParams + ): Promise; + getFillSpotOrderIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + order?: Pick, + fulfillmentConfig?: + | SerumV3FulfillmentConfigAccount + | PhoenixV1FulfillmentConfigAccount + | OpenbookV2FulfillmentConfigAccount, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + fillerPublicKey?: PublicKey + ): Promise; + addSpotFulfillmentAccounts( + marketIndex: number, + remainingAccounts: AccountMeta[], + fulfillmentConfig?: + | SerumV3FulfillmentConfigAccount + | PhoenixV1FulfillmentConfigAccount + | OpenbookV2FulfillmentConfigAccount + ): void; + addSerumRemainingAccounts( + marketIndex: number, + remainingAccounts: AccountMeta[], + fulfillmentConfig: SerumV3FulfillmentConfigAccount + ): void; + addPhoenixRemainingAccounts( + marketIndex: number, + remainingAccounts: AccountMeta[], + fulfillmentConfig: PhoenixV1FulfillmentConfigAccount + ): void; + addOpenbookRemainingAccounts( + marketIndex: number, + remainingAccounts: AccountMeta[], + fulfillmentConfig: OpenbookV2FulfillmentConfigAccount + ): void; + + /** + * Swap tokens in drift account using jupiter + * @param jupiterClient jupiter client to find routes and jupiter instructions + * @param outMarketIndex the market index of the token you're buying + * @param inMarketIndex the market index of the token you're selling + * @param outAssociatedTokenAccount the token account to receive the token being sold on jupiter + * @param inAssociatedTokenAccount the token account to + * @param amount the amount of TokenIn, regardless of swapMode + * @param slippageBps the max slippage passed to jupiter api + * @param swapMode jupiter swapMode (ExactIn or ExactOut), default is ExactIn + * @param route the jupiter route to use for the swap + * @param reduceOnly specify if In or Out token on the drift account must reduceOnly, checked at end of swap + * @param v6 pass in the quote response from Jupiter quote's API (deprecated, use quote instead) + * @param quote pass in the quote response from Jupiter quote's API + * @param txParams + */ + swap({ + jupiterClient, + outMarketIndex, + inMarketIndex, + outAssociatedTokenAccount, + inAssociatedTokenAccount, + amount, + slippageBps, + swapMode, + reduceOnly, + txParams, + v6, + quote, + onlyDirectRoutes, + }: { + jupiterClient: JupiterClient; + outMarketIndex: number; + inMarketIndex: number; + outAssociatedTokenAccount?: PublicKey; + inAssociatedTokenAccount?: PublicKey; + amount: BN; + slippageBps?: number; + swapMode?: SwapMode; + reduceOnly?: SwapReduceOnly; + txParams?: TxParams; + onlyDirectRoutes?: boolean; + v6?: { + quote?: QuoteResponse; + }; + quote?: QuoteResponse; + }): Promise; + + getJupiterSwapIxV6({ + jupiterClient, + outMarketIndex, + inMarketIndex, + outAssociatedTokenAccount, + inAssociatedTokenAccount, + amount, + slippageBps, + swapMode, + onlyDirectRoutes, + quote, + reduceOnly, + userAccountPublicKey, + }: { + jupiterClient: JupiterClient; + outMarketIndex: number; + inMarketIndex: number; + outAssociatedTokenAccount?: PublicKey; + inAssociatedTokenAccount?: PublicKey; + amount: BN; + slippageBps?: number; + swapMode?: SwapMode; + onlyDirectRoutes?: boolean; + quote?: QuoteResponse; + reduceOnly?: SwapReduceOnly; + userAccountPublicKey?: PublicKey; + }): Promise<{ + ixs: TransactionInstruction[]; + lookupTables: AddressLookupTableAccount[]; + }>; + + /** + * Get the drift begin_swap and end_swap instructions + * + * @param outMarketIndex the market index of the token you're buying + * @param inMarketIndex the market index of the token you're selling + * @param amountIn the amount of the token to sell + * @param inTokenAccount the token account to move the tokens being sold + * @param outTokenAccount the token account to receive the tokens being bought + * @param limitPrice the limit price of the swap + * @param reduceOnly + * @param userAccountPublicKey optional, specify a custom userAccountPublicKey to use instead of getting the current user account; can be helpful if the account is being created within the current tx + */ + getSwapIx({ + outMarketIndex, + inMarketIndex, + amountIn, + inTokenAccount, + outTokenAccount, + limitPrice, + reduceOnly, + userAccountPublicKey, + }: { + outMarketIndex: number; + inMarketIndex: number; + amountIn: BN; + inTokenAccount: PublicKey; + outTokenAccount: PublicKey; + limitPrice?: BN; + reduceOnly?: SwapReduceOnly; + userAccountPublicKey?: PublicKey; + }): Promise<{ + beginSwapIx: TransactionInstruction; + endSwapIx: TransactionInstruction; + }>; + + stakeForMSOL(params: { amount: BN }): Promise; + getStakeForMSOLIx({ + amount, + userAccountPublicKey, + }: { + amount: BN; + userAccountPublicKey?: PublicKey; + }): Promise; + triggerOrder( + userAccountPublicKey: PublicKey, + user: UserAccount, + order: Order, + txParams?: TxParams, + fillerPublicKey?: PublicKey + ): Promise; + getTriggerOrderIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + order: Order, + fillerPublicKey?: PublicKey + ): Promise; + forceCancelOrders( + userAccountPublicKey: PublicKey, + user: UserAccount, + txParams?: TxParams, + fillerPublicKey?: PublicKey + ): Promise; + getForceCancelOrdersIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + fillerPublicKey?: PublicKey + ): Promise; + updateUserIdle( + userAccountPublicKey: PublicKey, + user: UserAccount, + txParams?: TxParams, + fillerPublicKey?: PublicKey + ): Promise; + getUpdateUserIdleIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + fillerPublicKey?: PublicKey + ): Promise; + logUserBalances( + userAccountPublicKey: PublicKey, + txParams?: TxParams + ): Promise; + getLogUserBalancesIx( + userAccountPublicKey: PublicKey + ): Promise; + updateUserFuelBonus( + userAccountPublicKey: PublicKey, + user: UserAccount, + userAuthority: PublicKey, + txParams?: TxParams + ): Promise; + getUpdateUserFuelBonusIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + userAuthority: PublicKey + ): Promise; + updateUserStatsReferrerStatus( + userAuthority: PublicKey, + txParams?: TxParams + ): Promise; + getUpdateUserStatsReferrerStatusIx( + userAuthority: PublicKey + ): Promise; + updateUserOpenOrdersCount( + userAccountPublicKey: PublicKey, + user: UserAccount, + txParams?: TxParams, + fillerPublicKey?: PublicKey + ): Promise; + getUpdateUserOpenOrdersCountIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + fillerPublicKey?: PublicKey + ): Promise; + placeAndTakePerpOrder( + orderParams: OptionalOrderParams, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + successCondition?: PlaceAndTakeOrderSuccessCondition, + auctionDurationPercentage?: number, + txParams?: TxParams, + subAccountId?: number + ): Promise; + preparePlaceAndTakePerpOrderWithAdditionalOrders( + orderParams: OptionalOrderParams, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + bracketOrdersParams?: OptionalOrderParams[], + txParams?: TxParams, + subAccountId?: number, + cancelExistingOrders?: boolean, + settlePnl?: boolean, + exitEarlyIfSimFails?: boolean, + auctionDurationPercentage?: number, + optionalIxs?: TransactionInstruction[] + ): Promise<{ + placeAndTakeTx: Transaction | VersionedTransaction; + cancelExistingOrdersTx: Transaction | VersionedTransaction; + settlePnlTx: Transaction | VersionedTransaction; + }>; + placeAndTakePerpWithAdditionalOrders( + orderParams: OptionalOrderParams, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + bracketOrdersParams?: OptionalOrderParams[], + txParams?: TxParams, + subAccountId?: number, + cancelExistingOrders?: boolean, + settlePnl?: boolean, + exitEarlyIfSimFails?: boolean + ): Promise<{ + txSig: TransactionSignature; + signedCancelExistingOrdersTx?: Transaction; + signedSettlePnlTx?: Transaction; + }>; + getPlaceAndTakePerpOrderIx( + orderParams: OptionalOrderParams, + makerInfo?: MakerInfo | MakerInfo[], + referrerInfo?: ReferrerInfo, + successCondition?: PlaceAndTakeOrderSuccessCondition, + auctionDurationPercentage?: number, + subAccountId?: number + ): Promise; + placeAndMakePerpOrder( + orderParams: OptionalOrderParams, + takerInfo: TakerInfo, + referrerInfo?: ReferrerInfo, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getPlaceAndMakePerpOrderIx( + orderParams: OptionalOrderParams, + takerInfo: TakerInfo, + referrerInfo?: ReferrerInfo, + subAccountId?: number + ): Promise; + signSignedMsgOrderParamsMessage( + orderParamsMessage: + | SignedMsgOrderParamsMessage + | SignedMsgOrderParamsDelegateMessage, + delegateSigner?: boolean + ): SignedMsgOrderParams; + + /* + * Borsh encode signedMsg taker order params + */ + encodeSignedMsgOrderParamsMessage( + orderParamsMessage: + | SignedMsgOrderParamsMessage + | SignedMsgOrderParamsDelegateMessage, + delegateSigner?: boolean + ): Buffer; + + /* + * Decode signedMsg taker order params from borsh buffer + */ + decodeSignedMsgOrderParamsMessage( + encodedMessage: Buffer, + delegateSigner?: boolean + ): SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage; + + signMessage(message: Uint8Array, keypair?: Keypair): Buffer; + placeSignedMsgTakerOrder( + signedSignedMsgOrderParams: SignedMsgOrderParams, + marketIndex: number, + takerInfo: { + taker: PublicKey; + takerStats: PublicKey; + takerUserAccount: UserAccount; + signingAuthority: PublicKey; + }, + precedingIxs?: TransactionInstruction[], + overrideCustomIxIndex?: number, + txParams?: TxParams + ): Promise; + getPlaceSignedMsgTakerPerpOrderIxs( + signedSignedMsgOrderParams: SignedMsgOrderParams, + marketIndex: number, + takerInfo: { + taker: PublicKey; + takerStats: PublicKey; + takerUserAccount: UserAccount; + signingAuthority: PublicKey; + }, + precedingIxs?: TransactionInstruction[], + overrideCustomIxIndex?: number + ): Promise; + placeAndMakeSignedMsgPerpOrder( + signedSignedMsgOrderParams: SignedMsgOrderParams, + signedMsgOrderUuid: Uint8Array, + takerInfo: { + taker: PublicKey; + takerStats: PublicKey; + takerUserAccount: UserAccount; + signingAuthority: PublicKey; + }, + orderParams: OptionalOrderParams, + referrerInfo?: ReferrerInfo, + txParams?: TxParams, + subAccountId?: number, + precedingIxs?: TransactionInstruction[], + overrideCustomIxIndex?: number + ): Promise; + getPlaceAndMakeSignedMsgPerpOrderIxs( + signedSignedMsgOrderParams: SignedMsgOrderParams, + signedMsgOrderUuid: Uint8Array, + takerInfo: { + taker: PublicKey; + takerStats: PublicKey; + takerUserAccount: UserAccount; + signingAuthority: PublicKey; + }, + orderParams: OptionalOrderParams, + referrerInfo?: ReferrerInfo, + subAccountId?: number, + precedingIxs?: TransactionInstruction[], + overrideCustomIxIndex?: number + ): Promise; + preparePlaceAndTakeSpotOrder( + orderParams: OptionalOrderParams, + fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + makerInfo?: MakerInfo, + referrerInfo?: ReferrerInfo, + txParams?: TxParams, + subAccountId?: number + ): Promise<{ + placeAndTakeSpotOrderTx: Transaction | VersionedTransaction; + }>; + placeAndTakeSpotOrder( + orderParams: OptionalOrderParams, + fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + makerInfo?: MakerInfo, + referrerInfo?: ReferrerInfo, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getPlaceAndTakeSpotOrderIx( + orderParams: OptionalOrderParams, + fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + makerInfo?: MakerInfo, + referrerInfo?: ReferrerInfo, + subAccountId?: number + ): Promise; + placeAndMakeSpotOrder( + orderParams: OptionalOrderParams, + takerInfo: TakerInfo, + fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + referrerInfo?: ReferrerInfo, + txParams?: TxParams, + subAccountId?: number + ): Promise; + getPlaceAndMakeSpotOrderIx( + orderParams: OptionalOrderParams, + takerInfo: TakerInfo, + fulfillmentConfig?: SerumV3FulfillmentConfigAccount, + referrerInfo?: ReferrerInfo, + subAccountId?: number + ): Promise; + + /** + * @deprecated use {@link placePerpOrder} or {@link placeAndTakePerpOrder} instead + */ + closePosition( + marketIndex: number, + limitPrice?: BN, + subAccountId?: number + ): Promise; + + /** + * Modifies an open order by closing it and replacing it with a new order. + * @deprecated use modifyOrder instead + * @param orderId: The open order to modify + * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. + * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. + * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. + * @returns + */ + modifyPerpOrder( + orderId: number, + newBaseAmount?: BN, + newLimitPrice?: BN, + newOraclePriceOffset?: number + ): Promise; + + /** + * Modifies an open order by closing it and replacing it with a new order. + * @deprecated use modifyOrderByUserOrderId instead + * @param userOrderId: The open order to modify + * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. + * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. + * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. + * @returns + */ + modifyPerpOrderByUserOrderId( + userOrderId: number, + newBaseAmount?: BN, + newLimitPrice?: BN, + newOraclePriceOffset?: number + ): Promise; + + /** + * Modifies an open order (spot or perp) by closing it and replacing it with a new order. + * @param orderParams.orderId: The open order to modify + * @param orderParams.newDirection: The new direction for the order + * @param orderParams.newBaseAmount: The new base amount for the order + * @param orderParams.newLimitPice: The new limit price for the order + * @param orderParams.newOraclePriceOffset: The new oracle price offset for the order + * @param orderParams.newTriggerPrice: Optional - Thew new trigger price for the order. + * @param orderParams.auctionDuration: + * @param orderParams.auctionStartPrice: + * @param orderParams.auctionEndPrice: + * @param orderParams.reduceOnly: + * @param orderParams.postOnly: + * @param orderParams.bitFlags: + * @param orderParams.policy: + * @param orderParams.maxTs: + * @returns + */ + modifyOrder( + orderParams: { + orderId: number; + newDirection?: PositionDirection; + newBaseAmount?: BN; + newLimitPrice?: BN; + newOraclePriceOffset?: number; + newTriggerPrice?: BN; + newTriggerCondition?: OrderTriggerCondition; + auctionDuration?: number; + auctionStartPrice?: BN; + auctionEndPrice?: BN; + reduceOnly?: boolean; + postOnly?: boolean; + bitFlags?: number; + maxTs?: BN; + policy?: number; + }, + txParams?: TxParams, + subAccountId?: number + ): Promise; + + getModifyOrderIx( + orderParams: { + orderId: number; + newDirection?: PositionDirection; + newBaseAmount?: BN; + newLimitPrice?: BN; + newOraclePriceOffset?: number; + newTriggerPrice?: BN; + newTriggerCondition?: OrderTriggerCondition; + auctionDuration?: number; + auctionStartPrice?: BN; + auctionEndPrice?: BN; + reduceOnly?: boolean; + postOnly?: boolean; + bitFlags?: number; + maxTs?: BN; + policy?: number; + }, + subAccountId?: number + ): Promise; + + /** + * Modifies an open order by closing it and replacing it with a new order. + * @param orderParams.userOrderId: The open order to modify + * @param orderParams.newDirection: The new direction for the order + * @param orderParams.newBaseAmount: The new base amount for the order + * @param orderParams.newLimitPice: The new limit price for the order + * @param orderParams.newOraclePriceOffset: The new oracle price offset for the order + * @param orderParams.newTriggerPrice: Optional - Thew new trigger price for the order. + * @param orderParams.auctionDuration: Only required if order type changed to market from something else + * @param orderParams.auctionStartPrice: Only required if order type changed to market from something else + * @param orderParams.auctionEndPrice: Only required if order type changed to market from something else + * @param orderParams.reduceOnly: + * @param orderParams.postOnly: + * @param orderParams.bitFlags: + * @param orderParams.policy: + * @param orderParams.maxTs: + * @returns + */ + modifyOrderByUserOrderId( + orderParams: { + userOrderId: number; + newDirection?: PositionDirection; + newBaseAmount?: BN; + newLimitPrice?: BN; + newOraclePriceOffset?: number; + newTriggerPrice?: BN; + newTriggerCondition?: OrderTriggerCondition; + auctionDuration?: number; + auctionStartPrice?: BN; + auctionEndPrice?: BN; + reduceOnly?: boolean; + postOnly?: boolean; + bitFlags?: number; + policy?: ModifyOrderPolicy; + maxTs?: BN; + }, + txParams?: TxParams, + subAccountId?: number + ): Promise; + + getModifyOrderByUserIdIx( + orderParams: { + userOrderId: number; + newDirection?: PositionDirection; + newBaseAmount?: BN; + newLimitPrice?: BN; + newOraclePriceOffset?: number; + newTriggerPrice?: BN; + newTriggerCondition?: OrderTriggerCondition; + auctionDuration?: number; + auctionStartPrice?: BN; + auctionEndPrice?: BN; + reduceOnly?: boolean; + postOnly?: boolean; + bitFlags?: number; + policy?: ModifyOrderPolicy; + maxTs?: BN; + txParams?: TxParams; + }, + subAccountId?: number + ): Promise; + settlePNLs( + users: { + settleeUserAccountPublicKey: PublicKey; + settleeUserAccount: UserAccount; + }[], + marketIndexes: number[], + opts?: { + filterInvalidMarkets?: boolean; + }, + txParams?: TxParams + ): Promise; + getSettlePNLsIxs( + users: { + settleeUserAccountPublicKey: PublicKey; + settleeUserAccount: UserAccount; + }[], + marketIndexes: number[] + ): Promise; + settlePNL( + settleeUserAccountPublicKey: PublicKey, + settleeUserAccount: UserAccount, + marketIndex: number, + txParams?: TxParams, + optionalIxs?: TransactionInstruction[] + ): Promise; + settlePNLIx( + settleeUserAccountPublicKey: PublicKey, + settleeUserAccount: UserAccount, + marketIndex: number + ): Promise; + settleMultiplePNLs( + settleeUserAccountPublicKey: PublicKey, + settleeUserAccount: UserAccount, + marketIndexes: number[], + mode: SettlePnlMode, + txParams?: TxParams + ): Promise; + settleMultiplePNLsMultipleTxs( + settleeUserAccountPublicKey: PublicKey, + settleeUserAccount: UserAccount, + marketIndexes: number[], + mode: SettlePnlMode, + txParams?: TxParams, + optionalIxs?: TransactionInstruction[] + ): Promise; + settleMultiplePNLsIx( + settleeUserAccountPublicKey: PublicKey, + settleeUserAccount: UserAccount, + marketIndexes: number[], + mode: SettlePnlMode + ): Promise; + getSetUserStatusToBeingLiquidatedIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount + ): Promise; + setUserStatusToBeingLiquidated( + userAccountPublicKey: PublicKey, + userAccount: UserAccount + ): Promise; + liquidatePerp( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + maxBaseAssetAmount: BN, + limitPrice?: BN, + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getLiquidatePerpIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + maxBaseAssetAmount: BN, + limitPrice?: BN, + liquidatorSubAccountId?: number + ): Promise; + liquidatePerpWithFill( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + makerInfos: MakerInfo[], + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getLiquidatePerpWithFillIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + makerInfos: MakerInfo[], + liquidatorSubAccountId?: number + ): Promise; + liquidateSpot( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + assetMarketIndex: number, + liabilityMarketIndex: number, + maxLiabilityTransfer: BN, + limitPrice?: BN, + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getLiquidateSpotIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + assetMarketIndex: number, + liabilityMarketIndex: number, + maxLiabilityTransfer: BN, + limitPrice?: BN, + liquidatorSubAccountId?: number + ): Promise; + getJupiterLiquidateSpotWithSwapIxV6(params: { + jupiterClient: JupiterClient; + liabilityMarketIndex: number; + assetMarketIndex: number; + swapAmount: BN; + assetTokenAccount?: PublicKey; + liabilityTokenAccount?: PublicKey; + slippageBps?: number; + swapMode?: SwapMode; + onlyDirectRoutes?: boolean; + quote?: QuoteResponse; + userAccount: UserAccount; + userAccountPublicKey: PublicKey; + userStatsAccountPublicKey: PublicKey; + liquidatorSubAccountId?: number; + maxAccounts?: number; + }): Promise<{ + ixs: TransactionInstruction[]; + lookupTables: AddressLookupTableAccount[]; + }>; + + /** + * Get the drift liquidate_spot_with_swap instructions + * + * @param liabilityMarketIndex the market index of the token you're buying + * @param assetMarketIndex the market index of the token you're selling + * @param amountIn the amount of the token to sell + * @param assetTokenAccount the token account to move the tokens being sold + * @param liabilityTokenAccount the token account to receive the tokens being bought + * @param userAccount + * @param userAccountPublicKey + * @param userStatsAccountPublicKey + */ + getLiquidateSpotWithSwapIx(params: { + liabilityMarketIndex: number; + assetMarketIndex: number; + swapAmount: BN; + assetTokenAccount: PublicKey; + liabilityTokenAccount: PublicKey; + userAccount: UserAccount; + userAccountPublicKey: PublicKey; + userStatsAccountPublicKey: PublicKey; + liquidatorSubAccountId?: number; + }): Promise<{ + beginSwapIx: TransactionInstruction; + endSwapIx: TransactionInstruction; + }>; + + getInsuranceFundSwapIx(params: { + inMarketIndex: number; + outMarketIndex: number; + amountIn: BN; + inTokenAccount: PublicKey; + outTokenAccount: PublicKey; + }): Promise<{ + beginSwapIx: TransactionInstruction; + endSwapIx: TransactionInstruction; + }>; + liquidateBorrowForPerpPnl( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + perpMarketIndex: number, + liabilityMarketIndex: number, + maxLiabilityTransfer: BN, + limitPrice?: BN, + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getLiquidateBorrowForPerpPnlIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + perpMarketIndex: number, + liabilityMarketIndex: number, + maxLiabilityTransfer: BN, + limitPrice?: BN, + liquidatorSubAccountId?: number + ): Promise; + liquidatePerpPnlForDeposit( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + perpMarketIndex: number, + assetMarketIndex: number, + maxPnlTransfer: BN, + limitPrice?: BN, + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getLiquidatePerpPnlForDepositIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + perpMarketIndex: number, + assetMarketIndex: number, + maxPnlTransfer: BN, + limitPrice?: BN, + liquidatorSubAccountId?: number + ): Promise; + resolvePerpBankruptcy( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getResolvePerpBankruptcyIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + liquidatorSubAccountId?: number + ): Promise; + resolveSpotBankruptcy( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + txParams?: TxParams, + liquidatorSubAccountId?: number + ): Promise; + getResolveSpotBankruptcyIx( + userAccountPublicKey: PublicKey, + userAccount: UserAccount, + marketIndex: number, + liquidatorSubAccountId?: number + ): Promise; + updateFundingRate( + perpMarketIndex: number, + oracle: PublicKey, + txParams?: TxParams + ): Promise; + getUpdateFundingRateIx( + perpMarketIndex: number, + oracle: PublicKey + ): Promise; + updatePrelaunchOracle( + perpMarketIndex: number, + txParams?: TxParams + ): Promise; + getUpdatePrelaunchOracleIx( + perpMarketIndex: number + ): Promise; + updatePerpBidAskTwap( + perpMarketIndex: number, + makers: [PublicKey, PublicKey][], + txParams?: TxParams + ): Promise; + getUpdatePerpBidAskTwapIx( + perpMarketIndex: number, + makers: [PublicKey, PublicKey][] + ): Promise; + settleFundingPayment( + userAccountPublicKey: PublicKey, + txParams?: TxParams + ): Promise; + getSettleFundingPaymentIx( + userAccountPublicKey: PublicKey + ): Promise; + triggerEvent(eventName: keyof DriftClientAccountEvents, data?: any): void; + getOracleDataForPerpMarket(marketIndex: number): OraclePriceData; + getMMOracleDataForPerpMarket(marketIndex: number): OraclePriceData; + getOracleDataForSpotMarket(marketIndex: number): OraclePriceData; + initializeInsuranceFundStake( + marketIndex: number, + txParams?: TxParams + ): Promise; + getInitializeInsuranceFundStakeIx( + marketIndex: number + ): Promise; + getAddInsuranceFundStakeIx( + marketIndex: number, + amount: BN, + collateralAccountPublicKey: PublicKey + ): Promise; + + /** + * Add to an insurance fund stake and optionally initialize the account + */ + addInsuranceFundStake(params: { + /** + * Spot market index + */ + marketIndex: number; + amount: BN; + /** + * The account where the funds to stake come from. Usually an associated token account + */ + collateralAccountPublicKey: PublicKey; + /** + * Add instructions to initialize the staking account -- required if its the first time the currrent authority has staked in this market + */ + initializeStakeAccount?: boolean; + /** + * Optional -- withdraw from current subaccount to fund stake amount, instead of wallet balance + */ + fromSubaccount?: boolean; + txParams?: TxParams; + }): Promise; + + /** + * Get instructions to add to an insurance fund stake and optionally initialize the account + */ + getAddInsuranceFundStakeIxs(params: { + /** + * Spot market index + */ + marketIndex: number; + amount: BN; + /** + * The account where the funds to stake come from. Usually an associated token account + */ + collateralAccountPublicKey: PublicKey; + /** + * Add instructions to initialize the staking account -- required if its the first time the currrent authority has staked in this market + */ + initializeStakeAccount?: boolean; + /** + * Optional -- withdraw from current subaccount to fund stake amount, instead of wallet balance + */ + fromSubaccount?: boolean; + }): Promise; + + requestRemoveInsuranceFundStake( + marketIndex: number, + amount: BN, + txParams?: TxParams + ): Promise; + cancelRequestRemoveInsuranceFundStake( + marketIndex: number, + txParams?: TxParams + ): Promise; + removeInsuranceFundStake( + marketIndex: number, + collateralAccountPublicKey: PublicKey, + txParams?: TxParams + ): Promise; + updateUserQuoteAssetInsuranceStake( + authority: PublicKey, + txParams?: TxParams + ): Promise; + getUpdateUserQuoteAssetInsuranceStakeIx( + authority: PublicKey + ): Promise; + updateUserGovTokenInsuranceStake( + authority: PublicKey, + txParams?: TxParams, + env?: DriftEnv + ): Promise; + getUpdateUserGovTokenInsuranceStakeIx( + authority: PublicKey + ): Promise; + getUpdateUserGovTokenInsuranceStakeDevnetIx( + authority: PublicKey, + amount?: BN + ): Promise; + settleRevenueToInsuranceFund( + spotMarketIndex: number, + txParams?: TxParams + ): Promise; + getSettleRevenueToInsuranceFundIx( + spotMarketIndex: number + ): Promise; + resolvePerpPnlDeficit( + spotMarketIndex: number, + perpMarketIndex: number, + txParams?: TxParams + ): Promise; + getResolvePerpPnlDeficitIx( + spotMarketIndex: number, + perpMarketIndex: number + ): Promise; + getDepositIntoSpotMarketRevenuePoolIx( + marketIndex: number, + amount: BN, + userTokenAccountPublicKey: PublicKey + ): Promise; + + /** + * This ix will donate your funds to drift revenue pool. It does not deposit into your user account + * @param marketIndex + * @param amount + * @param userTokenAccountPublicKey + * @returns + */ + depositIntoSpotMarketRevenuePool( + marketIndex: number, + amount: BN, + userTokenAccountPublicKey: PublicKey + ): Promise; + + getPerpMarketExtendedInfo(marketIndex: number): PerpMarketExtendedInfo; + + /** + * Calculates taker / maker fee (as a percentage, e.g. .001 = 10 basis points) for particular marketType + * @param marketType + * @param positionMarketIndex + * @returns : {takerFee: number, makerFee: number} Precision None + */ + getMarketFees( + marketType: MarketType, + marketIndex?: number, + user?: User, + enteringHighLeverageMode?: boolean + ): { takerFee: number; makerFee: number }; + + /** + * Returns the market index and type for a given market name + * E.g. "SOL-PERP" -> { marketIndex: 0, marketType: MarketType.PERP } + * + * @param name + */ + getMarketIndexAndType( + name: string + ): { marketIndex: number; marketType: MarketType } | undefined; + + getReceiverProgram(): Program; + getSwitchboardOnDemandProgram(): Promise>; + postPythPullOracleUpdateAtomic( + vaaString: string, + feedId: string + ): Promise; + postMultiPythPullOracleUpdatesAtomic( + vaaString: string, + feedIds: string[] + ): Promise; + getPostPythPullOracleUpdateAtomicIxs( + vaaString: string, + feedIds: string | string[], + numSignatures?: number + ): Promise; + updatePythPullOracle( + vaaString: string, + feedId: string + ): Promise; + getUpdatePythPullOracleIxs( + params: { + merklePriceUpdate: { + message: Buffer; + proof: number[][]; + }; + }, + feedId: string, + encodedVaaAddress: PublicKey + ): Promise; + postPythLazerOracleUpdate( + feedIds: number[], + pythMessageHex: string + ): Promise; + getPostPythLazerOracleUpdateIxs( + feedIds: number[], + pythMessageHex: string, + precedingIxs?: TransactionInstruction[], + overrideCustomIxIndex?: number + ): Promise; + getPostManySwitchboardOnDemandUpdatesAtomicIxs( + feeds: PublicKey[], + recentSlothash?: Slothash, + numSignatures?: number + ): Promise; + + // @deprecated use getPostManySwitchboardOnDemandUpdatesAtomicIxs instead. This function no longer returns the required ixs due to upstream sdk changes. + getPostSwitchboardOnDemandUpdateAtomicIx( + feed: PublicKey, + recentSlothash?: Slothash, + numSignatures?: number + ): Promise; + + postSwitchboardOnDemandUpdate( + feed: PublicKey, + recentSlothash?: Slothash, + numSignatures?: number + ): Promise; + enableUserHighLeverageMode( + subAccountId: number, + txParams?: TxParams + ): Promise; + getEnableHighLeverageModeIx( + subAccountId: number, + depositToTradeArgs?: { + isMakingNewAccount: boolean; + depositMarketIndex: number; + orderMarketIndex: number; + } + ): Promise; + disableUserHighLeverageMode( + user: PublicKey, + userAccount?: UserAccount, + txParams?: TxParams + ): Promise; + getDisableHighLeverageModeIx( + user: PublicKey, + userAccount?: UserAccount + ): Promise; + fetchHighLeverageModeConfig(): Promise; + fetchProtectedMakerModeConfig(): Promise; + updateUserProtectedMakerOrders( + subAccountId: number, + protectedOrders: boolean, + authority?: PublicKey, + txParams?: TxParams + ): Promise; + getUpdateUserProtectedMakerOrdersIx( + subAccountId: number, + protectedOrders: boolean, + authority?: PublicKey + ): Promise; + getPauseSpotMarketDepositWithdrawIx( + spotMarketIndex: number + ): Promise; + pauseSpotMarketDepositWithdraw( + spotMarketIndex: number, + txParams?: TxParams + ): Promise; + updateMmOracleNative( + marketIndex: number, + oraclePrice: BN, + oracleSequenceId: BN + ): Promise; + getUpdateMmOracleNativeIx( + marketIndex: number, + oraclePrice: BN, + oracleSequenceId: BN + ): Promise; + updateAmmSpreadAdjustmentNative( + marketIndex: number, + ammSpreadAdjustment: number + ): Promise; + getUpdateAmmSpreadAdjustmentNativeIx( + marketIndex: number, + ammSpreadAdjustment: number + ): TransactionInstruction; + + /** + * Send a transaction. + * + * @param tx + * @param additionalSigners + * @param opts :: Will fallback to DriftClient's opts if not provided + * @param preSigned + * @returns + */ + sendTransaction( + tx: Transaction | VersionedTransaction, + additionalSigners?: Signer[], + opts?: ConfirmOptions, + preSigned?: boolean + ): Promise; + + buildTransaction( + instructions: TransactionInstruction | TransactionInstruction[], + txParams?: TxParams, + txVersion?: TransactionVersion, + lookupTables?: AddressLookupTableAccount[], + forceVersionedTransaction?: boolean, + recentBlockhash?: BlockhashWithExpiryBlockHeight, + optionalIxs?: TransactionInstruction[] + ): Promise; + buildBulkTransactions( + instructions: (TransactionInstruction | TransactionInstruction[])[], + txParams?: TxParams, + txVersion?: TransactionVersion, + lookupTables?: AddressLookupTableAccount[], + forceVersionedTransaction?: boolean + ): Promise<(Transaction | VersionedTransaction)[]>; + buildTransactionsMap( + instructionsMap: Record< + string, + TransactionInstruction | TransactionInstruction[] + >, + txParams?: TxParams, + txVersion?: TransactionVersion, + lookupTables?: AddressLookupTableAccount[], + forceVersionedTransaction?: boolean + ): ReturnType; + buildAndSignTransactionsMap( + instructionsMap: Record< + string, + TransactionInstruction | TransactionInstruction[] + >, + txParams?: TxParams, + txVersion?: TransactionVersion, + lookupTables?: AddressLookupTableAccount[], + forceVersionedTransaction?: boolean + ): ReturnType; +} diff --git a/sdk/src/events/types.ts b/sdk/src/events/types.ts index 3c4c92548c..2671344ceb 100644 --- a/sdk/src/events/types.ts +++ b/sdk/src/events/types.ts @@ -21,7 +21,7 @@ import { FuelSeasonRecord, InsuranceFundSwapRecord, TransferProtocolIfSharesToRevenuePoolRecord, -} from '../index'; +} from '../types'; import { EventEmitter } from 'events'; export type EventSubscriptionOptions = { diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index 71f29c9845..ea38698e46 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -24,7 +24,7 @@ import { } from '../types'; import { assert } from '../assert/assert'; import { squareRootBN, sigNum, clampBN } from './utils'; -import { standardizeBaseAssetAmount } from './orders'; +import { standardizeBaseAssetAmount } from './utils'; import { OraclePriceData } from '../oracles/types'; import { diff --git a/sdk/src/math/insurance.ts b/sdk/src/math/insurance.ts index 093a780073..db846e0e4f 100644 --- a/sdk/src/math/insurance.ts +++ b/sdk/src/math/insurance.ts @@ -1,6 +1,7 @@ import { PERCENTAGE_PRECISION, ZERO } from '../constants/numericConstants'; -import { BN, SpotMarketAccount, SpotBalanceType } from '../index'; +import { SpotMarketAccount, SpotBalanceType } from '../types'; import { getTokenAmount } from '../math/spotBalance'; +import { BN } from '@coral-xyz/anchor'; export function nextRevenuePoolSettleApr( spotMarket: SpotMarketAccount, diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index c7c9b30d4e..11c3bdc422 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -28,7 +28,7 @@ import { QUOTE_SPOT_MARKET_INDEX, } from '../constants/numericConstants'; import { getTokenAmount } from './spotBalance'; -import { DLOB } from '../dlob/DLOB'; +import { IDLOB } from '../dlob/types'; import { assert } from '../assert/assert'; /** @@ -295,7 +295,7 @@ export function calculateNetUserPnlImbalance( export function calculateAvailablePerpLiquidity( market: PerpMarketAccount, oraclePriceData: OraclePriceData, - dlob: DLOB, + dlob: IDLOB, slot: number ): { bids: BN; asks: BN } { let [bids, asks] = calculateMarketOpenBidAsk( diff --git a/sdk/src/math/oracles.ts b/sdk/src/math/oracles.ts index bfbd88702c..8873c66150 100644 --- a/sdk/src/math/oracles.ts +++ b/sdk/src/math/oracles.ts @@ -1,4 +1,11 @@ -import { AMM, OracleGuardRails, isVariant } from '../types'; +import { + AMM, + HistoricalOracleData, + OracleGuardRails, + OracleSource, + PerpMarketAccount, + isVariant, +} from '../types'; import { OraclePriceData } from '../oracles/types'; import { BID_ASK_SPREAD_PRECISION, @@ -9,13 +16,8 @@ import { FIVE_MINUTE, PERCENTAGE_PRECISION, } from '../constants/numericConstants'; -import { - BN, - HistoricalOracleData, - OracleSource, - PerpMarketAccount, -} from '../index'; import { assert } from '../assert/assert'; +import { BN } from '@coral-xyz/anchor'; export function oraclePriceBands( market: PerpMarketAccount, diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index daa70b4929..7c06f7b51f 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -21,6 +21,7 @@ import { calculateMaxBaseAssetAmountToTrade, calculateUpdatedAMM, } from './amm'; +import { standardizeBaseAssetAmount } from './utils'; export function isOrderRiskIncreasing(user: User, order: Order): boolean { if (!isVariant(order.status, 'open')) { @@ -121,14 +122,6 @@ export function isOrderReduceOnly(user: User, order: Order): boolean { return true; } -export function standardizeBaseAssetAmount( - baseAssetAmount: BN, - stepSize: BN -): BN { - const remainder = baseAssetAmount.mod(stepSize); - return baseAssetAmount.sub(remainder); -} - export function standardizePrice( price: BN, tickSize: BN, diff --git a/sdk/src/math/utils.ts b/sdk/src/math/utils.ts index fcebbdb78d..e5ad56b67a 100644 --- a/sdk/src/math/utils.ts +++ b/sdk/src/math/utils.ts @@ -119,3 +119,11 @@ export function numberToSafeBN(number: number, precision: BN): BN { } } } + +export function standardizeBaseAssetAmount( + baseAssetAmount: BN, + stepSize: BN +): BN { + const remainder = baseAssetAmount.mod(stepSize); + return baseAssetAmount.sub(remainder); +} diff --git a/sdk/src/orderSubscriber/OrderSubscriber.ts b/sdk/src/orderSubscriber/OrderSubscriber.ts index 5dca0aed4f..ef63ad887e 100644 --- a/sdk/src/orderSubscriber/OrderSubscriber.ts +++ b/sdk/src/orderSubscriber/OrderSubscriber.ts @@ -1,4 +1,4 @@ -import { DriftClient } from '../driftClient'; +import { IDriftClient } from '../driftClient/types'; import { UserAccount } from '../types'; import { getNonIdleUserFilter, @@ -8,18 +8,23 @@ import { import { Commitment, PublicKey, RpcResponseAndContext } from '@solana/web3.js'; import { Buffer } from 'buffer'; import { DLOB } from '../dlob/DLOB'; -import { OrderSubscriberConfig, OrderSubscriberEvents } from './types'; +import { + IOrderSubscriber, + OrderSubscriberConfig, + OrderSubscriberEvents, +} from './types'; import { PollingSubscription } from './PollingSubscription'; import { WebsocketSubscription } from './WebsocketSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { BN, ProtectMakerParamsMap } from '../index'; import { decodeUser } from '../decode/user'; import { grpcSubscription } from './grpcSubscription'; import { isUserProtectedMaker } from '../math/userStatus'; +import { BN } from '@coral-xyz/anchor'; +import { ProtectMakerParamsMap } from '../dlob/types'; -export class OrderSubscriber { - driftClient: DriftClient; +export class OrderSubscriber implements IOrderSubscriber { + driftClient: IDriftClient; usersAccounts = new Map(); subscription: PollingSubscription | WebsocketSubscription | grpcSubscription; commitment: Commitment; diff --git a/sdk/src/orderSubscriber/PollingSubscription.ts b/sdk/src/orderSubscriber/PollingSubscription.ts index cc6c8a0cf2..e19dd67764 100644 --- a/sdk/src/orderSubscriber/PollingSubscription.ts +++ b/sdk/src/orderSubscriber/PollingSubscription.ts @@ -1,7 +1,7 @@ -import { OrderSubscriber } from './OrderSubscriber'; +import { IOrderSubscriber } from './types'; export class PollingSubscription { - private orderSubscriber: OrderSubscriber; + private orderSubscriber: IOrderSubscriber; private frequency: number; intervalId?: ReturnType; @@ -10,7 +10,7 @@ export class PollingSubscription { orderSubscriber, frequency, }: { - orderSubscriber: OrderSubscriber; + orderSubscriber: IOrderSubscriber; frequency: number; }) { this.orderSubscriber = orderSubscriber; diff --git a/sdk/src/orderSubscriber/WebsocketSubscription.ts b/sdk/src/orderSubscriber/WebsocketSubscription.ts index cb305eaa77..769fc474bf 100644 --- a/sdk/src/orderSubscriber/WebsocketSubscription.ts +++ b/sdk/src/orderSubscriber/WebsocketSubscription.ts @@ -1,12 +1,12 @@ -import { OrderSubscriber } from './OrderSubscriber'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; +import { IOrderSubscriber } from './types'; export class WebsocketSubscription { - private orderSubscriber: OrderSubscriber; + private orderSubscriber: IOrderSubscriber; private commitment: Commitment; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; @@ -25,7 +25,7 @@ export class WebsocketSubscription { resyncIntervalMs, decoded = true, }: { - orderSubscriber: OrderSubscriber; + orderSubscriber: IOrderSubscriber; commitment: Commitment; skipInitialLoad?: boolean; resubOpts?: ResubOpts; diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index b681a34ce3..da4d7d4e6d 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -1,13 +1,13 @@ import { Context, PublicKey } from '@solana/web3.js'; import { Buffer } from 'buffer'; import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; -import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; import { getUserFilter, getNonIdleUserFilter } from '../memcmp'; +import { IOrderSubscriber } from './types'; export class grpcSubscription { - private orderSubscriber: OrderSubscriber; + private orderSubscriber: IOrderSubscriber; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; private resyncIntervalMs?: number; @@ -28,7 +28,7 @@ export class grpcSubscription { decoded = true, }: { grpcConfigs: GrpcConfigs; - orderSubscriber: OrderSubscriber; + orderSubscriber: IOrderSubscriber; skipInitialLoad?: boolean; resubOpts?: ResubOpts; resyncIntervalMs?: number; diff --git a/sdk/src/orderSubscriber/types.ts b/sdk/src/orderSubscriber/types.ts index 4e441e0cbf..0315c5b595 100644 --- a/sdk/src/orderSubscriber/types.ts +++ b/sdk/src/orderSubscriber/types.ts @@ -1,10 +1,14 @@ import { Commitment, PublicKey } from '@solana/web3.js'; import { Order, UserAccount } from '../types'; -import { DriftClient } from '../driftClient'; +import { IDriftClient } from '../driftClient/types'; import { GrpcConfigs } from '../accounts/types'; +import { Buffer } from 'buffer'; +import StrictEventEmitter from 'strict-event-emitter-types'; +import { EventEmitter } from 'events'; +import { ProtectMakerParamsMap, IDLOB } from '../dlob/types'; export type OrderSubscriberConfig = { - driftClient: DriftClient; + driftClient: IDriftClient; subscriptionConfig: | { type: 'polling'; @@ -53,3 +57,40 @@ export interface OrderSubscriberEvents { dataType: 'raw' | 'decoded' | 'buffer' ) => void; } + +export interface IOrderSubscriber { + driftClient: IDriftClient; + usersAccounts: Map; + commitment: Commitment; + eventEmitter: StrictEventEmitter; + fetchPromise?: Promise; + fetchPromiseResolver: () => void; + mostRecentSlot: number; + decodeFn: (name: string, data: Buffer) => UserAccount; + decodeData?: boolean; + fetchAllNonIdleUsers?: boolean; + + subscribe(): Promise; + + fetch(): Promise; + + tryUpdateUserAccount( + key: string, + dataType: 'raw' | 'decoded' | 'buffer', + data: string[] | UserAccount | Buffer, + slot: number + ): void; + + getDLOB( + slot: number, + protectedMakerParamsMap?: ProtectMakerParamsMap + ): Promise; + + getSlot(): number; + + addPubkey(userAccountPublicKey: PublicKey): Promise; + + mustGetUserAccount(key: string): Promise; + + unsubscribe(): Promise; +} diff --git a/sdk/src/swift/swiftOrderSubscriber.ts b/sdk/src/swift/swiftOrderSubscriber.ts index dbc6ff28bd..25bd7ea887 100644 --- a/sdk/src/swift/swiftOrderSubscriber.ts +++ b/sdk/src/swift/swiftOrderSubscriber.ts @@ -2,7 +2,7 @@ import { DevnetPerpMarkets, MainnetPerpMarkets, } from '../constants/perpMarkets'; -import { DriftClient } from '../driftClient'; +import { IDriftClient } from '../driftClient/types'; import { DriftEnv } from '../config/types'; import { getUserAccountPublicKey, @@ -28,7 +28,7 @@ export interface AccountGetter { } export type SwiftOrderSubscriberConfig = { - driftClient: DriftClient; + driftClient: IDriftClient; userAccountGetter?: AccountGetter; driftEnv: DriftEnv; endpoint?: string; @@ -45,7 +45,7 @@ export class SwiftOrderSubscriber { private heartbeatTimeout: ReturnType | null = null; private readonly heartbeatIntervalMs = 60000; private ws: WebSocket | null = null; - private driftClient: DriftClient; + private driftClient: IDriftClient; public userAccountGetter?: AccountGetter; // In practice, this for now is just an OrderSubscriber or a UserMap public onOrder: ( orderMessageRaw: any, diff --git a/sdk/src/user.ts b/sdk/src/user/index.ts similarity index 98% rename from sdk/src/user.ts rename to sdk/src/user/index.ts index 8e3f80769a..0b8b6323db 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user/index.ts @@ -1,7 +1,7 @@ import { PublicKey } from '@solana/web3.js'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; -import { DriftClient } from './driftClient'; +import { DriftClient } from '../driftClient'; import { HealthComponent, HealthComponents, @@ -14,12 +14,12 @@ import { UserAccount, UserStatus, UserStatsAccount, -} from './types'; +} from '../types'; import { calculateEntryPrice, calculateUnsettledFundingPnl, positionIsAvailable, -} from './math/position'; +} from '../math/position'; import { AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_EXP, @@ -40,41 +40,43 @@ import { TWO, ZERO, FUEL_START_TS, -} from './constants/numericConstants'; +} from '../constants/numericConstants'; import { DataAndSlot, UserAccountEvents, UserAccountSubscriber, -} from './accounts/types'; -import { BigNum } from './factory/bigNum'; +} from '../accounts/types'; +import { BigNum } from '../factory/bigNum'; import { BN } from '@coral-xyz/anchor'; -import { calculateBaseAssetValue, calculatePositionPNL } from './math/position'; +import { + calculateBaseAssetValue, + calculatePositionPNL, +} from '../math/position'; import { calculateMarketMarginRatio, calculateReservePrice, calculateUnrealizedAssetWeight, -} from './math/market'; +} from '../math/market'; import { calculatePerpLiabilityValue, calculateWorstCasePerpLiabilityValue, -} from './math/margin'; -import { calculateSpotMarketMarginRatio } from './math/spotMarket'; -import { divCeil, sigNum } from './math/utils'; +} from '../math/margin'; +import { calculateSpotMarketMarginRatio } from '../math/spotMarket'; +import { divCeil, sigNum } from '../math/utils'; import { getBalance, getSignedTokenAmount, getStrictTokenValue, getTokenValue, -} from './math/spotBalance'; -import { getUser30dRollingVolumeEstimate } from './math/trade'; +} from '../math/spotBalance'; +import { getUser30dRollingVolumeEstimate } from '../math/trade'; import { MarketType, PositionDirection, SpotBalanceType, SpotMarketAccount, -} from './types'; -import { standardizeBaseAssetAmount } from './math/orders'; -import { UserStats } from './userStats'; +} from '../types'; +import { standardizeBaseAssetAmount } from '../math/utils'; import { calculateAssetWeight, calculateLiabilityWeight, @@ -82,34 +84,39 @@ import { getSpotAssetValue, getSpotLiabilityValue, getTokenAmount, -} from './math/spotBalance'; -import { calculateMarketOpenBidAsk } from './math/amm'; +} from '../math/spotBalance'; +import { calculateMarketOpenBidAsk } from '../math/amm'; import { calculateBaseAssetValueWithOracle, calculateCollateralDepositRequiredForTrade, calculateMarginUSDCRequiredForTrade, calculateWorstCaseBaseAssetAmount, -} from './math/margin'; -import { OraclePriceData } from './oracles/types'; -import { UserConfig } from './userConfig'; -import { PollingUserAccountSubscriber } from './accounts/userAccount/pollingUserAccountSubscriber'; -import { WebSocketUserAccountSubscriber } from './accounts/userAccount/webSocketUserAccountSubscriber'; +} from '../math/margin'; +import { OraclePriceData } from '../oracles/types'; +import { UserConfig } from '../userConfig'; +import { PollingUserAccountSubscriber } from '../accounts/userAccount/pollingUserAccountSubscriber'; +import { WebSocketUserAccountSubscriber } from '../accounts/userAccount/webSocketUserAccountSubscriber'; import { calculateWeightedTokenValue, getWorstCaseTokenAmounts, isSpotPositionAvailable, -} from './math/spotPosition'; +} from '../math/spotPosition'; import { calculateLiveOracleTwap, getMultipleBetweenOracleSources, -} from './math/oracles'; -import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers'; -import { StrictOraclePrice } from './oracles/strictOraclePrice'; +} from '../math/oracles'; +import { + getPerpMarketTierNumber, + getSpotMarketTierNumber, +} from '../math/tiers'; +import { StrictOraclePrice } from '../oracles/strictOraclePrice'; -import { calculateSpotFuelBonus, calculatePerpFuelBonus } from './math/fuel'; -import { grpcUserAccountSubscriber } from './accounts/userAccount/grpcUserAccountSubscriber'; +import { calculateSpotFuelBonus, calculatePerpFuelBonus } from '../math/fuel'; +import { grpcUserAccountSubscriber } from '../accounts/userAccount/grpcUserAccountSubscriber'; +import { IUserStats } from '../userStats/types'; +import { IUser } from './types'; -export class User { +export class User implements IUser { driftClient: DriftClient; userAccountPublicKey: PublicKey; accountSubscriber: UserAccountSubscriber; @@ -935,7 +942,7 @@ export class User { now: BN, includeSettled = true, includeUnsettled = true, - givenUserStats?: UserStats + givenUserStats?: IUserStats ): { depositFuel: BN; borrowFuel: BN; diff --git a/sdk/src/user/types.ts b/sdk/src/user/types.ts new file mode 100644 index 0000000000..5cd7411f1a --- /dev/null +++ b/sdk/src/user/types.ts @@ -0,0 +1,730 @@ +import { PublicKey } from '@solana/web3.js'; +import { EventEmitter } from 'events'; +import StrictEventEmitter from 'strict-event-emitter-types'; +import { + HealthComponent, + HealthComponents, + MarginCategory, + Order, + PerpPosition, + SpotPosition, + UserAccount, + UserStatus, + MarketType, + PositionDirection, + SpotMarketAccount, + PerpMarketAccount, + FeeTier, +} from '../types'; +import { + DataAndSlot, + UserAccountEvents, + UserAccountSubscriber, +} from '../accounts/types'; +import { BN } from '@coral-xyz/anchor'; +import { OraclePriceData } from '../oracles/types'; +import { StrictOraclePrice } from '../oracles/strictOraclePrice'; +import { IUserStats } from '../userStats/types'; + +export interface IUser { + userAccountPublicKey: PublicKey; + accountSubscriber: UserAccountSubscriber; + eventEmitter: StrictEventEmitter; + isSubscribed: boolean; + + /** + * Subscribe to User state accounts + * @returns SusbcriptionSuccess result + */ + subscribe(userAccount?: UserAccount): Promise; + + /** + * Forces the accountSubscriber to fetch account updates from rpc + */ + fetchAccounts(): Promise; + + unsubscribe(): Promise; + + getUserAccount(): UserAccount; + + forceGetUserAccount(): Promise; + + getUserAccountAndSlot(): DataAndSlot | undefined; + + getPerpPositionForUserAccount( + userAccount: UserAccount, + marketIndex: number + ): PerpPosition | undefined; + + /** + * Gets the user's current position for a given perp market. If the user has no position returns undefined + * @param marketIndex + * @returns userPerpPosition + */ + getPerpPosition(marketIndex: number): PerpPosition | undefined; + + getPerpPositionAndSlot( + marketIndex: number + ): DataAndSlot; + + getSpotPositionForUserAccount( + userAccount: UserAccount, + marketIndex: number + ): SpotPosition | undefined; + + /** + * Gets the user's current position for a given spot market. If the user has no position returns undefined + * @param marketIndex + * @returns userSpotPosition + */ + getSpotPosition(marketIndex: number): SpotPosition | undefined; + + getSpotPositionAndSlot( + marketIndex: number + ): DataAndSlot; + + getEmptySpotPosition(marketIndex: number): SpotPosition; + + /** + * Returns the token amount for a given market. The spot market precision is based on the token mint decimals. + * Positive if it is a deposit, negative if it is a borrow. + * + * @param marketIndex + */ + getTokenAmount(marketIndex: number): BN; + + getEmptyPosition(marketIndex: number): PerpPosition; + + getClonedPosition(position: PerpPosition): PerpPosition; + + getOrderForUserAccount( + userAccount: UserAccount, + orderId: number + ): Order | undefined; + + /** + * @param orderId + * @returns Order + */ + getOrder(orderId: number): Order | undefined; + + getOrderAndSlot(orderId: number): DataAndSlot; + + getOrderByUserIdForUserAccount( + userAccount: UserAccount, + userOrderId: number + ): Order | undefined; + + /** + * @param userOrderId + * @returns Order + */ + getOrderByUserOrderId(userOrderId: number): Order | undefined; + + getOrderByUserOrderIdAndSlot( + userOrderId: number + ): DataAndSlot; + + getOpenOrdersForUserAccount(userAccount?: UserAccount): Order[]; + + getOpenOrders(): Order[]; + + getOpenOrdersAndSlot(): DataAndSlot; + + getUserAccountPublicKey(): PublicKey; + + exists(): Promise; + + /** + * calculates the total open bids/asks in a perp market (including lps) + * @returns : open bids + * @returns : open asks + */ + getPerpBidAsks(marketIndex: number): [BN, BN]; + + /** + * calculates the open bids and asks for an lp + * optionally pass in lpShares to see what bid/asks a user *would* take on + * @returns : lp open bids + * @returns : lp open asks + */ + getLPBidAsks(marketIndex: number, lpShares?: BN): [BN, BN]; + + /** + * calculates the market position if the lp position was settled + * @returns : the settled userPosition + * @returns : the dust base asset amount (ie, < stepsize) + * @returns : pnl from settle + */ + getPerpPositionWithLPSettle( + marketIndex: number, + originalPosition?: PerpPosition, + burnLpShares?: boolean, + includeRemainderInBaseAmount?: boolean + ): [PerpPosition, BN, BN]; + + /** + * calculates Buying Power = free collateral / initial margin ratio + * @returns : Precision QUOTE_PRECISION + */ + getPerpBuyingPower( + marketIndex: number, + collateralBuffer?: BN, + enterHighLeverageMode?: boolean + ): BN; + + getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount( + marketIndex: number, + freeCollateral: BN, + baseAssetAmount: BN, + enterHighLeverageMode?: boolean + ): BN; + + /** + * calculates Free Collateral = Total collateral - margin requirement + * @returns : Precision QUOTE_PRECISION + */ + getFreeCollateral( + marginCategory?: MarginCategory, + enterHighLeverageMode?: boolean + ): BN; + + /** + * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION + */ + getMarginRequirement( + marginCategory: MarginCategory, + liquidationBuffer?: BN, + strict?: boolean, + includeOpenOrders?: boolean, + enteringHighLeverage?: boolean + ): BN; + + /** + * @returns The initial margin requirement in USDC. : QUOTE_PRECISION + */ + getInitialMarginRequirement(enterHighLeverageMode?: boolean): BN; + + /** + * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION + */ + getMaintenanceMarginRequirement(liquidationBuffer?: BN): BN; + + getActivePerpPositionsForUserAccount( + userAccount: UserAccount + ): PerpPosition[]; + + getActivePerpPositions(): PerpPosition[]; + + getActivePerpPositionsAndSlot(): DataAndSlot; + + getActiveSpotPositionsForUserAccount( + userAccount: UserAccount + ): SpotPosition[]; + + getActiveSpotPositions(): SpotPosition[]; + + getActiveSpotPositionsAndSlot(): DataAndSlot; + + /** + * calculates unrealized position price pnl + * @returns : Precision QUOTE_PRECISION + */ + getUnrealizedPNL( + withFunding?: boolean, + marketIndex?: number, + withWeightMarginCategory?: MarginCategory, + strict?: boolean, + liquidationBuffer?: BN + ): BN; + + /** + * calculates unrealized funding payment pnl + * @returns : Precision QUOTE_PRECISION + */ + getUnrealizedFundingPNL(marketIndex?: number): BN; + + getFuelBonus( + now: BN, + includeSettled?: boolean, + includeUnsettled?: boolean, + givenUserStats?: IUserStats + ): { + depositFuel: BN; + borrowFuel: BN; + positionFuel: BN; + takerFuel: BN; + makerFuel: BN; + insuranceFuel: BN; + }; + + getSpotMarketAssetAndLiabilityValue( + marketIndex?: number, + marginCategory?: MarginCategory, + liquidationBuffer?: BN, + includeOpenOrders?: boolean, + strict?: boolean, + now?: BN + ): { totalAssetValue: BN; totalLiabilityValue: BN }; + + getSpotMarketLiabilityValue( + marketIndex?: number, + marginCategory?: MarginCategory, + liquidationBuffer?: BN, + includeOpenOrders?: boolean, + strict?: boolean, + now?: BN + ): BN; + + getSpotLiabilityValue( + tokenAmount: BN, + strictOraclePrice: StrictOraclePrice, + spotMarketAccount: SpotMarketAccount, + marginCategory?: MarginCategory, + liquidationBuffer?: BN + ): BN; + + getSpotMarketAssetValue( + marketIndex?: number, + marginCategory?: MarginCategory, + includeOpenOrders?: boolean, + strict?: boolean, + now?: BN + ): BN; + + getSpotAssetValue( + tokenAmount: BN, + strictOraclePrice: StrictOraclePrice, + spotMarketAccount: SpotMarketAccount, + marginCategory?: MarginCategory + ): BN; + + getSpotPositionValue( + marketIndex: number, + marginCategory?: MarginCategory, + includeOpenOrders?: boolean, + strict?: boolean, + now?: BN + ): BN; + + getNetSpotMarketValue(withWeightMarginCategory?: MarginCategory): BN; + + /** + * calculates TotalCollateral: collateral + unrealized pnl + * @returns : Precision QUOTE_PRECISION + */ + getTotalCollateral( + marginCategory?: MarginCategory, + strict?: boolean, + includeOpenOrders?: boolean, + liquidationBuffer?: BN + ): BN; + + getLiquidationBuffer(): BN | undefined; + + /** + * calculates User Health by comparing total collateral and maint. margin requirement + * @returns : number (value from [0, 100]) + */ + getHealth(): number; + + calculateWeightedPerpPositionLiability( + perpPosition: PerpPosition, + marginCategory?: MarginCategory, + liquidationBuffer?: BN, + includeOpenOrders?: boolean, + strict?: boolean, + enteringHighLeverage?: boolean + ): BN; + + /** + * calculates position value of a single perp market in margin system + * @returns : Precision QUOTE_PRECISION + */ + getPerpMarketLiabilityValue( + marketIndex: number, + marginCategory?: MarginCategory, + liquidationBuffer?: BN, + includeOpenOrders?: boolean, + strict?: boolean + ): BN; + + /** + * calculates sum of position value across all positions in margin system + * @returns : Precision QUOTE_PRECISION + */ + getTotalPerpPositionLiability( + marginCategory?: MarginCategory, + liquidationBuffer?: BN, + includeOpenOrders?: boolean, + strict?: boolean, + enteringHighLeverage?: boolean + ): BN; + + /** + * calculates position value based on oracle + * @returns : Precision QUOTE_PRECISION + */ + getPerpPositionValue( + marketIndex: number, + oraclePriceData: OraclePriceData, + includeOpenOrders?: boolean + ): BN; + + /** + * calculates position liabiltiy value in margin system + * @returns : Precision QUOTE_PRECISION + */ + getPerpLiabilityValue( + marketIndex: number, + oraclePriceData: OraclePriceData, + includeOpenOrders?: boolean + ): BN; + + getPositionSide( + currentPosition: Pick + ): PositionDirection | undefined; + + /** + * calculates average exit price (optionally for closing up to 100% of position) + * @returns : Precision PRICE_PRECISION + */ + getPositionEstimatedExitPriceAndPnl( + position: PerpPosition, + amountToClose?: BN, + useAMMClose?: boolean + ): [BN, BN]; + + /** + * calculates current user leverage which is (total liability size) / (net asset value) + * @returns : Precision TEN_THOUSAND + */ + getLeverage(includeOpenOrders?: boolean): BN; + + calculateLeverageFromComponents({ + perpLiabilityValue, + perpPnl, + spotAssetValue, + spotLiabilityValue, + }: { + perpLiabilityValue: BN; + perpPnl: BN; + spotAssetValue: BN; + spotLiabilityValue: BN; + }): BN; + + getLeverageComponents( + includeOpenOrders?: boolean, + marginCategory?: MarginCategory + ): { + perpLiabilityValue: BN; + perpPnl: BN; + spotAssetValue: BN; + spotLiabilityValue: BN; + }; + + isDustDepositPosition(spotMarketAccount: SpotMarketAccount): boolean; + + getSpotMarketAccountsWithDustPosition(): SpotMarketAccount[]; + + getTotalLiabilityValue(marginCategory?: MarginCategory): BN; + + getTotalAssetValue(marginCategory?: MarginCategory): BN; + + getNetUsdValue(): BN; + + /** + * Calculates the all time P&L of the user. + * + * Net withdraws + Net spot market value + Net unrealized P&L - + */ + getTotalAllTimePnl(): BN; + + /** + * calculates max allowable leverage exceeding hitting requirement category + * for large sizes where imf factor activates, result is a lower bound + * @param marginCategory {Initial, Maintenance} + * @param isLp if calculating max leveraging for adding lp, need to add buffer + * @param enterHighLeverageMode can pass this as true to calculate max leverage if the user was to enter high leverage mode + * @returns : Precision TEN_THOUSAND + */ + getMaxLeverageForPerp( + perpMarketIndex: number, + _marginCategory?: MarginCategory, + isLp?: boolean, + enterHighLeverageMode?: boolean + ): BN; + + /** + * calculates max allowable leverage exceeding hitting requirement category + * @param spotMarketIndex + * @param direction + * @returns : Precision TEN_THOUSAND + */ + getMaxLeverageForSpot( + spotMarketIndex: number, + direction: PositionDirection + ): BN; + + /** + * calculates margin ratio: 1 / leverage + * @returns : Precision TEN_THOUSAND + */ + getMarginRatio(): BN; + + canBeLiquidated(): { + canBeLiquidated: boolean; + marginRequirement: BN; + totalCollateral: BN; + }; + + isBeingLiquidated(): boolean; + + hasStatus(status: UserStatus): boolean; + + isBankrupt(): boolean; + + isHighLeverageMode(marginCategory: MarginCategory): boolean; + + /** + * Checks if any user position cumulative funding differs from respective market cumulative funding + * @returns + */ + needsToSettleFundingPayment(): boolean; + + /** + * Calculate the liquidation price of a spot position + * @param marketIndex + * @returns Precision : PRICE_PRECISION + */ + spotLiquidationPrice(marketIndex: number, positionBaseSizeChange?: BN): BN; + + /** + * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade + * @param marketIndex + * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^9 + * @param estimatedEntryPrice + * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking + * @param includeOpenOrders + * @param offsetCollateral // allows calculating the liquidation price after this offset collateral is added to the user's account (e.g. : what will the liquidation price be for this position AFTER I deposit $x worth of collateral) + * @returns Precision : PRICE_PRECISION + */ + liquidationPrice( + marketIndex: number, + positionBaseSizeChange?: BN, + estimatedEntryPrice?: BN, + marginCategory?: MarginCategory, + includeOpenOrders?: boolean, + offsetCollateral?: BN, + enteringHighLeverage?: boolean + ): BN; + + calculateEntriesEffectOnFreeCollateral( + market: PerpMarketAccount, + oraclePrice: BN, + perpPosition: PerpPosition, + positionBaseSizeChange: BN, + estimatedEntryPrice: BN, + includeOpenOrders: boolean, + enteringHighLeverage?: boolean, + marginCategory?: MarginCategory + ): BN; + + calculateFreeCollateralDeltaForPerp( + market: PerpMarketAccount, + perpPosition: PerpPosition, + positionBaseSizeChange: BN, + oraclePrice: BN, + marginCategory?: MarginCategory, + includeOpenOrders?: boolean, + enteringHighLeverage?: boolean + ): BN | undefined; + + calculateFreeCollateralDeltaForSpot( + market: SpotMarketAccount, + signedTokenAmount: BN, + marginCategory?: MarginCategory + ): BN; + + /** + * Calculates the estimated liquidation price for a position after closing a quote amount of the position. + * @param positionMarketIndex + * @param closeQuoteAmount + * @returns : Precision PRICE_PRECISION + */ + liquidationPriceAfterClose( + positionMarketIndex: number, + closeQuoteAmount: BN, + estimatedEntryPrice?: BN + ): BN; + + getMarginUSDCRequiredForTrade( + targetMarketIndex: number, + baseSize: BN, + estEntryPrice?: BN + ): BN; + + getCollateralDepositRequiredForTrade( + targetMarketIndex: number, + baseSize: BN, + collateralIndex: number + ): BN; + + /** + * Separates the max trade size into two parts: + * - tradeSize: The maximum trade size for target direction + * - oppositeSideTradeSize: the trade size for closing the opposite direction + * @param targetMarketIndex + * @param tradeSide + * @param isLp + * @returns { tradeSize: BN, oppositeSideTradeSize: BN} : Precision QUOTE_PRECISION + */ + getMaxTradeSizeUSDCForPerp( + targetMarketIndex: number, + tradeSide: PositionDirection, + isLp?: boolean, + enterHighLeverageMode?: boolean + ): { tradeSize: BN; oppositeSideTradeSize: BN }; + + /** + * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc. + * + * @param targetMarketIndex + * @param direction + * @param currentQuoteAssetValue + * @param currentSpotMarketNetValue + * @returns tradeSizeAllowed : Precision QUOTE_PRECISION + */ + getMaxTradeSizeUSDCForSpot( + targetMarketIndex: number, + direction: PositionDirection, + currentQuoteAssetValue?: BN, + currentSpotMarketNetValue?: BN + ): BN; + + /** + * Calculates the max amount of token that can be swapped from inMarket to outMarket + * Assumes swap happens at oracle price + * + * @param inMarketIndex + * @param outMarketIndex + * @param calculateSwap function to similate in to out swa + * @param iterationLimit how long to run appromixation before erroring out + */ + getMaxSwapAmount({ + inMarketIndex, + outMarketIndex, + calculateSwap, + iterationLimit, + }: { + inMarketIndex: number; + outMarketIndex: number; + calculateSwap?: (inAmount: BN) => BN; + iterationLimit?: number; + }): { inAmount: BN; outAmount: BN; leverage: BN }; + + cloneAndUpdateSpotPosition( + position: SpotPosition, + tokenAmount: BN, + market: SpotMarketAccount + ): SpotPosition; + + calculateSpotPositionFreeCollateralContribution( + spotPosition: SpotPosition, + strictOraclePrice: StrictOraclePrice + ): BN; + + calculateSpotPositionLeverageContribution( + spotPosition: SpotPosition, + strictOraclePrice: StrictOraclePrice + ): { + totalAssetValue: BN; + totalLiabilityValue: BN; + }; + + /** + * Estimates what the user leverage will be after swap + * @param inMarketIndex + * @param outMarketIndex + * @param inAmount + * @param outAmount + */ + accountLeverageAfterSwap({ + inMarketIndex, + outMarketIndex, + inAmount, + outAmount, + }: { + inMarketIndex: number; + outMarketIndex: number; + inAmount: BN; + outAmount: BN; + }): BN; + + /** + * Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position + * @param targetMarketIndex + * @param: targetMarketType + * @param tradeQuoteAmount + * @param tradeSide + * @param includeOpenOrders + * @returns leverageRatio : Precision TEN_THOUSAND + */ + accountLeverageRatioAfterTrade( + targetMarketIndex: number, + targetMarketType: MarketType, + tradeQuoteAmount: BN, + tradeSide: PositionDirection, + includeOpenOrders?: boolean + ): BN; + + getUserFeeTier(marketType: MarketType, now?: BN): FeeTier; + + /** + * Calculates how much perp fee will be taken for a given sized trade + * @param quoteAmount + * @returns feeForQuote : Precision QUOTE_PRECISION + */ + calculateFeeForQuoteAmount( + quoteAmount: BN, + marketIndex?: number, + enteringHighLeverageMode?: boolean + ): BN; + + /** + * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true, + * it will return the max withdrawal amount without opening a liability for the user + * @param marketIndex + * @returns withdrawalLimit : Precision is the token precision for the chosen SpotMarket + */ + getWithdrawalLimit(marketIndex: number, reduceOnly?: boolean): BN; + + canBypassWithdrawLimits(marketIndex: number): { + canBypass: boolean; + netDeposits: BN; + depositAmount: BN; + maxDepositAmount: BN; + }; + + canMakeIdle(slot: BN): boolean; + + getSafestTiers(): { perpTier: number; spotTier: number }; + + getPerpPositionHealth({ + marginCategory, + perpPosition, + oraclePriceData, + quoteOraclePriceData, + }: { + marginCategory: MarginCategory; + perpPosition: PerpPosition; + oraclePriceData?: OraclePriceData; + quoteOraclePriceData?: OraclePriceData; + }): HealthComponent; + + getHealthComponents({ + marginCategory, + }: { + marginCategory: MarginCategory; + }): HealthComponents; +} diff --git a/sdk/src/userMap/PollingSubscription.ts b/sdk/src/userMap/PollingSubscription.ts index 45d01b4ed3..1e53623e0f 100644 --- a/sdk/src/userMap/PollingSubscription.ts +++ b/sdk/src/userMap/PollingSubscription.ts @@ -1,7 +1,7 @@ -import { UserMapInterface } from './types'; +import { IUserMap } from './types'; export class PollingSubscription { - private userMap: UserMapInterface; + private userMap: IUserMap; private frequency: number; private skipInitialLoad: boolean; @@ -12,7 +12,7 @@ export class PollingSubscription { frequency, skipInitialLoad = false, }: { - userMap: UserMapInterface; + userMap: IUserMap; frequency: number; skipInitialLoad?: boolean; includeIdle?: boolean; diff --git a/sdk/src/userMap/WebsocketSubscription.ts b/sdk/src/userMap/WebsocketSubscription.ts index 41fef10d5a..19832eb8e4 100644 --- a/sdk/src/userMap/WebsocketSubscription.ts +++ b/sdk/src/userMap/WebsocketSubscription.ts @@ -3,10 +3,10 @@ import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/we import { UserAccount } from '../types'; import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; -import { UserMapInterface } from './types'; +import { IUserMap } from './types'; export class WebsocketSubscription { - private userMap: UserMapInterface; + private userMap: IUserMap; private commitment: Commitment; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; @@ -25,7 +25,7 @@ export class WebsocketSubscription { decodeFn, additionalFilters = undefined, }: { - userMap: UserMapInterface; + userMap: IUserMap; commitment: Commitment; skipInitialLoad?: boolean; resubOpts?: ResubOpts; diff --git a/sdk/src/userMap/grpcSubscription.ts b/sdk/src/userMap/grpcSubscription.ts index dcedabafeb..d1df8b63f3 100644 --- a/sdk/src/userMap/grpcSubscription.ts +++ b/sdk/src/userMap/grpcSubscription.ts @@ -4,11 +4,11 @@ import { UserAccount } from '../types'; import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; -import { UserMapInterface } from './types'; +import { IUserMap } from './types'; export class grpcSubscription { private grpcConfigs: GrpcConfigs; - private userMap: UserMapInterface; + private userMap: IUserMap; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; private includeIdle?: boolean; @@ -27,7 +27,7 @@ export class grpcSubscription { additionalFilters = undefined, }: { grpcConfigs: GrpcConfigs; - userMap: UserMapInterface; + userMap: IUserMap; skipInitialLoad?: boolean; resubOpts?: ResubOpts; includeIdle?: boolean; diff --git a/sdk/src/userMap/types.ts b/sdk/src/userMap/types.ts index 77f5cdc1be..476d5c86ef 100644 --- a/sdk/src/userMap/types.ts +++ b/sdk/src/userMap/types.ts @@ -1,47 +1,106 @@ +import { User } from '../user'; +import { DriftClient } from '../driftClient'; +import { UserAccount, OrderRecord } from '../types'; +import { WrappedEvent } from '../events/types'; import { UserSubscriptionConfig } from '../userConfig'; +import { DataAndSlot } from '../accounts/types'; +import { IDLOB, ProtectMakerParamsMap } from '../dlob/types'; +import { PublicKey } from '@solana/web3.js'; +import { UserAccountFilterCriteria as UserFilterCriteria } from './userMapConfig'; +import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { StrictEventEmitter } from 'strict-event-emitter-types'; -import { User } from '../user'; import { UserEvents } from './events'; -import { OrderRecord, UserAccount } from '../types'; -import { PublicKey } from '@solana/web3.js'; -import { DataAndSlot } from '../accounts/types'; -import { DriftClient } from '../driftClient'; -export interface UserMapInterface { - eventEmitter: StrictEventEmitter; +export interface IUserMap { driftClient: DriftClient; + eventEmitter: StrictEventEmitter; + subscribe(): Promise; - unsubscribe(): Promise; + addPubkey( userAccountPublicKey: PublicKey, userAccount?: UserAccount, slot?: number, accountSubscription?: UserSubscriptionConfig ): Promise; + has(key: string): boolean; + + /** + * gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned + * @param key userAccountPublicKey to get User for + * @returns user User | undefined + */ get(key: string): User | undefined; + getWithSlot(key: string): DataAndSlot | undefined; + + /** + * gets the User for a particular userAccountPublicKey, if no User exists, new one is created + * @param key userAccountPublicKey to get User for + * @returns User + */ mustGet( key: string, accountSubscription?: UserSubscriptionConfig ): Promise; + mustGetWithSlot( key: string, accountSubscription?: UserSubscriptionConfig ): Promise>; + + mustGetUserAccount(key: string): Promise; + + /** + * gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned + * @param key userAccountPublicKey to get User for + * @returns authority PublicKey | undefined + */ getUserAuthority(key: string): PublicKey | undefined; + + /** + * implements the {@link DLOBSource} interface + * create a DLOB from all the subscribed users + * @param slot + */ + getDLOB( + slot: number, + protectedMakerParamsMap?: ProtectMakerParamsMap + ): Promise; + updateWithOrderRecord(record: OrderRecord): Promise; + + updateWithEventRecord(record: WrappedEvent): Promise; + values(): IterableIterator; + valuesWithSlot(): IterableIterator>; + entries(): IterableIterator<[string, User]>; + entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; + + size(): number; + + /** + * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria + * @param filterCriteria: Users must meet these criteria to be included + * @returns + */ + getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[]; + sync(): Promise; + + unsubscribe(): Promise; + updateUserAccount( key: string, userAccount: UserAccount, slot: number ): Promise; + updateLatestSlot(slot: number): void; + getSlot(): number; } diff --git a/sdk/src/userMap/userMap.ts b/sdk/src/userMap/userMap.ts index 3f76690475..98e2bfdaa4 100644 --- a/sdk/src/userMap/userMap.ts +++ b/sdk/src/userMap/userMap.ts @@ -46,11 +46,11 @@ import { grpcSubscription } from './grpcSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserEvents } from './events'; -import { UserMapInterface } from './types'; +import { IUserMap } from './types'; const MAX_USER_ACCOUNT_SIZE_BYTES = 4376; -export class UserMap implements UserMapInterface { +export class UserMap implements IUserMap { private userMap = new Map>(); driftClient: DriftClient; eventEmitter: StrictEventEmitter; diff --git a/sdk/src/userStats.ts b/sdk/src/userStats/index.ts similarity index 83% rename from sdk/src/userStats.ts rename to sdk/src/userStats/index.ts index 017fd87db4..264d86d79c 100644 --- a/sdk/src/userStats.ts +++ b/sdk/src/userStats/index.ts @@ -1,26 +1,27 @@ -import { DriftClient } from './driftClient'; import { PublicKey } from '@solana/web3.js'; -import { DataAndSlot, UserStatsAccountSubscriber } from './accounts/types'; -import { UserStatsConfig } from './userStatsConfig'; -import { PollingUserStatsAccountSubscriber } from './accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; -import { WebSocketUserStatsAccountSubscriber } from './accounts/userStatsAccount/webSocketUserStatsAccountSubsriber'; -import { ReferrerInfo, SpotMarketAccount, UserStatsAccount } from './types'; +import { DataAndSlot, UserStatsAccountSubscriber } from '../accounts/types'; +import { UserStatsConfig } from '../userStatsConfig'; +import { PollingUserStatsAccountSubscriber } from '../accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; +import { WebSocketUserStatsAccountSubscriber } from '../accounts/userStatsAccount/webSocketUserStatsAccountSubsriber'; +import { ReferrerInfo, SpotMarketAccount, UserStatsAccount } from '../types'; import { getUserAccountPublicKeySync, getUserStatsAccountPublicKey, -} from './addresses/pda'; -import { grpcUserStatsAccountSubscriber } from './accounts/userStatsAccount/grpcUserStatsAccountSubscriber'; -import { FUEL_START_TS } from './constants/numericConstants'; -import { ZERO } from './constants/numericConstants'; +} from '../addresses/pda'; +import { grpcUserStatsAccountSubscriber } from '../accounts/userStatsAccount/grpcUserStatsAccountSubscriber'; +import { FUEL_START_TS } from '../constants/numericConstants'; +import { ZERO } from '../constants/numericConstants'; import { GOV_SPOT_MARKET_INDEX, QUOTE_SPOT_MARKET_INDEX, -} from './constants/numericConstants'; +} from '../constants/numericConstants'; import { BN } from '@coral-xyz/anchor'; -import { calculateInsuranceFuelBonus } from './math/fuel'; +import { calculateInsuranceFuelBonus } from '../math/fuel'; +import { IDriftClient } from '../driftClient/types'; +import { IUserStats } from './types'; -export class UserStats { - driftClient: DriftClient; +export class UserStats implements IUserStats { + driftClient: IDriftClient; userStatsAccountPublicKey: PublicKey; accountSubscriber: UserStatsAccountSubscriber; isSubscribed: boolean; diff --git a/sdk/src/userStats/types.ts b/sdk/src/userStats/types.ts new file mode 100644 index 0000000000..3b8e9b0319 --- /dev/null +++ b/sdk/src/userStats/types.ts @@ -0,0 +1,23 @@ +import { BN } from '@coral-xyz/anchor'; +import { DataAndSlot, UserStatsAccountSubscriber } from '../accounts/types'; +import { UserStatsAccount } from '../types'; +import { ReferrerInfo } from '../types'; +import { PublicKey } from '@solana/web3.js'; + +export interface IUserStats { + userStatsAccountPublicKey: PublicKey; + accountSubscriber: UserStatsAccountSubscriber; + isSubscribed: boolean; + + subscribe(userStatsAccount?: UserStatsAccount): Promise; + fetchAccounts(): Promise; + unsubscribe(): Promise; + getAccountAndSlot(): DataAndSlot; + getAccount(): UserStatsAccount; + getInsuranceFuelBonus( + now: BN, + includeSettled?: boolean, + includeUnsettled?: boolean + ): BN; + getReferrerInfo(): ReferrerInfo | undefined; +} From 07b3fb52ded0a90ecb09db70fcfd3cf322a806e2 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:03:26 +0000 Subject: [PATCH 020/216] sdk: release v2.130.0-beta.10 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 2603f83184..8886cef7ef 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.9 \ No newline at end of file +2.130.0-beta.10 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index a9b6073488..3f5ecb5b5b 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.9", + "version": "2.130.0-beta.10", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From ca42b4341e61c58f57f04f46096a299982fa3247 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 29 Jul 2025 23:31:46 +0800 Subject: [PATCH 021/216] Remove Circular Dependencies 3 (#1779) --- sdk/src/dlob/DLOB.ts | 4 ++-- sdk/src/driftClient/index.ts | 2 +- sdk/src/driftClient/types.ts | 14 +++++++------- sdk/src/index.ts | 2 +- sdk/src/math/margin.ts | 10 +++++----- sdk/src/math/orders.ts | 8 ++++---- sdk/src/user/index.ts | 12 +++++++++--- sdk/src/user/types.ts | 26 +++++++++++++++++++++++++- sdk/src/userConfig.ts | 32 -------------------------------- sdk/src/userMap/events.ts | 4 ++-- sdk/src/userMap/types.ts | 23 +++++++++++------------ sdk/src/userMap/userMap.ts | 6 +++--- sdk/src/userMap/userMapConfig.ts | 4 ++-- sdk/src/userStats/index.ts | 8 +++++++- sdk/src/userStatsConfig.ts | 9 +-------- 15 files changed, 80 insertions(+), 84 deletions(-) delete mode 100644 sdk/src/userConfig.ts diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 0993304494..9481c92250 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -10,7 +10,7 @@ import { } from '../constants/numericConstants'; import { decodeName } from '../userName'; import { DLOBNode, DLOBNodeType } from './DLOBNode'; -import { DriftClient } from '../driftClient'; +import { IDriftClient } from '../driftClient/types'; import { getLimitPrice, isOrderExpired, @@ -1561,7 +1561,7 @@ export class DLOB implements IDLOB { } public printTop( - driftClient: DriftClient, + driftClient: IDriftClient, slotSubscriber: SlotSubscriber, marketIndex: number, marketType: MarketType diff --git a/sdk/src/driftClient/index.ts b/sdk/src/driftClient/index.ts index a8dec642d3..6f0ae7e10b 100644 --- a/sdk/src/driftClient/index.ts +++ b/sdk/src/driftClient/index.ts @@ -137,7 +137,7 @@ import { PollingDriftClientAccountSubscriber } from '../accounts/driftClientAcco import { WebSocketDriftClientAccountSubscriber } from '../accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; import { RetryTxSender } from '../tx/retryTxSender'; import { User } from '../user'; -import { UserSubscriptionConfig } from '../userConfig'; +import { UserSubscriptionConfig } from '../user/types'; import { configs, DRIFT_ORACLE_RECEIVER_ID, diff --git a/sdk/src/driftClient/types.ts b/sdk/src/driftClient/types.ts index c78c6b19fb..08ae38611e 100644 --- a/sdk/src/driftClient/types.ts +++ b/sdk/src/driftClient/types.ts @@ -59,8 +59,7 @@ import { } from '../accounts/types'; import { TxSender, TxSigAndSlot } from '../tx/types'; import { OraclePriceData } from '../oracles/types'; -import { User } from '../user'; -import { UserSubscriptionConfig } from '../userConfig'; +import { UserSubscriptionConfig } from '../user/types'; import { DriftEnv } from '../config/types'; import { IUserStats } from '../userStats/types'; import { UserStatsSubscriptionConfig } from '../userStatsConfig'; @@ -74,6 +73,7 @@ import { SwapMode, } from '../jupiter/jupiterClient'; import { TxHandler } from '../tx/txHandler'; +import { IUser } from '../user/types'; type RemainingAccountParams = { userAccounts: UserAccount[]; @@ -93,7 +93,7 @@ export interface IDriftClient { env: DriftEnv; opts?: ConfirmOptions; useHotWalletAdmin?: boolean; - users: Map; + users: Map; userStats?: IUserStats; activeSubAccountId: number; userAccountSubscriptionConfig: UserSubscriptionConfig; @@ -139,7 +139,7 @@ export interface IDriftClient { subAccountId: number, accountSubscriptionConfig: UserSubscriptionConfig, authority?: PublicKey - ): User; + ): IUser; subscribe(): Promise; subscribeUsers(): Promise[]; @@ -427,9 +427,9 @@ export interface IDriftClient { getReclaimRentIx( userAccountPublicKey: PublicKey ): Promise; - getUser(subAccountId?: number, authority?: PublicKey): User; + getUser(subAccountId?: number, authority?: PublicKey): IUser; hasUser(subAccountId?: number, authority?: PublicKey): boolean; - getUsers(): User[]; + getUsers(): IUser[]; getUserStats(): IUserStats; fetchReferrerNameAccount( name: string @@ -2018,7 +2018,7 @@ export interface IDriftClient { getMarketFees( marketType: MarketType, marketIndex?: number, - user?: User, + user?: IUser, enteringHighLeverageMode?: boolean ): { takerFee: number; makerFee: number }; diff --git a/sdk/src/index.ts b/sdk/src/index.ts index fdf9157024..122f1436b5 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -31,7 +31,7 @@ export * from './adminClient'; export * from './assert/assert'; export * from './testClient'; export * from './user'; -export * from './userConfig'; +export * from './user/types'; export * from './userStats'; export * from './userName'; export * from './userStatsConfig'; diff --git a/sdk/src/math/margin.ts b/sdk/src/math/margin.ts index 63832805e4..26fa69e3df 100644 --- a/sdk/src/math/margin.ts +++ b/sdk/src/math/margin.ts @@ -15,7 +15,6 @@ import { BN } from '@coral-xyz/anchor'; import { OraclePriceData } from '../oracles/types'; import { calculateMarketMarginRatio } from './market'; import { calculateScaledInitialAssetWeight } from './spotBalance'; -import { DriftClient } from '../driftClient'; import { OneShotUserAccountSubscriber } from '../accounts/userAccount/oneShotUserAccountSubscriber'; import { PerpMarketAccount, @@ -27,6 +26,7 @@ import { PublicKey } from '@solana/web3.js'; import { User } from '../user'; import { isVariant } from '../types'; import { assert } from '../assert/assert'; +import { IDriftClient } from '../driftClient/types'; export function calculateSizePremiumLiabilityWeight( size: BN, // AMM_RESERVE_PRECISION @@ -216,7 +216,7 @@ export function calculatePerpLiabilityValue( * @returns */ export function calculateMarginUSDCRequiredForTrade( - driftClient: DriftClient, + driftClient: IDriftClient, targetMarketIndex: number, baseSize: BN, userMaxMarginRatio?: number, @@ -256,7 +256,7 @@ export function calculateMarginUSDCRequiredForTrade( * Returns collateral required in the precision of the target collateral market. */ export function calculateCollateralDepositRequiredForTrade( - driftClient: DriftClient, + driftClient: IDriftClient, targetMarketIndex: number, baseSize: BN, collateralIndex: number, @@ -298,7 +298,7 @@ export function calculateCollateralDepositRequiredForTrade( } export function calculateCollateralValueOfDeposit( - driftClient: DriftClient, + driftClient: IDriftClient, collateralIndex: number, baseSize: BN ): BN { @@ -345,7 +345,7 @@ export function calculateLiquidationPrice( } export function calculateUserMaxPerpOrderSize( - driftClient: DriftClient, + driftClient: IDriftClient, userAccountKey: PublicKey, userAccount: UserAccount, targetMarketIndex: number, diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index 7c06f7b51f..898728ebb1 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -1,4 +1,3 @@ -import { User } from '../user'; import { isOneOfVariant, isVariant, @@ -22,8 +21,9 @@ import { calculateUpdatedAMM, } from './amm'; import { standardizeBaseAssetAmount } from './utils'; +import { IUser } from '../user/types'; -export function isOrderRiskIncreasing(user: User, order: Order): boolean { +export function isOrderRiskIncreasing(user: IUser, order: Order): boolean { if (!isVariant(order.status, 'open')) { return false; } @@ -62,7 +62,7 @@ export function isOrderRiskIncreasing(user: User, order: Order): boolean { } export function isOrderRiskIncreasingInSameDirection( - user: User, + user: IUser, order: Order ): boolean { if (!isVariant(order.status, 'open')) { @@ -94,7 +94,7 @@ export function isOrderRiskIncreasingInSameDirection( return false; } -export function isOrderReduceOnly(user: User, order: Order): boolean { +export function isOrderReduceOnly(user: IUser, order: Order): boolean { if (!isVariant(order.status, 'open')) { return false; } diff --git a/sdk/src/user/index.ts b/sdk/src/user/index.ts index 0b8b6323db..5bf7d26a29 100644 --- a/sdk/src/user/index.ts +++ b/sdk/src/user/index.ts @@ -1,7 +1,6 @@ import { PublicKey } from '@solana/web3.js'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; -import { DriftClient } from '../driftClient'; import { HealthComponent, HealthComponents, @@ -93,7 +92,7 @@ import { calculateWorstCaseBaseAssetAmount, } from '../math/margin'; import { OraclePriceData } from '../oracles/types'; -import { UserConfig } from '../userConfig'; +import { UserSubscriptionConfig } from './types'; import { PollingUserAccountSubscriber } from '../accounts/userAccount/pollingUserAccountSubscriber'; import { WebSocketUserAccountSubscriber } from '../accounts/userAccount/webSocketUserAccountSubscriber'; import { @@ -115,9 +114,16 @@ import { calculateSpotFuelBonus, calculatePerpFuelBonus } from '../math/fuel'; import { grpcUserAccountSubscriber } from '../accounts/userAccount/grpcUserAccountSubscriber'; import { IUserStats } from '../userStats/types'; import { IUser } from './types'; +import { IDriftClient } from '../driftClient/types'; + +export type UserConfig = { + accountSubscription?: UserSubscriptionConfig; + driftClient: IDriftClient; + userAccountPublicKey: PublicKey; +}; export class User implements IUser { - driftClient: DriftClient; + driftClient: IDriftClient; userAccountPublicKey: PublicKey; accountSubscriber: UserAccountSubscriber; _isSubscribed = false; diff --git a/sdk/src/user/types.ts b/sdk/src/user/types.ts index 5cd7411f1a..dfba936f50 100644 --- a/sdk/src/user/types.ts +++ b/sdk/src/user/types.ts @@ -1,4 +1,4 @@ -import { PublicKey } from '@solana/web3.js'; +import { Commitment, PublicKey } from '@solana/web3.js'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; import { @@ -18,6 +18,7 @@ import { } from '../types'; import { DataAndSlot, + GrpcConfigs, UserAccountEvents, UserAccountSubscriber, } from '../accounts/types'; @@ -25,6 +26,29 @@ import { BN } from '@coral-xyz/anchor'; import { OraclePriceData } from '../oracles/types'; import { StrictOraclePrice } from '../oracles/strictOraclePrice'; import { IUserStats } from '../userStats/types'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; + +export type UserSubscriptionConfig = + | { + type: 'grpc'; + resubTimeoutMs?: number; + logResubMessages?: boolean; + grpcConfigs: GrpcConfigs; + } + | { + type: 'websocket'; + resubTimeoutMs?: number; + logResubMessages?: boolean; + commitment?: Commitment; + } + | { + type: 'polling'; + accountLoader: BulkAccountLoader; + } + | { + type: 'custom'; + userAccountSubscriber: UserAccountSubscriber; + }; export interface IUser { userAccountPublicKey: PublicKey; diff --git a/sdk/src/userConfig.ts b/sdk/src/userConfig.ts deleted file mode 100644 index 004d90c659..0000000000 --- a/sdk/src/userConfig.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { DriftClient } from './driftClient'; -import { Commitment, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; -import { GrpcConfigs, UserAccountSubscriber } from './accounts/types'; - -export type UserConfig = { - accountSubscription?: UserSubscriptionConfig; - driftClient: DriftClient; - userAccountPublicKey: PublicKey; -}; - -export type UserSubscriptionConfig = - | { - type: 'grpc'; - resubTimeoutMs?: number; - logResubMessages?: boolean; - grpcConfigs: GrpcConfigs; - } - | { - type: 'websocket'; - resubTimeoutMs?: number; - logResubMessages?: boolean; - commitment?: Commitment; - } - | { - type: 'polling'; - accountLoader: BulkAccountLoader; - } - | { - type: 'custom'; - userAccountSubscriber: UserAccountSubscriber; - }; diff --git a/sdk/src/userMap/events.ts b/sdk/src/userMap/events.ts index e3e60e3d26..522e693a4a 100644 --- a/sdk/src/userMap/events.ts +++ b/sdk/src/userMap/events.ts @@ -1,7 +1,7 @@ -import { User } from '../user'; +import { IUser } from '../user/types'; export interface UserEvents { - userUpdate: (payload: User) => void; + userUpdate: (payload: IUser) => void; update: void; error: (e: Error) => void; } diff --git a/sdk/src/userMap/types.ts b/sdk/src/userMap/types.ts index 476d5c86ef..44b2b797f5 100644 --- a/sdk/src/userMap/types.ts +++ b/sdk/src/userMap/types.ts @@ -1,8 +1,6 @@ -import { User } from '../user'; -import { DriftClient } from '../driftClient'; +import { IDriftClient } from '../driftClient/types'; import { UserAccount, OrderRecord } from '../types'; import { WrappedEvent } from '../events/types'; -import { UserSubscriptionConfig } from '../userConfig'; import { DataAndSlot } from '../accounts/types'; import { IDLOB, ProtectMakerParamsMap } from '../dlob/types'; import { PublicKey } from '@solana/web3.js'; @@ -10,9 +8,10 @@ import { UserAccountFilterCriteria as UserFilterCriteria } from './userMapConfig import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserEvents } from './events'; +import { IUser, UserSubscriptionConfig } from '../user/types'; export interface IUserMap { - driftClient: DriftClient; + driftClient: IDriftClient; eventEmitter: StrictEventEmitter; subscribe(): Promise; @@ -31,9 +30,9 @@ export interface IUserMap { * @param key userAccountPublicKey to get User for * @returns user User | undefined */ - get(key: string): User | undefined; + get(key: string): IUser | undefined; - getWithSlot(key: string): DataAndSlot | undefined; + getWithSlot(key: string): DataAndSlot | undefined; /** * gets the User for a particular userAccountPublicKey, if no User exists, new one is created @@ -43,12 +42,12 @@ export interface IUserMap { mustGet( key: string, accountSubscription?: UserSubscriptionConfig - ): Promise; + ): Promise; mustGetWithSlot( key: string, accountSubscription?: UserSubscriptionConfig - ): Promise>; + ): Promise>; mustGetUserAccount(key: string): Promise; @@ -73,13 +72,13 @@ export interface IUserMap { updateWithEventRecord(record: WrappedEvent): Promise; - values(): IterableIterator; + values(): IterableIterator; - valuesWithSlot(): IterableIterator>; + valuesWithSlot(): IterableIterator>; - entries(): IterableIterator<[string, User]>; + entries(): IterableIterator<[string, IUser]>; - entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; + entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; size(): number; diff --git a/sdk/src/userMap/userMap.ts b/sdk/src/userMap/userMap.ts index 98e2bfdaa4..bdbb79cf3f 100644 --- a/sdk/src/userMap/userMap.ts +++ b/sdk/src/userMap/userMap.ts @@ -1,6 +1,5 @@ import { BN } from '@coral-xyz/anchor'; import { User } from '../user'; -import { DriftClient } from '../driftClient'; import { UserAccount, OrderRecord, @@ -15,7 +14,7 @@ import { } from '../types'; import { WrappedEvent } from '../events/types'; import { DLOB } from '../dlob/DLOB'; -import { UserSubscriptionConfig } from '../userConfig'; +import { UserSubscriptionConfig } from '../user/types'; import { DataAndSlot } from '../accounts/types'; import { OneShotUserAccountSubscriber } from '../accounts/userAccount/oneShotUserAccountSubscriber'; import { ProtectMakerParamsMap } from '../dlob/types'; @@ -47,12 +46,13 @@ import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserEvents } from './events'; import { IUserMap } from './types'; +import { IDriftClient } from '../driftClient/types'; const MAX_USER_ACCOUNT_SIZE_BYTES = 4376; export class UserMap implements IUserMap { private userMap = new Map>(); - driftClient: DriftClient; + driftClient: IDriftClient; eventEmitter: StrictEventEmitter; private connection: Connection; private commitment: Commitment; diff --git a/sdk/src/userMap/userMapConfig.ts b/sdk/src/userMap/userMapConfig.ts index afb980c3e7..31bd29fc9c 100644 --- a/sdk/src/userMap/userMapConfig.ts +++ b/sdk/src/userMap/userMapConfig.ts @@ -1,5 +1,5 @@ import { Commitment, Connection, MemcmpFilter } from '@solana/web3.js'; -import { DriftClient } from '../driftClient'; +import { IDriftClient } from '../driftClient/types'; import { GrpcConfigs } from '../accounts/types'; // passed into UserMap.getUniqueAuthorities to filter users @@ -19,7 +19,7 @@ export type SyncConfig = }; export type UserMapConfig = { - driftClient: DriftClient; + driftClient: IDriftClient; // connection object to use specifically for the UserMap. If undefined, will use the driftClient's connection connection?: Connection; subscriptionConfig: diff --git a/sdk/src/userStats/index.ts b/sdk/src/userStats/index.ts index 264d86d79c..e1b2fdc7c4 100644 --- a/sdk/src/userStats/index.ts +++ b/sdk/src/userStats/index.ts @@ -1,6 +1,6 @@ import { PublicKey } from '@solana/web3.js'; import { DataAndSlot, UserStatsAccountSubscriber } from '../accounts/types'; -import { UserStatsConfig } from '../userStatsConfig'; +import { UserStatsSubscriptionConfig } from '../userStatsConfig'; import { PollingUserStatsAccountSubscriber } from '../accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; import { WebSocketUserStatsAccountSubscriber } from '../accounts/userStatsAccount/webSocketUserStatsAccountSubsriber'; import { ReferrerInfo, SpotMarketAccount, UserStatsAccount } from '../types'; @@ -20,6 +20,12 @@ import { calculateInsuranceFuelBonus } from '../math/fuel'; import { IDriftClient } from '../driftClient/types'; import { IUserStats } from './types'; +export type UserStatsConfig = { + accountSubscription?: UserStatsSubscriptionConfig; + driftClient: IDriftClient; + userStatsAccountPublicKey: PublicKey; +}; + export class UserStats implements IUserStats { driftClient: IDriftClient; userStatsAccountPublicKey: PublicKey; diff --git a/sdk/src/userStatsConfig.ts b/sdk/src/userStatsConfig.ts index b3bfbc87b4..a1866a1182 100644 --- a/sdk/src/userStatsConfig.ts +++ b/sdk/src/userStatsConfig.ts @@ -1,14 +1,7 @@ -import { DriftClient } from './driftClient'; -import { Commitment, PublicKey } from '@solana/web3.js'; +import { Commitment } from '@solana/web3.js'; import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; import { GrpcConfigs } from './accounts/types'; -export type UserStatsConfig = { - accountSubscription?: UserStatsSubscriptionConfig; - driftClient: DriftClient; - userStatsAccountPublicKey: PublicKey; -}; - export type UserStatsSubscriptionConfig = | { type: 'websocket'; From 28b15318812350a8fc1c7cfaa6bd6ab708f79b55 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 15:36:56 +0000 Subject: [PATCH 022/216] sdk: release v2.130.0-beta.11 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8886cef7ef..dd9d308011 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.10 \ No newline at end of file +2.130.0-beta.11 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 3f5ecb5b5b..8f2572a547 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.10", + "version": "2.130.0-beta.11", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 255c0707ab6914bd472836f24701aea665db7c91 Mon Sep 17 00:00:00 2001 From: moosecat Date: Tue, 29 Jul 2025 10:42:07 -0700 Subject: [PATCH 023/216] vamm l2 oracle changes (#1780) * vamm l2 oracle changes * add drift client types to barrel file --- sdk/src/dlob/DLOBSubscriber.ts | 3 +- sdk/src/driftClient/index.ts | 22 ++++++++++--- sdk/src/index.ts | 1 + sdk/src/math/amm.ts | 59 ++++++++++++++++++++++++++++------ sdk/src/oracles/types.ts | 1 + sdk/src/oracles/utils.ts | 4 +-- 6 files changed, 72 insertions(+), 18 deletions(-) diff --git a/sdk/src/dlob/DLOBSubscriber.ts b/sdk/src/dlob/DLOBSubscriber.ts index 58a8b5e5a9..111d231dc6 100644 --- a/sdk/src/dlob/DLOBSubscriber.ts +++ b/sdk/src/dlob/DLOBSubscriber.ts @@ -140,7 +140,8 @@ export class DLOBSubscriber { fallbackL2Generators = [ getVammL2Generator({ marketAccount: this.driftClient.getPerpMarketAccount(marketIndex), - oraclePriceData, + oraclePriceData: + this.driftClient.getMMOracleDataForPerpMarket(marketIndex), numOrders: numVammOrders ?? depth, topOfBookQuoteAmounts: marketIndex < 3 diff --git a/sdk/src/driftClient/index.ts b/sdk/src/driftClient/index.ts index 6f0ae7e10b..1bede7816e 100644 --- a/sdk/src/driftClient/index.ts +++ b/sdk/src/driftClient/index.ts @@ -123,6 +123,8 @@ import { TxSender, TxSigAndSlot } from '../tx/types'; import { BASE_PRECISION, GOV_SPOT_MARKET_INDEX, + ONE, + PERCENTAGE_PRECISION, PRICE_PRECISION, QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, @@ -8487,15 +8489,25 @@ export class DriftClient implements IDriftClient { } public getOracleDataForPerpMarket(marketIndex: number): OraclePriceData { - return this.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket( - marketIndex - ).data; + const perpMarket = this.getPerpMarketAccount(marketIndex); + const isMMOracleActive = !perpMarket.amm.mmOracleSlot.eq(ZERO); + return { + ...this.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket( + marketIndex + ).data, + isMMOracleActive, + }; } public getMMOracleDataForPerpMarket(marketIndex: number): OraclePriceData { const perpMarket = this.getPerpMarketAccount(marketIndex); const oracleData = this.getOracleDataForPerpMarket(marketIndex); const stateAccountAndSlot = this.accountSubscriber.getStateAccountAndSlot(); + const pctDiff = perpMarket.amm.mmOraclePrice + .sub(oracleData.price) + .abs() + .mul(PERCENTAGE_PRECISION) + .div(BN.max(oracleData.price, ONE)); if ( !isOracleValid( perpMarket, @@ -8504,7 +8516,8 @@ export class DriftClient implements IDriftClient { stateAccountAndSlot.slot ) || perpMarket.amm.mmOraclePrice.eq(ZERO) || - perpMarket.amm.mmOracleSlot < oracleData.slot + perpMarket.amm.mmOracleSlot < oracleData.slot || + pctDiff.gt(PERCENTAGE_PRECISION.divn(100)) // 1% threshold ) { return { ...oracleData, fetchedWithMMOracle: true }; } else { @@ -8519,6 +8532,7 @@ export class DriftClient implements IDriftClient { confidence: conf, hasSufficientNumberOfDataPoints: true, fetchedWithMMOracle: true, + isMMOracleActive: oracleData.isMMOracleActive, }; } } diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 122f1436b5..aa09c8c4e4 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -38,6 +38,7 @@ export * from './userStatsConfig'; export * from './decode/user'; export * from './decode/customCoder'; export * from './driftClient'; +export * from './driftClient/types'; export * from './factory/oracleClient'; export * from './factory/bigNum'; export * from './events/types'; diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index ea38698e46..ed26de9be7 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -157,11 +157,6 @@ export function calculateUpdatedAMM( amm: AMM, oraclePriceData: OraclePriceData ): AMM { - if (!oraclePriceData?.fetchedWithMMOracle) { - console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing' - ); - } if (amm.curveUpdateIntensity == 0 || oraclePriceData === undefined) { return amm; } @@ -204,9 +199,12 @@ export function calculateUpdatedAMMSpreadReserves( oraclePriceData: OraclePriceData, isPrediction = false ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } { - if (!oraclePriceData?.fetchedWithMMOracle) { + if ( + !oraclePriceData?.fetchedWithMMOracle && + oraclePriceData?.isMMOracleActive + ) { console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing' + 'Use driftClient method getMMOracleDataForPerpMarket for accurate updated AMM in calculateUpdatedAMMSpreadReserves' ); } const newAmm = calculateUpdatedAMM(amm, oraclePriceData); @@ -231,15 +229,18 @@ export function calculateUpdatedAMMSpreadReserves( return result; } -export function calculateBidAskPrice( +export function calculateAMMBidAskPrice( amm: AMM, oraclePriceData: OraclePriceData, withUpdate = true, isPrediction = false ): [BN, BN] { - if (!oraclePriceData?.fetchedWithMMOracle) { + if ( + !oraclePriceData?.fetchedWithMMOracle && + oraclePriceData?.isMMOracleActive + ) { console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing' + 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing in calculateAMMBidAskPrice' ); } let newAmm: AMM; @@ -271,6 +272,44 @@ export function calculateBidAskPrice( return [bidPrice, askPrice]; } +/** + * @deprecated Use calculateAMMBidAskPrice instead + */ +export function calculateBidAskPrice( + amm: AMM, + oraclePriceData: OraclePriceData, + withUpdate = true, + isPrediction = false +): [BN, BN] { + let newAmm: AMM; + if (withUpdate) { + newAmm = calculateUpdatedAMM(amm, oraclePriceData); + } else { + newAmm = amm; + } + + const [bidReserves, askReserves] = calculateSpreadReserves( + newAmm, + oraclePriceData, + undefined, + isPrediction + ); + + const askPrice = calculatePrice( + askReserves.baseAssetReserve, + askReserves.quoteAssetReserve, + newAmm.pegMultiplier + ); + + const bidPrice = calculatePrice( + bidReserves.baseAssetReserve, + bidReserves.quoteAssetReserve, + newAmm.pegMultiplier + ); + + return [bidPrice, askPrice]; +} + /** * Calculates a price given an arbitrary base and quote amount (they must have the same precision) * diff --git a/sdk/src/oracles/types.ts b/sdk/src/oracles/types.ts index 167dee3af8..5ffbc25744 100644 --- a/sdk/src/oracles/types.ts +++ b/sdk/src/oracles/types.ts @@ -17,6 +17,7 @@ export type OraclePriceData = { twapConfidence?: BN; maxPrice?: BN; // pre-launch markets only fetchedWithMMOracle?: boolean; + isMMOracleActive?: boolean; }; export type OracleInfo = { diff --git a/sdk/src/oracles/utils.ts b/sdk/src/oracles/utils.ts index 6dfcc36934..f26e498487 100644 --- a/sdk/src/oracles/utils.ts +++ b/sdk/src/oracles/utils.ts @@ -1,13 +1,11 @@ import { BN } from '@coral-xyz/anchor'; import { MMOraclePriceData } from './types'; -import { FIVE } from '../constants/numericConstants'; export function getOracleConfidenceFromMMOracleData( mmOracleData: MMOraclePriceData ): BN { const mmOracleDiffPremium = mmOracleData.mmOraclePrice .sub(mmOracleData.oraclePriceData.price) - .abs() - .div(FIVE); + .abs(); return mmOracleData.oraclePriceData.confidence.add(mmOracleDiffPremium); } From 6d8cf56273b26eb86c0d5f488ebff0ca50a8a07b Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 17:47:23 +0000 Subject: [PATCH 024/216] sdk: release v2.130.0-beta.12 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index dd9d308011..bb4d1e1e9b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.11 \ No newline at end of file +2.130.0-beta.12 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 8f2572a547..fa40814caa 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.11", + "version": "2.130.0-beta.12", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From cb1e7dff995363f284f445f82f446306f10f7574 Mon Sep 17 00:00:00 2001 From: lil perp Date: Tue, 29 Jul 2025 14:12:26 -0400 Subject: [PATCH 025/216] program: stop counting reduce only orders to open bids/asks (#1746) * program: stop counting reduce only orders to open bids/asks * account for old vs new reduce only orders * add sdk * ignore max lev flag for reduce only orders * tweak filter logic * test * moar tests * simplify * only exclude tpsl * cargo fmt -- * tweak * more tweaks --- programs/drift/src/controller/orders.rs | 104 +++++++--- programs/drift/src/controller/position.rs | 16 +- .../drift/src/controller/spot_position.rs | 16 +- programs/drift/src/math/orders.rs | 6 +- programs/drift/src/math/orders/tests.rs | 19 ++ programs/drift/src/state/order_params.rs | 4 + programs/drift/src/state/user.rs | 15 +- programs/drift/src/state/user/tests.rs | 183 ++++++++++++++++++ sdk/src/dlob/DLOB.ts | 30 ++- sdk/src/dlob/DLOBNode.ts | 31 ++- sdk/src/dlob/NodeList.ts | 7 +- sdk/src/dlob/orderBookLevels.ts | 10 +- sdk/src/dlob/types.ts | 2 + sdk/src/math/orders.ts | 18 ++ sdk/src/orderSubscriber/OrderSubscriber.ts | 15 +- 15 files changed, 421 insertions(+), 55 deletions(-) diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 377fcd3c9e..3b0d25ae85 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -212,7 +212,9 @@ pub fn place_perp_order( market.amm.order_step_size )?; - let base_asset_amount = if params.base_asset_amount == u64::MAX { + let base_asset_amount = if params.base_asset_amount == u64::MAX + && !(params.is_trigger_order() && params.reduce_only) + { calculate_max_perp_order_size( user, position_index, @@ -288,7 +290,18 @@ pub fn place_perp_order( // Start with 0 and set bit flags let mut bit_flags: u8 = 0; - bit_flags = set_is_signed_msg_flag(bit_flags, options.is_signed_msg_order()); + bit_flags = set_order_bit_flag( + bit_flags, + options.is_signed_msg_order(), + OrderBitFlag::SignedMessage, + ); + + let reduce_only = params.reduce_only || force_reduce_only; + bit_flags = set_order_bit_flag( + bit_flags, + params.is_trigger_order() && reduce_only, + OrderBitFlag::NewTriggerReduceOnly, + ); let new_order = Order { status: OrderStatus::Open, @@ -309,7 +322,7 @@ pub fn place_perp_order( base_asset_amount_filled: 0, quote_asset_amount_filled: 0, direction: params.direction, - reduce_only: params.reduce_only || force_reduce_only, + reduce_only, trigger_price: standardize_price( params.trigger_price.unwrap_or(0), market.amm.order_tick_size, @@ -350,13 +363,12 @@ pub fn place_perp_order( user.increment_open_orders(new_order.has_auction()); user.orders[new_order_index] = new_order; user.perp_positions[position_index].open_orders += 1; - if !new_order.must_be_triggered() { - increase_open_bids_and_asks( - &mut user.perp_positions[position_index], - ¶ms.direction, - order_base_asset_amount, - )?; - } + increase_open_bids_and_asks( + &mut user.perp_positions[position_index], + ¶ms.direction, + order_base_asset_amount, + new_order.update_open_bids_and_asks(), + )?; options.update_risk_increasing(risk_increasing); @@ -719,13 +731,15 @@ pub fn cancel_order( let position_index = get_position_index(&user.perp_positions, order_market_index)?; // only decrease open/bids ask if it's not a trigger order or if it's been triggered - if !user.orders[order_index].must_be_triggered() || user.orders[order_index].triggered() { + let update_open_bids_and_asks = user.orders[order_index].update_open_bids_and_asks(); + if update_open_bids_and_asks { let base_asset_amount_unfilled = user.orders[order_index].get_base_asset_amount_unfilled(None)?; position::decrease_open_bids_and_asks( &mut user.perp_positions[position_index], &order_direction, base_asset_amount_unfilled.cast()?, + update_open_bids_and_asks, )?; } @@ -735,13 +749,15 @@ pub fn cancel_order( let spot_position_index = user.get_spot_position_index(order_market_index)?; // only decrease open/bids ask if it's not a trigger order or if it's been triggered - if !user.orders[order_index].must_be_triggered() || user.orders[order_index].triggered() { + let update_open_bids_and_asks = user.orders[order_index].update_open_bids_and_asks(); + if update_open_bids_and_asks { let base_asset_amount_unfilled = user.orders[order_index].get_base_asset_amount_unfilled(None)?; decrease_spot_open_bids_and_asks( &mut user.spot_positions[spot_position_index], &order_direction, base_asset_amount_unfilled, + update_open_bids_and_asks, )?; } user.spot_positions[spot_position_index].open_orders -= 1; @@ -2360,6 +2376,7 @@ pub fn fulfill_perp_order_with_amm( &mut user.perp_positions[position_index], &order_direction, base_asset_amount, + user.orders[order_index].update_open_bids_and_asks(), )?; let (taker, taker_order, maker, maker_order) = @@ -2372,9 +2389,10 @@ pub fn fulfill_perp_order_with_amm( _ => OrderActionExplanation::OrderFilledWithAMM, }; let mut order_action_bit_flags: u8 = 0; - order_action_bit_flags = set_is_signed_msg_flag( + order_action_bit_flags = set_order_bit_flag( order_action_bit_flags, user.orders[order_index].is_signed_msg(), + OrderBitFlag::SignedMessage, ); let ( taker_existing_quote_entry_amount, @@ -2815,6 +2833,7 @@ pub fn fulfill_perp_order_with_match( &mut taker.perp_positions[taker_position_index], &taker.orders[taker_order_index].direction, base_asset_amount_fulfilled_by_maker, + taker.orders[taker_order_index].update_open_bids_and_asks(), )?; update_order_after_fill( @@ -2827,6 +2846,7 @@ pub fn fulfill_perp_order_with_match( &mut maker.perp_positions[maker_position_index], &maker.orders[maker_order_index].direction, base_asset_amount_fulfilled_by_maker, + maker.orders[maker_order_index].update_open_bids_and_asks(), )?; let fill_record_id = get_then_update_id!(market, next_fill_record_id); @@ -2838,9 +2858,10 @@ pub fn fulfill_perp_order_with_match( OrderActionExplanation::OrderFilledWithMatch }; let mut order_action_bit_flags = 0; - order_action_bit_flags = set_is_signed_msg_flag( + order_action_bit_flags = set_order_bit_flag( order_action_bit_flags, taker.orders[taker_order_index].is_signed_msg(), + OrderBitFlag::SignedMessage, ); let (taker_existing_quote_entry_amount, taker_existing_base_asset_amount) = calculate_existing_position_fields_for_order_action( @@ -3054,9 +3075,15 @@ pub fn trigger_order( let direction = user.orders[order_index].direction; let base_asset_amount = user.orders[order_index].base_asset_amount; + let update_open_bids_and_asks = user.orders[order_index].update_open_bids_and_asks(); let user_position = user.get_perp_position_mut(market_index)?; - increase_open_bids_and_asks(user_position, &direction, base_asset_amount)?; + increase_open_bids_and_asks( + user_position, + &direction, + base_asset_amount, + update_open_bids_and_asks, + )?; } let is_filler_taker = user_key == filler_key; @@ -3615,7 +3642,9 @@ pub fn place_spot_order( step_size )?; - let base_asset_amount = if params.base_asset_amount == u64::MAX { + let base_asset_amount = if params.base_asset_amount == u64::MAX + && !(params.is_trigger_order() && params.reduce_only) + { calculate_max_spot_order_size( user, params.market_index, @@ -3668,6 +3697,15 @@ pub fn place_spot_order( "must be spot order" )?; + let mut bit_flags = 0; + + let reduce_only = params.reduce_only || force_reduce_only; + bit_flags = set_order_bit_flag( + bit_flags, + params.is_trigger_order() && reduce_only, + OrderBitFlag::NewTriggerReduceOnly, + ); + let new_order = Order { status: OrderStatus::Open, order_type: params.order_type, @@ -3682,7 +3720,7 @@ pub fn place_spot_order( base_asset_amount_filled: 0, quote_asset_amount_filled: 0, direction: params.direction, - reduce_only: params.reduce_only || force_reduce_only, + reduce_only, trigger_price: standardize_price( params.trigger_price.unwrap_or(0), spot_market.order_tick_size, @@ -3697,7 +3735,7 @@ pub fn place_spot_order( auction_duration, max_ts, posted_slot_tail: get_posted_slot_from_clock_slot(slot), - bit_flags: 0, + bit_flags, padding: [0; 1], }; @@ -3717,13 +3755,12 @@ pub fn place_spot_order( user.increment_open_orders(new_order.has_auction()); user.orders[new_order_index] = new_order; user.spot_positions[spot_position_index].open_orders += 1; - if !new_order.must_be_triggered() { - increase_spot_open_bids_and_asks( - &mut user.spot_positions[spot_position_index], - ¶ms.direction, - order_base_asset_amount, - )?; - } + increase_spot_open_bids_and_asks( + &mut user.spot_positions[spot_position_index], + ¶ms.direction, + order_base_asset_amount, + new_order.update_open_bids_and_asks(), + )?; options.update_risk_increasing(risk_increasing); @@ -4880,10 +4917,13 @@ pub fn fulfill_spot_order_with_match( )?; let taker_order_direction = taker.orders[taker_order_index].direction; + let taker_update_open_bids_and_asks = + taker.orders[taker_order_index].update_open_bids_and_asks(); decrease_spot_open_bids_and_asks( &mut taker.spot_positions[taker_spot_position_index], &taker_order_direction, base_asset_amount, + taker_update_open_bids_and_asks, )?; taker_stats.update_taker_volume_30d(base_market.fuel_boost_taker, quote_asset_amount, now)?; @@ -4923,10 +4963,13 @@ pub fn fulfill_spot_order_with_match( )?; let maker_order_direction = maker.orders[maker_order_index].direction; + let maker_update_open_bids_and_asks = + maker.orders[maker_order_index].update_open_bids_and_asks(); decrease_spot_open_bids_and_asks( &mut maker.spot_positions[maker_spot_position_index], &maker_order_direction, base_asset_amount, + maker_update_open_bids_and_asks, )?; if let Some(maker_stats) = maker_stats { @@ -5210,10 +5253,13 @@ pub fn fulfill_spot_order_with_external_market( )?; let taker_order_direction = taker.orders[taker_order_index].direction; + let taker_update_open_bids_and_asks = + taker.orders[taker_order_index].update_open_bids_and_asks(); decrease_spot_open_bids_and_asks( taker.force_get_spot_position_mut(base_market.market_index)?, &taker_order_direction, base_asset_amount_filled, + taker_update_open_bids_and_asks, )?; if let (Some(filler), Some(filler_stats)) = (filler, filler_stats) { @@ -5426,9 +5472,15 @@ pub fn trigger_spot_order( let direction = user.orders[order_index].direction; let base_asset_amount = user.orders[order_index].base_asset_amount; + let update_open_bids_and_asks = user.orders[order_index].update_open_bids_and_asks(); let user_position = user.force_get_spot_position_mut(market_index)?; - increase_spot_open_bids_and_asks(user_position, &direction, base_asset_amount.cast()?)?; + increase_spot_open_bids_and_asks( + user_position, + &direction, + base_asset_amount.cast()?, + update_open_bids_and_asks, + )?; } let is_filler_taker = user_key == filler_key; diff --git a/programs/drift/src/controller/position.rs b/programs/drift/src/controller/position.rs index f6cc7ae7ad..53b76188d4 100644 --- a/programs/drift/src/controller/position.rs +++ b/programs/drift/src/controller/position.rs @@ -619,7 +619,12 @@ pub fn increase_open_bids_and_asks( position: &mut PerpPosition, direction: &PositionDirection, base_asset_amount_unfilled: u64, + update: bool, ) -> DriftResult { + if !update { + return Ok(()); + } + match direction { PositionDirection::Long => { position.open_bids = position @@ -640,17 +645,24 @@ pub fn decrease_open_bids_and_asks( position: &mut PerpPosition, direction: &PositionDirection, base_asset_amount_unfilled: u64, + update: bool, ) -> DriftResult { + if !update { + return Ok(()); + } + match direction { PositionDirection::Long => { position.open_bids = position .open_bids - .safe_sub(base_asset_amount_unfilled.cast()?)?; + .safe_sub(base_asset_amount_unfilled.cast()?)? + .max(0); } PositionDirection::Short => { position.open_asks = position .open_asks - .safe_add(base_asset_amount_unfilled.cast()?)?; + .safe_add(base_asset_amount_unfilled.cast()?)? + .min(0); } } diff --git a/programs/drift/src/controller/spot_position.rs b/programs/drift/src/controller/spot_position.rs index 533c8635f6..dd652b22f8 100644 --- a/programs/drift/src/controller/spot_position.rs +++ b/programs/drift/src/controller/spot_position.rs @@ -24,7 +24,12 @@ pub fn increase_spot_open_bids_and_asks( spot_position: &mut SpotPosition, direction: &PositionDirection, base_asset_amount_unfilled: u64, + update: bool, ) -> DriftResult { + if !update { + return Ok(()); + } + match direction { PositionDirection::Long => { spot_position.open_bids = spot_position @@ -45,17 +50,24 @@ pub fn decrease_spot_open_bids_and_asks( spot_position: &mut SpotPosition, direction: &PositionDirection, base_asset_amount_unfilled: u64, + update: bool, ) -> DriftResult { + if !update { + return Ok(()); + } + match direction { PositionDirection::Long => { spot_position.open_bids = spot_position .open_bids - .safe_sub(base_asset_amount_unfilled.cast()?)?; + .safe_sub(base_asset_amount_unfilled.cast()?)? + .max(0); } PositionDirection::Short => { spot_position.open_asks = spot_position .open_asks - .safe_add(base_asset_amount_unfilled.cast()?)?; + .safe_add(base_asset_amount_unfilled.cast()?)? + .min(0); } } diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 6b3b3c88fe..488c6bdb2e 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1387,11 +1387,11 @@ pub fn get_posted_slot_from_clock_slot(slot: u64) -> u8 { } // Bit flag operators -pub fn set_is_signed_msg_flag(mut flags: u8, value: bool) -> u8 { +pub fn set_order_bit_flag(mut flags: u8, value: bool, flag: OrderBitFlag) -> u8 { if value { - flags |= OrderBitFlag::SignedMessage as u8; + flags |= flag as u8; } else { - flags &= !(OrderBitFlag::SignedMessage as u8); + flags &= !(flag as u8); } flags } diff --git a/programs/drift/src/math/orders/tests.rs b/programs/drift/src/math/orders/tests.rs index 35b78ca9b0..97aac34377 100644 --- a/programs/drift/src/math/orders/tests.rs +++ b/programs/drift/src/math/orders/tests.rs @@ -4186,3 +4186,22 @@ mod posted_slot_tail { } } } + +mod order_bit_flags { + use crate::math::orders::set_order_bit_flag; + use crate::state::user::OrderBitFlag; + + #[test] + fn test_set_order_bit_flag() { + let mut flags = 0; + + flags = set_order_bit_flag(flags, true, OrderBitFlag::SignedMessage); + assert_eq!(flags, 1); + + flags = set_order_bit_flag(flags, true, OrderBitFlag::NewTriggerReduceOnly); + assert_eq!(flags, 9); + + flags = set_order_bit_flag(flags, false, OrderBitFlag::SignedMessage); + assert_eq!(flags, 8); + } +} diff --git a/programs/drift/src/state/order_params.rs b/programs/drift/src/state/order_params.rs index 0c9cb83257..48bc7399d2 100644 --- a/programs/drift/src/state/order_params.rs +++ b/programs/drift/src/state/order_params.rs @@ -850,6 +850,10 @@ impl OrderParams { pub fn is_max_leverage_order(&self) -> bool { self.base_asset_amount == u64::MAX } + + pub fn is_trigger_order(&self) -> bool { + self.order_type == OrderType::TriggerMarket || self.order_type == OrderType::TriggerLimit + } } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 4056dbb2d8..4bcb6fb2f4 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -667,10 +667,10 @@ pub struct SpotPosition { /// interest of corresponding market. /// precision: SPOT_BALANCE_PRECISION pub scaled_balance: u64, - /// How many spot bids the user has open + /// How many spot non reduce only trigger orders the user has open /// precision: token mint precision pub open_bids: i64, - /// How many spot asks the user has open + /// How many spot non reduce only trigger orders the user has open /// precision: token mint precision pub open_asks: i64, /// The cumulative deposits/borrows a user has made into a market @@ -940,10 +940,10 @@ pub struct PerpPosition { /// Updated when the user open/closes position. Excludes fees/funding /// precision: QUOTE_PRECISION pub quote_entry_amount: i64, - /// The amount of open bids the user has in this perp market + /// The amount of non reduce only trigger orders the user has open /// precision: BASE_PRECISION pub open_bids: i64, - /// The amount of open asks the user has in this perp market + /// The amount of non reduce only trigger orders the user has open /// precision: BASE_PRECISION pub open_asks: i64, /// The amount of pnl settled in this market since opening the position @@ -1597,6 +1597,12 @@ impl Order { pub fn is_available(&self) -> bool { self.status != OrderStatus::Open } + + pub fn update_open_bids_and_asks(&self) -> bool { + !self.must_be_triggered() + || (self.triggered() + && !(self.reduce_only && self.is_bit_flag_set(OrderBitFlag::NewTriggerReduceOnly))) + } } impl Default for Order { @@ -1685,6 +1691,7 @@ pub enum OrderBitFlag { SignedMessage = 0b00000001, OracleTriggerMarket = 0b00000010, SafeTriggerOrder = 0b00000100, + NewTriggerReduceOnly = 0b00001000, } #[account(zero_copy(unsafe))] diff --git a/programs/drift/src/state/user/tests.rs b/programs/drift/src/state/user/tests.rs index 57a8654086..4e8d4f3be7 100644 --- a/programs/drift/src/state/user/tests.rs +++ b/programs/drift/src/state/user/tests.rs @@ -2309,3 +2309,186 @@ mod update_referrer_status { assert_eq!(user_stats.referrer_status, 1); } } + +mod update_open_bids_and_asks { + use crate::state::user::{ + Order, OrderBitFlag, OrderTriggerCondition, OrderType, PositionDirection, + }; + + #[test] + fn test_regular_limit_order() { + let order = Order { + order_type: OrderType::Limit, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_regular_market_order() { + let order = Order { + order_type: OrderType::Market, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_trigger_market_order_not_triggered() { + let order = Order { + order_type: OrderType::TriggerMarket, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(!order.update_open_bids_and_asks()); + } + + #[test] + fn test_trigger_market_order_triggered_above() { + let order = Order { + order_type: OrderType::TriggerMarket, + trigger_condition: OrderTriggerCondition::TriggeredAbove, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_trigger_market_order_triggered_below() { + let order = Order { + order_type: OrderType::TriggerMarket, + trigger_condition: OrderTriggerCondition::TriggeredBelow, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_trigger_limit_order_not_triggered() { + let order = Order { + order_type: OrderType::TriggerLimit, + trigger_condition: OrderTriggerCondition::Below, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(!order.update_open_bids_and_asks()); + } + + #[test] + fn test_trigger_limit_order_triggered() { + let order = Order { + order_type: OrderType::TriggerLimit, + trigger_condition: OrderTriggerCondition::TriggeredBelow, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_reduce_only_order_without_new_reduce_only_flag() { + let order = Order { + order_type: OrderType::Limit, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: true, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_reduce_only_order_with_new_reduce_only_flag() { + let mut order = Order { + order_type: OrderType::Limit, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: true, + bit_flags: 0, + ..Order::default() + }; + order.add_bit_flag(OrderBitFlag::NewTriggerReduceOnly); + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_triggered_reduce_only_order_with_new_reduce_only_flag() { + let mut order = Order { + order_type: OrderType::TriggerMarket, + trigger_condition: OrderTriggerCondition::TriggeredAbove, + reduce_only: true, + bit_flags: 0, + ..Order::default() + }; + order.add_bit_flag(OrderBitFlag::NewTriggerReduceOnly); + + assert!(!order.update_open_bids_and_asks()); + } + + #[test] + fn test_oracle_order() { + let order = Order { + order_type: OrderType::Oracle, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_order_with_other_bit_flags() { + let mut order = Order { + order_type: OrderType::Limit, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: false, + bit_flags: 0, + ..Order::default() + }; + order.add_bit_flag(OrderBitFlag::SignedMessage); + order.add_bit_flag(OrderBitFlag::OracleTriggerMarket); + order.add_bit_flag(OrderBitFlag::SafeTriggerOrder); + + assert!(order.update_open_bids_and_asks()); + } + + #[test] + fn test_reduce_only_order_with_other_bit_flags() { + let mut order = Order { + order_type: OrderType::Limit, + trigger_condition: OrderTriggerCondition::Above, + reduce_only: true, + bit_flags: 0, + ..Order::default() + }; + order.add_bit_flag(OrderBitFlag::SignedMessage); + order.add_bit_flag(OrderBitFlag::OracleTriggerMarket); + order.add_bit_flag(OrderBitFlag::SafeTriggerOrder); + + assert!(order.update_open_bids_and_asks()); + } +} diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 9481c92250..bd55479ec1 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -12,6 +12,7 @@ import { decodeName } from '../userName'; import { DLOBNode, DLOBNodeType } from './DLOBNode'; import { IDriftClient } from '../driftClient/types'; import { + calculateOrderBaseAssetAmount, getLimitPrice, isOrderExpired, isRestingLimitOrder, @@ -137,7 +138,26 @@ export class DLOB implements IDLOB { const protectedMaker = isUserProtectedMaker(userAccount); for (const order of userAccount.orders) { - this.insertOrder(order, userAccountPubkeyString, slot, protectedMaker); + let baseAssetAmount = order.baseAssetAmount; + if (order.reduceOnly) { + const existingBaseAmount = + userAccount.perpPositions.find( + (pos) => + pos.marketIndex === order.marketIndex && pos.openOrders > 0 + )?.baseAssetAmount || ZERO; + baseAssetAmount = calculateOrderBaseAssetAmount( + order, + existingBaseAmount + ); + } + + this.insertOrder( + order, + userAccountPubkeyString, + slot, + protectedMaker, + baseAssetAmount + ); } } @@ -150,6 +170,7 @@ export class DLOB implements IDLOB { userAccount: string, slot: number, isUserProtectedMaker: boolean, + baseAssetAmount: BN, onInsert?: OrderBookCallback ): void { if (!isVariant(order.status, 'open')) { @@ -177,7 +198,8 @@ export class DLOB implements IDLOB { marketType, userAccount, isUserProtectedMaker, - this.protectedMakerParamsMap[marketType].get(order.marketIndex) + this.protectedMakerParamsMap[marketType].get(order.marketIndex), + baseAssetAmount ); if (onInsert) { @@ -189,6 +211,7 @@ export class DLOB implements IDLOB { order: Order, userAccount: string, isUserProtectedMaker: boolean, + baseAssetAmount?: BN, onInsert?: OrderBookCallback ): void { const marketType = getVariant(order.marketType) as MarketTypeStr; @@ -208,7 +231,8 @@ export class DLOB implements IDLOB { marketType, userAccount, isUserProtectedMaker, - this.protectedMakerParamsMap[marketType].get(order.marketIndex) + this.protectedMakerParamsMap[marketType].get(order.marketIndex), + baseAssetAmount ); if (onInsert) { onInsert(); diff --git a/sdk/src/dlob/DLOBNode.ts b/sdk/src/dlob/DLOBNode.ts index 5d97deb622..fb5429eaeb 100644 --- a/sdk/src/dlob/DLOBNode.ts +++ b/sdk/src/dlob/DLOBNode.ts @@ -20,6 +20,7 @@ export interface DLOBNode { isProtectedMaker: boolean; protectedMakerParams?: ProtectedMakerParams; isSignedMsg: boolean | undefined; + baseAssetAmount: BN; } export abstract class OrderNode implements DLOBNode { @@ -30,6 +31,7 @@ export abstract class OrderNode implements DLOBNode { haveTrigger = false; isProtectedMaker: boolean; protectedMakerParams?: ProtectedMakerParams; + baseAssetAmount: BN; isSignedMsg: boolean; constructor( @@ -37,6 +39,7 @@ export abstract class OrderNode implements DLOBNode { userAccount: string, isProtectedMaker: boolean, protectedMakerParams?: ProtectedMakerParams, + baseAssetAmount?: BN, isSignedMsg = false ) { // Copy the order over to the node @@ -45,6 +48,7 @@ export abstract class OrderNode implements DLOBNode { this.sortValue = this.getSortValue(order); this.isProtectedMaker = isProtectedMaker; this.protectedMakerParams = protectedMakerParams; + this.baseAssetAmount = baseAssetAmount ?? order.baseAssetAmount; this.isSignedMsg = isSignedMsg; } @@ -156,8 +160,8 @@ export class SignedMsgOrderNode extends OrderNode { next?: SignedMsgOrderNode; previous?: SignedMsgOrderNode; - constructor(order: Order, userAccount: string) { - super(order, userAccount, false, undefined, true); + constructor(order: Order, userAccount: string, baseAssetAmount?: BN) { + super(order, userAccount, false, undefined, baseAssetAmount, true); } getSortValue(order: Order): BN { @@ -189,7 +193,8 @@ export function createNode( order: Order, userAccount: string, isProtectedMaker: boolean, - protectedMakerParams?: ProtectedMakerParams + protectedMakerParams?: ProtectedMakerParams, + baseAssetAmount?: BN ): DLOBNodeMap[T] { switch (nodeType) { case 'floatingLimit': @@ -197,45 +202,51 @@ export function createNode( order, userAccount, isProtectedMaker, - protectedMakerParams + protectedMakerParams, + baseAssetAmount ); case 'protectedFloatingLimit': return new FloatingLimitOrderNode( order, userAccount, isProtectedMaker, - protectedMakerParams + protectedMakerParams, + baseAssetAmount ); case 'restingLimit': return new RestingLimitOrderNode( order, userAccount, isProtectedMaker, - protectedMakerParams + protectedMakerParams, + baseAssetAmount ); case 'takingLimit': return new TakingLimitOrderNode( order, userAccount, isProtectedMaker, - protectedMakerParams + protectedMakerParams, + baseAssetAmount ); case 'market': return new MarketOrderNode( order, userAccount, isProtectedMaker, - undefined + undefined, + baseAssetAmount ); case 'trigger': return new TriggerOrderNode( order, userAccount, isProtectedMaker, - undefined + undefined, + baseAssetAmount ); case 'signedMsg': - return new SignedMsgOrderNode(order, userAccount); + return new SignedMsgOrderNode(order, userAccount, baseAssetAmount); default: throw Error(`Unknown DLOBNode type ${nodeType}`); } diff --git a/sdk/src/dlob/NodeList.ts b/sdk/src/dlob/NodeList.ts index 0fd3afe1c4..b53bf5eea5 100644 --- a/sdk/src/dlob/NodeList.ts +++ b/sdk/src/dlob/NodeList.ts @@ -6,6 +6,7 @@ import { } from '../types'; import { createNode, DLOBNode, DLOBNodeMap } from './DLOBNode'; import { getOrderSignature } from './utils'; +import { BN } from '../index'; export type SortDirection = 'asc' | 'desc'; @@ -36,7 +37,8 @@ export class NodeList marketType: MarketTypeStr, userAccount: string, isProtectedMaker: boolean, - protectedMakerParamsMap?: ProtectedMakerParams + protectedMakerParamsMap?: ProtectedMakerParams, + baseAssetAmount?: BN ): void { if (!isVariant(order.status, 'open')) { return; @@ -47,7 +49,8 @@ export class NodeList order, userAccount, isProtectedMaker, - protectedMakerParamsMap + protectedMakerParamsMap, + baseAssetAmount ); const orderSignature = getOrderSignature(order.orderId, userAccount); diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index 2de983eb81..c5f290c2c4 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -79,22 +79,28 @@ export const MAJORS_TOP_OF_BOOK_QUOTE_AMOUNTS = [ new BN(50000).mul(QUOTE_PRECISION), ]; +const INDICATIVE_QUOTES_PUBKEY = 'inDNdu3ML4vG5LNExqcwuCQtLcCU8KfK5YM2qYV3JJz'; + /** * Get an {@link Generator} generator from a {@link Generator} * @param dlobNodes e.g. {@link DLOB#getRestingLimitAsks} or {@link DLOB#getRestingLimitBids} * @param oraclePriceData * @param slot */ -const INDICATIVE_QUOTES_PUBKEY = 'inDNdu3ML4vG5LNExqcwuCQtLcCU8KfK5YM2qYV3JJz'; export function* getL2GeneratorFromDLOBNodes( dlobNodes: Generator, oraclePriceData: OraclePriceData, slot: number ): Generator { for (const dlobNode of dlobNodes) { - const size = dlobNode.order.baseAssetAmount.sub( + const size = dlobNode.baseAssetAmount.sub( dlobNode.order.baseAssetAmountFilled ) as BN; + + if (size.lte(ZERO)) { + continue; + } + yield { size, price: dlobNode.getPrice(oraclePriceData, slot), diff --git a/sdk/src/dlob/types.ts b/sdk/src/dlob/types.ts index d2757e40df..0e05c17184 100644 --- a/sdk/src/dlob/types.ts +++ b/sdk/src/dlob/types.ts @@ -126,6 +126,7 @@ export interface IDLOB { userAccount: string, slot: number, isUserProtectedMaker: boolean, + baseAssetAmount: BN, onInsert?: OrderBookCallback ): void; @@ -133,6 +134,7 @@ export interface IDLOB { order: Order, userAccount: string, isUserProtectedMaker: boolean, + baseAssetAmount: BN, onInsert?: OrderBookCallback ): void; diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index 898728ebb1..c829a673a4 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -380,3 +380,21 @@ const FLAG_IS_SIGNED_MSG = 0x01; export function isSignedMsgOrder(order: Order): boolean { return (order.bitFlags & FLAG_IS_SIGNED_MSG) !== 0; } + +export function calculateOrderBaseAssetAmount( + order: Order, + existingBaseAssetAmount: BN +): BN { + if (!order.reduceOnly) { + return order.baseAssetAmount; + } + + if (isVariant(order.direction, 'long')) { + return BN.min( + BN.min(existingBaseAssetAmount, ZERO).abs(), + order.baseAssetAmount + ); + } else { + return BN.min(BN.max(existingBaseAssetAmount, ZERO), order.baseAssetAmount); + } +} diff --git a/sdk/src/orderSubscriber/OrderSubscriber.ts b/sdk/src/orderSubscriber/OrderSubscriber.ts index ef63ad887e..26d44341c6 100644 --- a/sdk/src/orderSubscriber/OrderSubscriber.ts +++ b/sdk/src/orderSubscriber/OrderSubscriber.ts @@ -17,6 +17,7 @@ import { PollingSubscription } from './PollingSubscription'; import { WebsocketSubscription } from './WebsocketSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; +import { calculateOrderBaseAssetAmount, ZERO } from '../index'; import { decodeUser } from '../decode/user'; import { grpcSubscription } from './grpcSubscription'; import { isUserProtectedMaker } from '../math/userStatus'; @@ -251,7 +252,19 @@ export class OrderSubscriber implements IOrderSubscriber { for (const [key, { userAccount }] of this.usersAccounts.entries()) { const protectedMaker = isUserProtectedMaker(userAccount); for (const order of userAccount.orders) { - dlob.insertOrder(order, key, slot, protectedMaker); + let baseAssetAmount = order.baseAssetAmount; + if (order.reduceOnly) { + const existingBaseAmount = + userAccount.perpPositions.find( + (pos) => + pos.marketIndex === order.marketIndex && pos.openOrders > 0 + )?.baseAssetAmount || ZERO; + baseAssetAmount = calculateOrderBaseAssetAmount( + order, + existingBaseAmount + ); + } + dlob.insertOrder(order, key, slot, protectedMaker, baseAssetAmount); } } return dlob; From b2f3d7e24db992853624f60869d27c17879751e9 Mon Sep 17 00:00:00 2001 From: moosecat Date: Tue, 29 Jul 2025 11:13:04 -0700 Subject: [PATCH 026/216] Nour/validate fill change (#1772) * program: new amm oracle (#1738) * zero unused amm fields * cargo fmt * bare bones ix * minimal anchor mm oracle impl * update test file * only do admin validate when not anchor test * updates * generalize native entry * fix weird function name chop off * make it compile for --feature cpi (#1748) Co-authored-by: jordy25519 * more efficeint clock and state bit flags check * vamm uses mm oracle (#1747) * add offset * working tests * refactor to use MM oracle as its own type * remove weird preface * sdk updates * bankrun tests all pass * fix test * changes and fixes * widen confidence if mm oracle too diff * sdk side for confidence adjust * changelog * fix lint * fix cargo tests * address comments * add conf check * remove anchor ix and cache oracle confidence * only state admin can reenable mm oracle kill switch * cargo fmt --------- Co-authored-by: jordy25519 * fix tests (#1764) * Nour/move ixs around (#1766) * move around ixs * remove message * add devnet oracle crank wallet * refactored mm oracle * sdk changes + cargo fmt * fix tests * validate price bands with fill fix * normalize fill within price bands * add sdk warning * updated type * undefined guard so anchor tests pass * accept vec for update amm and view amm * adjust test to work with new price bands * Revert "adjust test to work with new price bands" This reverts commit ee40ac8799fa2f6222ea7d0e9b3e07014346a699. * remove price bands logic * add zero ix for mm oracle for reset * v1 safety improvements * isolate funding from MM oracle * add cargo tests for amm availability * change oracle validity log bool to enum * address comment * make validate fill direction agnostic * fix liquidate borrow for perp pnl test * fix tests and address comments --------- Co-authored-by: jordy25519 --- programs/drift/src/controller/orders.rs | 2 - programs/drift/src/math/orders.rs | 116 ++++++++---------------- programs/drift/src/math/orders/tests.rs | 55 +++++++---- programs/drift/src/math/spot_swap.rs | 5 +- sdk/src/idl/drift.json | 44 ++++++++- tests/liquidateBorrowForPerpPnl.ts | 16 ++-- tests/liquidityProvider.ts | 4 +- tests/pauseExchange.ts | 2 +- tests/triggerOrders.ts | 4 +- 9 files changed, 134 insertions(+), 114 deletions(-) diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 3b0d25ae85..437809e50b 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -1327,7 +1327,6 @@ pub fn fill_perp_order( let perp_market = perp_market_map.get_ref(&market_index)?; validate_fill_price_within_price_bands( fill_price, - order_direction, oracle_price, oracle_twap_5min, perp_market.margin_ratio_initial, @@ -4090,7 +4089,6 @@ pub fn fill_spot_order( .last_oracle_price_twap_5min; validate_fill_price_within_price_bands( fill_price, - order_direction, oracle_price, oracle_twap_5min, spot_market.get_margin_ratio(&MarginRequirementType::Initial)?, diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 488c6bdb2e..62eb8c135c 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1,6 +1,8 @@ use std::cmp::min; use std::ops::Sub; +use crate::math::constants::PERCENTAGE_PRECISION; +use crate::math::constants::PERCENTAGE_PRECISION_I128; use crate::msg; use crate::controller::position::PositionDelta; @@ -11,10 +13,10 @@ use crate::math::casting::Cast; use crate::state::protected_maker_mode_config::ProtectedMakerParams; use crate::state::user::OrderBitFlag; use crate::{ - load, math, FeeTier, State, BASE_PRECISION_I128, FEE_ADJUSTMENT_MAX, MARGIN_PRECISION_I128, + load, math, FeeTier, BASE_PRECISION_I128, FEE_ADJUSTMENT_MAX, MARGIN_PRECISION_I128, MAX_PREDICTION_MARKET_PRICE, MAX_PREDICTION_MARKET_PRICE_I64, OPEN_ORDER_MARGIN_REQUIREMENT, - PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, PRICE_PRECISION_U64, - QUOTE_PRECISION_I128, SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_I128, + PERCENTAGE_PRECISION_U64, PRICE_PRECISION_I128, PRICE_PRECISION_U64, QUOTE_PRECISION_I128, + SPOT_WEIGHT_PRECISION, SPOT_WEIGHT_PRECISION_I128, }; use crate::math::constants::MARGIN_PRECISION_U128; @@ -484,7 +486,6 @@ pub fn limit_price_breaches_maker_oracle_price_bands( pub fn validate_fill_price_within_price_bands( fill_price: u64, - direction: PositionDirection, oracle_price: i64, oracle_twap_5min: i64, margin_ratio_initial: u32, @@ -503,85 +504,44 @@ pub fn validate_fill_price_within_price_bands( return Ok(()); } - let oracle_price = oracle_price.unsigned_abs(); - let oracle_twap_5min = oracle_twap_5min.unsigned_abs(); - let max_oracle_diff = margin_ratio_initial.cast::()?; let max_oracle_twap_diff = oracle_twap_5min_percent_divergence.cast::()?; // 50% - if direction == PositionDirection::Long { - if fill_price < oracle_price && fill_price < oracle_twap_5min { - return Ok(()); - } - - let percent_diff: u128 = fill_price - .saturating_sub(oracle_price) - .cast::()? - .safe_mul(MARGIN_PRECISION_U128)? - .safe_div(oracle_price.cast()?)?; - - validate!( - percent_diff < max_oracle_diff, - ErrorCode::PriceBandsBreached, - "Fill Price Breaches Oracle Price Bands: {} % <= {} % (fill: {} >= oracle: {})", - max_oracle_diff, - percent_diff, - fill_price, - oracle_price - )?; - - let percent_diff = fill_price - .saturating_sub(oracle_twap_5min) - .cast::()? - .safe_mul(PERCENTAGE_PRECISION)? - .safe_div(oracle_twap_5min.cast()?)?; - - validate!( - percent_diff < max_oracle_twap_diff, - ErrorCode::PriceBandsBreached, - "Fill Price Breaches Oracle TWAP Price Bands: {} % <= {} % (fill: {} >= twap: {})", - max_oracle_twap_diff, - percent_diff, - fill_price, - oracle_twap_5min - )?; - } else { - if fill_price > oracle_price && fill_price > oracle_twap_5min { - return Ok(()); - } - - let percent_diff: u128 = oracle_price - .saturating_sub(fill_price) - .cast::()? - .safe_mul(MARGIN_PRECISION_U128)? - .safe_div(oracle_price.cast()?)?; - - validate!( - percent_diff < max_oracle_diff, - ErrorCode::PriceBandsBreached, - "Fill Price Breaches Oracle Price Bands: {} % <= {} % (fill: {} <= oracle: {})", - max_oracle_diff, - percent_diff, - fill_price, - oracle_price - )?; + let percent_diff = fill_price + .cast::()? + .safe_sub(oracle_price)? + .cast::()? + .safe_mul(MARGIN_PRECISION_I128)? + .safe_div(oracle_price.cast::()?)? + .unsigned_abs(); - let percent_diff = oracle_twap_5min - .saturating_sub(fill_price) - .cast::()? - .safe_mul(PERCENTAGE_PRECISION)? - .safe_div(oracle_twap_5min.cast()?)?; + let percent_diff_twap = fill_price + .cast::()? + .safe_sub(oracle_twap_5min)? + .cast::()? + .safe_mul(PERCENTAGE_PRECISION_I128)? + .safe_div(oracle_twap_5min.cast::()?)? + .unsigned_abs(); + + validate!( + percent_diff < max_oracle_diff, + ErrorCode::PriceBandsBreached, + "Fill Price Breaches Oracle Price Bands: max_oracle_diff {} % , pct diff {} % (fill: {} , oracle: {})", + max_oracle_diff, + percent_diff, + fill_price, + oracle_price + )?; - validate!( - percent_diff < max_oracle_twap_diff, - ErrorCode::PriceBandsBreached, - "Fill Price Breaches Oracle TWAP Price Bands: {} % <= {} % (fill: {} <= twap: {})", - max_oracle_twap_diff, - percent_diff, - fill_price, - oracle_twap_5min - )?; - } + validate!( + percent_diff_twap < max_oracle_twap_diff, + ErrorCode::PriceBandsBreached, + "Fill Price Breaches Oracle TWAP Price Bands: max_oracle_twap_diff {} % , pct diff {} % (fill: {} , twap: {})", + max_oracle_twap_diff, + percent_diff, + fill_price, + oracle_twap_5min + )?; Ok(()) } diff --git a/programs/drift/src/math/orders/tests.rs b/programs/drift/src/math/orders/tests.rs index 97aac34377..a60b35a87d 100644 --- a/programs/drift/src/math/orders/tests.rs +++ b/programs/drift/src/math/orders/tests.rs @@ -3161,22 +3161,17 @@ mod calculate_max_perp_order_size { pub mod validate_fill_price_within_price_bands { use crate::math::orders::validate_fill_price_within_price_bands; - use crate::{ - PositionDirection, MARGIN_PRECISION, PERCENTAGE_PRECISION, PRICE_PRECISION_I64, - PRICE_PRECISION_U64, - }; + use crate::{MARGIN_PRECISION, PERCENTAGE_PRECISION, PRICE_PRECISION_I64, PRICE_PRECISION_U64}; #[test] fn valid_long() { let oracle_price = 100 * PRICE_PRECISION_I64; let twap = oracle_price; let fill_price = 105 * PRICE_PRECISION_U64; - let direction = PositionDirection::Long; let margin_ratio_initial = MARGIN_PRECISION / 10; assert!(validate_fill_price_within_price_bands( fill_price, - direction, oracle_price, twap, margin_ratio_initial, @@ -3191,12 +3186,10 @@ pub mod validate_fill_price_within_price_bands { let oracle_price = 100 * PRICE_PRECISION_I64; let twap = oracle_price; let fill_price = 95 * PRICE_PRECISION_U64; - let direction = PositionDirection::Short; let margin_ratio_initial = MARGIN_PRECISION / 10; assert!(validate_fill_price_within_price_bands( fill_price, - direction, oracle_price, twap, margin_ratio_initial, @@ -3212,12 +3205,10 @@ pub mod validate_fill_price_within_price_bands { let twap = oracle_price; // 11% greater than oracle price let fill_price = 111 * PRICE_PRECISION_U64; - let direction = PositionDirection::Long; let margin_ratio_initial = MARGIN_PRECISION / 10; // 10x assert!(validate_fill_price_within_price_bands( fill_price, - direction, oracle_price, twap, margin_ratio_initial, @@ -3233,12 +3224,10 @@ pub mod validate_fill_price_within_price_bands { let twap = oracle_price; // 11% less than oracle price let fill_price = 89 * PRICE_PRECISION_U64; - let direction = PositionDirection::Short; let margin_ratio_initial = MARGIN_PRECISION / 10; // 10x assert!(validate_fill_price_within_price_bands( fill_price, - direction, oracle_price, twap, margin_ratio_initial, @@ -3254,12 +3243,10 @@ pub mod validate_fill_price_within_price_bands { let twap = 100 * PRICE_PRECISION_I64; // 50% greater than twap let fill_price = 150 * PRICE_PRECISION_U64; - let direction = PositionDirection::Long; let margin_ratio_initial = MARGIN_PRECISION / 10; // 10x assert!(validate_fill_price_within_price_bands( fill_price, - direction, oracle_price, twap, margin_ratio_initial, @@ -3275,12 +3262,48 @@ pub mod validate_fill_price_within_price_bands { let twap = 100 * PRICE_PRECISION_I64; // 50% less than twap let fill_price = 50 * PRICE_PRECISION_U64; - let direction = PositionDirection::Short; let margin_ratio_initial = MARGIN_PRECISION / 10; // 10x assert!(validate_fill_price_within_price_bands( fill_price, - direction, + oracle_price, + twap, + margin_ratio_initial, + (PERCENTAGE_PRECISION / 2) as u64, + false, + ) + .is_err()) + } + + #[test] + fn invalid_volatile_taker_long() { + let oracle_price = 50 * PRICE_PRECISION_I64; + let twap = 100 * PRICE_PRECISION_I64; + // 50% less than twap + let fill_price = 1 * PRICE_PRECISION_U64; + let margin_ratio_initial = MARGIN_PRECISION / 10; // 10x + + assert!(validate_fill_price_within_price_bands( + fill_price, + oracle_price, + twap, + margin_ratio_initial, + (PERCENTAGE_PRECISION / 2) as u64, + false, + ) + .is_err()) + } + + #[test] + fn invalid_volatile_taker_short() { + let oracle_price = 50 * PRICE_PRECISION_I64; + let twap = 100 * PRICE_PRECISION_I64; + // 50% less than twap + let fill_price = 200 * PRICE_PRECISION_U64; + let margin_ratio_initial = MARGIN_PRECISION / 10; // 10x + + assert!(validate_fill_price_within_price_bands( + fill_price, oracle_price, twap, margin_ratio_initial, diff --git a/programs/drift/src/math/spot_swap.rs b/programs/drift/src/math/spot_swap.rs index c0ebde6426..c08852d479 100644 --- a/programs/drift/src/math/spot_swap.rs +++ b/programs/drift/src/math/spot_swap.rs @@ -100,7 +100,7 @@ pub fn validate_price_bands_for_swap( out_price: i64, oracle_twap_5min_percent_divergence: u64, ) -> DriftResult { - let (fill_price, direction, oracle_price, oracle_twap_5min, margin_ratio) = { + let (fill_price, oracle_price, oracle_twap_5min, margin_ratio) = { let in_market_margin_ratio = in_market.get_margin_ratio(&MarginRequirementType::Initial)?; if in_market_margin_ratio != 0 { @@ -113,7 +113,6 @@ pub fn validate_price_bands_for_swap( ( fill_price, - PositionDirection::Short, in_price, in_market.historical_oracle_data.last_oracle_price_twap_5min, in_market_margin_ratio, @@ -124,7 +123,6 @@ pub fn validate_price_bands_for_swap( ( fill_price, - PositionDirection::Long, out_price, out_market .historical_oracle_data @@ -136,7 +134,6 @@ pub fn validate_price_bands_for_swap( validate_fill_price_within_price_bands( fill_price, - direction, oracle_price, oracle_twap_5min, margin_ratio, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 78a6bafe37..ee618052a0 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -2099,7 +2099,12 @@ "isSigner": false } ], - "args": [] + "args": [ + { + "name": "disableMaintenance", + "type": "bool" + } + ] }, { "name": "updateUserFuelBonus", @@ -7795,11 +7800,24 @@ "type": "u8" }, { - "name": "padding", + "name": "padding1", "type": { "array": [ "u8", - 31 + 3 + ] + } + }, + { + "name": "currentMaintenanceUsers", + "type": "u32" + }, + { + "name": "padding2", + "type": { + "array": [ + "u8", + 24 ] } } @@ -12002,6 +12020,26 @@ ] } }, + { + "name": "LogMode", + "type": { + "kind": "enum", + "variants": [ + { + "name": "None" + }, + { + "name": "ExchangeOracle" + }, + { + "name": "MMOracle" + }, + { + "name": "SafeMMOracle" + } + ] + } + }, { "name": "PositionUpdateType", "type": { diff --git a/tests/liquidateBorrowForPerpPnl.ts b/tests/liquidateBorrowForPerpPnl.ts index f884140e3c..ad97045443 100644 --- a/tests/liquidateBorrowForPerpPnl.ts +++ b/tests/liquidateBorrowForPerpPnl.ts @@ -54,10 +54,10 @@ describe('liquidate borrow for perp pnl', () => { // ammInvariant == k == x * y const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + const ammInitialQuoteAssetReserve = new anchor.BN(500 * 10 ** 13).mul( mantissaSqrtScale ); - const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( + const ammInitialBaseAssetReserve = new anchor.BN(500 * 10 ** 13).mul( mantissaSqrtScale ); @@ -174,7 +174,10 @@ describe('liquidate borrow for perp pnl', () => { new BN(0) ); - await driftClient.moveAmmToPrice(0, new BN(2).mul(PRICE_PRECISION)); + await driftClient.moveAmmToPrice( + 0, + new BN(1).mul(PRICE_PRECISION).muln(101).divn(100) + ); await driftClient.closePosition(0); @@ -256,14 +259,15 @@ describe('liquidate borrow for perp pnl', () => { ) ); assert(liquidationRecord.liquidateBorrowForPerpPnl.perpMarketIndex === 0); + assert( liquidationRecord.liquidateBorrowForPerpPnl.pnlTransfer.gt( - new BN(9969992 - 10) + new BN(79897 - 10) ) ); assert( liquidationRecord.liquidateBorrowForPerpPnl.pnlTransfer.lt( - new BN(9969992 + 10) + new BN(79897 + 10) ) ); assert( @@ -277,7 +281,7 @@ describe('liquidate borrow for perp pnl', () => { assert( liquidationRecord.liquidateBorrowForPerpPnl.liabilityTransfer.eq( - new BN(199399800) + new BN(1597940) ) ); }); diff --git a/tests/liquidityProvider.ts b/tests/liquidityProvider.ts index 10e09b9b41..6425b008d2 100644 --- a/tests/liquidityProvider.ts +++ b/tests/liquidityProvider.ts @@ -871,6 +871,8 @@ describe('liquidity providing', () => { } }); + return; + it('provides lp, users shorts, removes lp, lp has long', async () => { await driftClient.fetchAccounts(); await traderDriftClient.fetchAccounts(); @@ -1609,8 +1611,6 @@ describe('liquidity providing', () => { assert(posAfterSettle.quoteAssetAmount.eq(posAfter.quoteAssetAmount)); }); - return; - it('permissionless lp burn', async () => { return; const lpAmount = new BN(1 * BASE_PRECISION.toNumber()); diff --git a/tests/pauseExchange.ts b/tests/pauseExchange.ts index ddc0329a2e..28d40955d2 100644 --- a/tests/pauseExchange.ts +++ b/tests/pauseExchange.ts @@ -63,7 +63,7 @@ describe('Pause exchange', () => { bankrunContextWrapper ); - const solOracle = await mockOracleNoProgram(bankrunContextWrapper, 30); + const solOracle = await mockOracleNoProgram(bankrunContextWrapper, 1); const periodicity = new BN(60 * 60); // 1 HOUR driftClient = new TestClient({ diff --git a/tests/triggerOrders.ts b/tests/triggerOrders.ts index 85785e2740..e40473653a 100644 --- a/tests/triggerOrders.ts +++ b/tests/triggerOrders.ts @@ -690,7 +690,7 @@ describe('trigger orders', () => { await fillerDriftClient.moveAmmPrice( marketIndex, - ammInitialBaseAssetReserve.div(new BN(10)), + ammInitialBaseAssetReserve.div(new BN(2)), ammInitialQuoteAssetReserve ); await setFeedPriceNoProgram(bankrunContextWrapper, 2.01, solUsd); @@ -901,7 +901,7 @@ describe('trigger orders', () => { await fillerDriftClient.moveAmmPrice( marketIndex, - ammInitialBaseAssetReserve.mul(new BN(10)), + ammInitialBaseAssetReserve.mul(new BN(2)), ammInitialQuoteAssetReserve ); await setFeedPriceNoProgram(bankrunContextWrapper, 0.49, solUsd); From 8964eb08a57f69a76686629d183af0ff9fe905a3 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 18:17:42 +0000 Subject: [PATCH 027/216] sdk: release v2.130.0-beta.13 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index bb4d1e1e9b..f1837b1c2c 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.12 \ No newline at end of file +2.130.0-beta.13 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index fa40814caa..1e754816ed 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.12", + "version": "2.130.0-beta.13", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 827731d45b19ca96c0bd99879057bdd3c889d477 Mon Sep 17 00:00:00 2001 From: lil perp Date: Tue, 29 Jul 2025 16:50:23 -0400 Subject: [PATCH 028/216] sdk: revert drift interface (#1784) * Revert "Remove Circular Dependencies 3 (#1779)" This reverts commit 697ce5083937c6ef4786c16eb616b10dde9ee902. * Revert "Improve Circular Dependencies 2 (#1778)" This reverts commit f09b59dba3c5d66b5182ff43d1cc516d56d8d5ab. * Revert "Improve circular dependencies 1 (#1776)" This reverts commit f4e3333395a455396753a0ea44dc44293fa4832c. * prettify:fix --- bun.lock | 1072 -------- .../basicUserAccountSubscriber.ts | 8 +- .../bulkAccountLoader.ts | 8 +- sdk/src/accounts/bulkUserStatsSubscription.ts | 4 +- sdk/src/accounts/bulkUserSubscription.ts | 4 +- .../customizedCadenceBulkAccountLoader.ts | 2 +- .../grpcAccountSubscriber.ts | 4 +- .../grpcDriftClientAccountSubscriber.ts | 18 +- ...grpcInsuranceFundStakeAccountSubscriber.ts | 6 +- .../grpcProgramAccountSubscriber.ts | 4 +- .../grpcUserAccountSubscriber.ts | 6 +- .../grpcUserStatsAccountSubscriber.ts | 6 +- .../oneShotUserAccountSubscriber.ts | 4 +- .../pollingDriftClientAccountSubscriber.ts | 20 +- ...HighLeverageModeConfigAccountSubscriber.ts | 6 +- ...lingInsuranceFundStakeAccountSubscriber.ts | 6 +- .../pollingOracleAccountSubscriber.ts | 6 +- .../pollingTokenAccountSubscriber.ts | 6 +- .../pollingUserAccountSubscriber.ts | 8 +- .../pollingUserStatsAccountSubscriber.ts | 6 +- .../testBulkAccountLoader.ts | 0 sdk/src/accounts/types.ts | 7 + .../webSocketAccountSubscriber.ts | 4 +- .../webSocketDriftClientAccountSubscriber.ts | 26 +- ...HighLeverageModeConfigAccountSubscriber.ts | 6 +- ...cketInsuranceFundStakeAccountSubscriber.ts | 6 +- .../webSocketProgramAccountSubscriber.ts | 2 +- .../webSocketUserAccountSubscriber.ts | 6 +- .../webSocketUserStatsAccountSubsriber.ts | 6 +- .../auctionSubscriber/auctionSubscriber.ts | 2 +- .../auctionSubscriberGrpc.ts | 4 +- sdk/src/{config/index.ts => config.ts} | 13 +- sdk/src/config/types.ts | 1 - sdk/src/constants/perpMarkets.ts | 2 +- sdk/src/constants/spotMarkets.ts | 2 +- sdk/src/dlob/DLOB.ts | 79 +- sdk/src/dlob/DLOBNode.ts | 2 +- sdk/src/dlob/DLOBSubscriber.ts | 9 +- sdk/src/dlob/NodeList.ts | 10 +- sdk/src/dlob/orderBookLevels.ts | 3 +- sdk/src/dlob/types.ts | 434 +--- sdk/src/dlob/utils.ts | 6 - .../{driftClient/index.ts => driftClient.ts} | 94 +- sdk/src/driftClient/types.ts | 2204 ----------------- sdk/src/driftClientConfig.ts | 4 +- sdk/src/events/types.ts | 2 +- sdk/src/index.ts | 43 +- sdk/src/math/amm.ts | 2 +- sdk/src/math/insurance.ts | 3 +- sdk/src/math/margin.ts | 12 +- sdk/src/math/market.ts | 4 +- sdk/src/math/oracles.ts | 16 +- sdk/src/math/orders.ts | 17 +- sdk/src/math/utils.ts | 8 - sdk/src/openbook/openbookV2Subscriber.ts | 2 +- sdk/src/orderSubscriber/OrderSubscriber.ts | 16 +- .../orderSubscriber/PollingSubscription.ts | 6 +- .../orderSubscriber/WebsocketSubscription.ts | 8 +- sdk/src/orderSubscriber/grpcSubscription.ts | 8 +- sdk/src/orderSubscriber/types.ts | 45 +- sdk/src/phoenix/phoenixSubscriber.ts | 2 +- sdk/src/serum/serumSubscriber.ts | 2 +- sdk/src/serum/types.ts | 2 +- .../grpcSignedMsgUserAccountSubscriber.ts | 2 +- .../swift/signedMsgUserAccountSubscriber.ts | 2 +- sdk/src/swift/swiftOrderSubscriber.ts | 8 +- sdk/src/testClient.ts | 2 +- sdk/src/{user/index.ts => user.ts} | 79 +- sdk/src/user/types.ts | 754 ------ sdk/src/userConfig.ts | 32 + sdk/src/userMap/PollingSubscription.ts | 6 +- sdk/src/userMap/WebsocketSubscription.ts | 8 +- sdk/src/userMap/events.ts | 7 - sdk/src/userMap/grpcSubscription.ts | 10 +- sdk/src/userMap/types.ts | 105 - sdk/src/userMap/userMap.ts | 43 +- sdk/src/userMap/userMapConfig.ts | 4 +- sdk/src/userMap/userStatsMap.ts | 4 +- sdk/src/{userStats/index.ts => userStats.ts} | 35 +- sdk/src/userStats/types.ts | 23 - sdk/src/userStatsConfig.ts | 11 +- ...customizedCadenceBulkAccountLoader.test.ts | 2 +- tests/admin.ts | 2 +- tests/adminDeposit.ts | 2 +- tests/assetTier.ts | 2 +- tests/cancelAllOrders.ts | 2 +- tests/cappedSymFunding.ts | 2 +- tests/curve.ts | 2 +- tests/deleteInitializedSpotMarket.ts | 2 +- tests/depositIntoSpotMarketVault.ts | 2 +- tests/driftClient.ts | 2 +- tests/fillSpot.ts | 2 +- tests/forceUserDelete.ts | 2 +- tests/fuel.ts | 2 +- tests/fuelSweep.ts | 2 +- tests/govStakeDevnet.ts | 2 +- tests/highLeverageMode.ts | 2 +- tests/ifRebalance.ts | 2 +- tests/imbalancePerpPnl.ts | 2 +- tests/insuranceFundStake.ts | 2 +- tests/ksolver.ts | 2 +- tests/liquidateBorrowForPerpPnl.ts | 2 +- tests/liquidateMaxLps.ts | 2 +- tests/liquidatePerp.ts | 2 +- tests/liquidatePerpAndLp.ts | 2 +- tests/liquidatePerpPnlForDeposit.ts | 2 +- tests/liquidatePerpWithFill.ts | 2 +- tests/liquidateSpot.ts | 2 +- tests/liquidateSpotSocialLoss.ts | 2 +- tests/liquidateSpotWithSwap.ts | 2 +- tests/liquidityProvider.ts | 2 +- tests/marketOrder.ts | 2 +- tests/marketOrderBaseAssetAmount.ts | 2 +- tests/maxDeposit.ts | 2 +- tests/maxLeverageOrderParams.ts | 2 +- tests/modifyOrder.ts | 2 +- tests/multipleMakerOrders.ts | 2 +- tests/multipleSpotMakerOrders.ts | 2 +- tests/openbookTest.ts | 2 +- tests/oracleDiffSources.ts | 2 +- tests/oracleFillPriceGuardrails.ts | 2 +- tests/oracleOffsetOrders.ts | 2 +- tests/order.ts | 2 +- tests/ordersWithSpread.ts | 2 +- tests/overwritePerpAccounts.ts | 2 +- tests/pauseDepositWithdraw.ts | 2 +- tests/pauseExchange.ts | 2 +- tests/perpLpJit.ts | 2 +- tests/perpLpRiskMitigation.ts | 2 +- tests/phoenixTest.ts | 2 +- tests/placeAndMakePerp.ts | 2 +- tests/placeAndMakeSignedMsgBankrun.ts | 2 +- tests/placeAndMakeSpotOrder.ts | 2 +- tests/postOnly.ts | 2 +- tests/postOnlyAmmFulfillment.ts | 2 +- tests/prelisting.ts | 2 +- tests/pyth.ts | 2 +- tests/pythLazerBankrun.ts | 2 +- tests/pythPull.ts | 2 +- tests/referencePriceOffset.ts | 2 +- tests/referrer.ts | 2 +- tests/resizeSwiftUserOrderIds.ts | 2 +- tests/roundInFavorBaseAsset.ts | 2 +- tests/serumTest.ts | 2 +- tests/signedMsgWsDelegates.ts | 2 +- tests/spotDepositWithdraw.ts | 2 +- tests/spotDepositWithdraw22.ts | 2 +- tests/spotDepositWithdraw22TransferHooks.ts | 2 +- tests/spotMarketPoolIds.ts | 2 +- tests/spotSwap.ts | 2 +- tests/spotSwap22.ts | 2 +- tests/spotWithdrawUtil100.ts | 2 +- tests/stopLimits.ts | 2 +- tests/subaccounts.ts | 2 +- tests/surgePricing.ts | 2 +- tests/switchOracle.ts | 2 +- tests/switchboardOnDemand.ts | 2 +- tests/switchboardTxCus.ts | 2 +- tests/testHelpers.ts | 2 +- tests/tokenFaucet.ts | 2 +- tests/tradingLP.ts | 2 +- tests/transferPerpPosition.ts | 2 +- tests/transferPools.ts | 2 +- tests/triggerOrders.ts | 2 +- tests/triggerSpotOrder.ts | 2 +- tests/userAccount.ts | 2 +- tests/userDelegate.ts | 2 +- tests/userOrderId.ts | 2 +- tests/whitelist.ts | 2 +- 169 files changed, 538 insertions(+), 5107 deletions(-) delete mode 100644 bun.lock rename sdk/src/accounts/{userAccount => }/basicUserAccountSubscriber.ts (92%) rename sdk/src/accounts/{bulkAccountLoader => }/bulkAccountLoader.ts (96%) rename sdk/src/accounts/{bulkAccountLoader => }/customizedCadenceBulkAccountLoader.ts (98%) rename sdk/src/accounts/{baseSubscribers => }/grpcAccountSubscriber.ts (98%) rename sdk/src/accounts/{driftClientAccount => }/grpcDriftClientAccountSubscriber.ts (93%) rename sdk/src/accounts/{insuranceFundStakeAccount => }/grpcInsuranceFundStakeAccountSubscriber.ts (89%) rename sdk/src/accounts/{programAccount => }/grpcProgramAccountSubscriber.ts (98%) rename sdk/src/accounts/{userAccount => }/grpcUserAccountSubscriber.ts (86%) rename sdk/src/accounts/{userStatsAccount => }/grpcUserStatsAccountSubscriber.ts (87%) rename sdk/src/accounts/{userAccount => }/oneShotUserAccountSubscriber.ts (94%) rename sdk/src/accounts/{driftClientAccount => }/pollingDriftClientAccountSubscriber.ts (97%) rename sdk/src/accounts/{highLeverageModeConfigAccount => }/pollingHighLeverageModeConfigAccountSubscriber.ts (96%) rename sdk/src/accounts/{insuranceFundStakeAccount => }/pollingInsuranceFundStakeAccountSubscriber.ts (96%) rename sdk/src/accounts/{oracleAccount => }/pollingOracleAccountSubscriber.ts (94%) rename sdk/src/accounts/{tokenAccount => }/pollingTokenAccountSubscriber.ts (95%) rename sdk/src/accounts/{userAccount => }/pollingUserAccountSubscriber.ts (94%) rename sdk/src/accounts/{userStatsAccount => }/pollingUserStatsAccountSubscriber.ts (96%) rename sdk/src/accounts/{bulkAccountLoader => }/testBulkAccountLoader.ts (100%) rename sdk/src/accounts/{baseSubscribers => }/webSocketAccountSubscriber.ts (99%) rename sdk/src/accounts/{driftClientAccount => }/webSocketDriftClientAccountSubscriber.ts (96%) rename sdk/src/accounts/{highLeverageModeConfigAccount => }/webSocketHighLeverageModeConfigAccountSubscriber.ts (95%) rename sdk/src/accounts/{insuranceFundStakeAccount => }/webSocketInsuranceFundStakeAccountSubscriber.ts (95%) rename sdk/src/accounts/{programAccount => }/webSocketProgramAccountSubscriber.ts (99%) rename sdk/src/accounts/{userAccount => }/webSocketUserAccountSubscriber.ts (94%) rename sdk/src/accounts/{userStatsAccount => }/webSocketUserStatsAccountSubsriber.ts (93%) rename sdk/src/{config/index.ts => config.ts} (96%) delete mode 100644 sdk/src/config/types.ts delete mode 100644 sdk/src/dlob/utils.ts rename sdk/src/{driftClient/index.ts => driftClient.ts} (99%) delete mode 100644 sdk/src/driftClient/types.ts rename sdk/src/{user/index.ts => user.ts} (98%) delete mode 100644 sdk/src/user/types.ts create mode 100644 sdk/src/userConfig.ts delete mode 100644 sdk/src/userMap/events.ts delete mode 100644 sdk/src/userMap/types.ts rename sdk/src/{userStats/index.ts => userStats.ts} (80%) delete mode 100644 sdk/src/userStats/types.ts diff --git a/bun.lock b/bun.lock deleted file mode 100644 index 5f7ade575e..0000000000 --- a/bun.lock +++ /dev/null @@ -1,1072 +0,0 @@ -{ - "lockfileVersion": 1, - "workspaces": { - "": { - "dependencies": { - "@ellipsis-labs/phoenix-sdk": "1.4.2", - "@pythnetwork/pyth-solana-receiver": "0.8.0", - "@switchboard-xyz/common": "3.0.14", - "@switchboard-xyz/on-demand": "2.4.1", - "anchor-bankrun": "0.3.0", - "chai-bn": "0.2.2", - "csvtojson": "2.0.10", - "dotenv": "16.4.5", - "json2csv": "5.0.7", - "nanoid": "3.3.4", - "rpc-websockets": "7.5.1", - "solana-bankrun": "0.3.0", - "zstddec": "0.1.0", - }, - "devDependencies": { - "@coral-xyz/anchor": "0.29.0", - "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1", - "@project-serum/common": "0.0.1-beta.3", - "@project-serum/serum": "0.13.65", - "@pythnetwork/client": "2.21.0", - "@pythnetwork/price-service-client": "1.9.0", - "@solana/spl-token": "0.4.13", - "@solana/web3.js": "1.73.2", - "@types/bn.js": "5.1.6", - "@types/chai": "5.0.0", - "@types/mocha": "8.2.3", - "@typescript-eslint/eslint-plugin": "6.21.0", - "@typescript-eslint/parser": "6.21.0", - "chai": "4.4.1", - "eslint": "8.57.0", - "eslint-config-prettier": "8.3.0", - "eslint-plugin-prettier": "3.4.0", - "husky": "7.0.4", - "prettier": "3.0.1", - "typedoc": "0.23.23", - "typescript": "5.4.5", - }, - }, - }, - "packages": { - "@babel/runtime": ["@babel/runtime@7.28.2", "", {}, "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA=="], - - "@coral-xyz/anchor": ["@coral-xyz/anchor@0.29.0", "", { "dependencies": { "@coral-xyz/borsh": "^0.29.0", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA=="], - - "@coral-xyz/anchor-30": ["@coral-xyz/anchor@0.30.1", "", { "dependencies": { "@coral-xyz/anchor-errors": "^0.30.1", "@coral-xyz/borsh": "^0.30.1", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ=="], - - "@coral-xyz/anchor-errors": ["@coral-xyz/anchor-errors@0.30.1", "", {}, "sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ=="], - - "@coral-xyz/borsh": ["@coral-xyz/borsh@0.29.0", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ=="], - - "@ellipsis-labs/phoenix-sdk": ["@ellipsis-labs/phoenix-sdk@1.4.2", "", { "dependencies": { "@metaplex-foundation/beet": "^0.7.1", "@metaplex-foundation/rustbin": "^0.3.1", "@metaplex-foundation/solita": "^0.12.2", "@solana/spl-token": "^0.3.7", "@types/node": "^18.11.13", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^5.0.0" } }, "sha512-7Rf2aWHZwuLX8jcrNSRUDf2aHuBnBzsDBN4GzClTdJYVGo4uQzf9ixju5J3apZ+xkQ6qvrEVYOXtogdgOhJFvw=="], - - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], - - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], - - "@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], - - "@eslint/js": ["@eslint/js@8.57.0", "", {}, "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g=="], - - "@grpc/grpc-js": ["@grpc/grpc-js@1.13.4", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg=="], - - "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], - - "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.11.14", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg=="], - - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], - - "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], - - "@isaacs/ttlcache": ["@isaacs/ttlcache@1.4.1", "", {}, "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="], - - "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], - - "@metaplex-foundation/beet": ["@metaplex-foundation/beet@0.7.2", "", { "dependencies": { "ansicolors": "^0.3.2", "assert": "^2.1.0", "bn.js": "^5.2.0", "debug": "^4.3.3" } }, "sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg=="], - - "@metaplex-foundation/beet-solana": ["@metaplex-foundation/beet-solana@0.3.1", "", { "dependencies": { "@metaplex-foundation/beet": ">=0.1.0", "@solana/web3.js": "^1.56.2", "bs58": "^5.0.0", "debug": "^4.3.4" } }, "sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g=="], - - "@metaplex-foundation/rustbin": ["@metaplex-foundation/rustbin@0.3.5", "", { "dependencies": { "debug": "^4.3.3", "semver": "^7.3.7", "text-table": "^0.2.0", "toml": "^3.0.0" } }, "sha512-m0wkRBEQB/8krwMwKBvFugufZtYwMXiGHud2cTDAv+aGXK4M90y0Hx67/wpu+AqqoQfdV8VM9YezUOHKD+Z5kA=="], - - "@metaplex-foundation/solita": ["@metaplex-foundation/solita@0.12.2", "", { "dependencies": { "@metaplex-foundation/beet": "^0.4.0", "@metaplex-foundation/beet-solana": "^0.3.0", "@metaplex-foundation/rustbin": "^0.3.0", "@solana/web3.js": "^1.36.0", "camelcase": "^6.2.1", "debug": "^4.3.3", "js-sha256": "^0.9.0", "prettier": "^2.5.1", "snake-case": "^3.0.4", "spok": "^1.4.3" }, "bin": { "solita": "dist/src/cli/solita.js" } }, "sha512-oczMfE43NNHWweSqhXPTkQBUbap/aAiwjDQw8zLKNnd/J8sXr/0+rKcN5yJIEgcHeKRkp90eTqkmt2WepQc8yw=="], - - "@noble/curves": ["@noble/curves@1.9.5", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-IHiC8xU74NLKg7gNmwMbUVtqqZy9OWKphTAChESCgsXI5NTK6n3ewOFXrj4Dxal/Ml8D3msbPIHfpHLwv50Q2w=="], - - "@noble/ed25519": ["@noble/ed25519@1.7.5", "", {}, "sha512-xuS0nwRMQBvSxDa7UxMb61xTiH3MxTgUfhyPUALVIe0FlOAz4sjELwyDRyUvqeEYfRSG9qNjFIycqLZppg4RSA=="], - - "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], - - "@noble/secp256k1": ["@noble/secp256k1@1.7.2", "", {}, "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ=="], - - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - - "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - - "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - - "@project-serum/anchor": ["@project-serum/anchor@0.11.1", "", { "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" } }, "sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA=="], - - "@project-serum/borsh": ["@project-serum/borsh@0.2.5", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.2.0" } }, "sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q=="], - - "@project-serum/common": ["@project-serum/common@0.0.1-beta.3", "", { "dependencies": { "@project-serum/serum": "^0.13.21", "bn.js": "^5.1.2", "superstruct": "0.8.3" }, "peerDependencies": { "@solana/web3.js": "^0.87.1" } }, "sha512-gnQE/eUydTtto5okCgLWj1M97R9RRPJqnhKklikYI7jP/pnNhDmngSXC/dmfzED2GXSJEIKNIlxVw1k+E2Aw3w=="], - - "@project-serum/serum": ["@project-serum/serum@0.13.65", "", { "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" } }, "sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA=="], - - "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], - - "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], - - "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], - - "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], - - "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], - - "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], - - "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], - - "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], - - "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], - - "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], - - "@pythnetwork/client": ["@pythnetwork/client@2.21.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@coral-xyz/borsh": "^0.28.0", "buffer": "^6.0.1" }, "peerDependencies": { "@solana/web3.js": "^1.30.2" } }, "sha512-jqUuPLuVKRNUsZfwLuvK/MwnJ3LIrIxBNoz43xt0fjvVuH5QyTlz51ek76CkeKfCbomGKe41Vq7bvn8aqWVOGA=="], - - "@pythnetwork/price-service-client": ["@pythnetwork/price-service-client@1.9.0", "", { "dependencies": { "@pythnetwork/price-service-sdk": "*", "@types/ws": "^8.5.3", "axios": "^1.5.1", "axios-retry": "^3.8.0", "isomorphic-ws": "^4.0.1", "ts-log": "^2.2.4", "ws": "^8.6.0" } }, "sha512-SLm3IFcfmy9iMqHeT4Ih6qMNZhJEefY14T9yTlpsH2D/FE5+BaGGnfcexUifVlfH6M7mwRC4hEFdNvZ6ebZjJg=="], - - "@pythnetwork/price-service-sdk": ["@pythnetwork/price-service-sdk@1.8.0", "", { "dependencies": { "bn.js": "^5.2.1" } }, "sha512-tFZ1thj3Zja06DzPIX2dEWSi7kIfIyqreoywvw5NQ3Z1pl5OJHQGMEhxt6Li3UCGSp2ooYZS9wl8/8XfrfrNSA=="], - - "@pythnetwork/pyth-solana-receiver": ["@pythnetwork/pyth-solana-receiver@0.8.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@noble/hashes": "^1.4.0", "@pythnetwork/price-service-sdk": ">=1.6.0", "@pythnetwork/solana-utils": "*", "@solana/web3.js": "^1.90.0" } }, "sha512-5lhLtggAqsiHtffTPM8vcKJmhBdxzidBmiNNUlqPyg9XmhZ4Z+roY0dfzluEoX5xer9rEA1ThsBpX0bG1DRIGA=="], - - "@pythnetwork/solana-utils": ["@pythnetwork/solana-utils@0.4.5", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@solana/web3.js": "^1.90.0", "bs58": "^5.0.0", "jito-ts": "^3.0.1", "ts-log": "^2.2.7" } }, "sha512-NoLdC2rRAx9a66L0hSOAGt6Wj/YxfnKkw+mbb7Tn/Nn1du4HyShi41DiN6B+2XXqnMthNGbf9FSHvj4NXXABvA=="], - - "@solana/buffer-layout": ["@solana/buffer-layout@4.0.1", "", { "dependencies": { "buffer": "~6.0.3" } }, "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA=="], - - "@solana/buffer-layout-utils": ["@solana/buffer-layout-utils@0.2.0", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/web3.js": "^1.32.0", "bigint-buffer": "^1.1.5", "bignumber.js": "^9.0.1" } }, "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g=="], - - "@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], - - "@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], - - "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], - - "@solana/codecs-numbers": ["@solana/codecs-numbers@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg=="], - - "@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], - - "@solana/errors": ["@solana/errors@2.3.0", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^14.0.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ=="], - - "@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], - - "@solana/spl-token": ["@solana/spl-token@0.4.13", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-group": "^0.0.7", "@solana/spl-token-metadata": "^0.1.6", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.95.5" } }, "sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w=="], - - "@solana/spl-token-group": ["@solana/spl-token-group@0.0.7", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug=="], - - "@solana/spl-token-metadata": ["@solana/spl-token-metadata@0.1.6", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA=="], - - "@solana/web3.js": ["@solana/web3.js@1.73.2", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/ed25519": "^1.7.0", "@noble/hashes": "^1.1.2", "@noble/secp256k1": "^1.6.3", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.1", "fast-stable-stringify": "^1.0.0", "jayson": "^3.4.4", "node-fetch": "2", "rpc-websockets": "^7.5.0", "superstruct": "^0.14.2" } }, "sha512-9WACF8W4Nstj7xiDw3Oom22QmrhBh0VyZyZ7JvvG3gOxLWLlX3hvm5nPVJOGcCE/9fFavBbCUb5A6CIuvMGdoA=="], - - "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], - - "@switchboard-xyz/common": ["@switchboard-xyz/common@3.0.14", "", { "dependencies": { "@solana/web3.js": "^1.98.0", "axios": "^1.8.3", "big.js": "^6.2.2", "bn.js": "^5.2.1", "bs58": "^6.0.0", "buffer": "^6.0.3", "decimal.js": "^10.4.3", "js-sha256": "^0.11.0", "protobufjs": "^7.4.0", "yaml": "^2.6.1" } }, "sha512-LpxzEywO0DjPYIgPzQYkf32C7agwW4YRsPN6BcIvYrw0iJdDMtPZ3SQfIGHLSlD1fwvn2KLUYuGaKegeq4aBTw=="], - - "@switchboard-xyz/on-demand": ["@switchboard-xyz/on-demand@2.4.1", "", { "dependencies": { "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1", "@isaacs/ttlcache": "^1.4.1", "@switchboard-xyz/common": ">=3.0.0", "axios": "^1.8.3", "bs58": "^6.0.0", "buffer": "^6.0.3", "js-yaml": "^4.1.0" } }, "sha512-eSlBp+c8lxpcSgh0/2xK8OaLHPziTSZlcs8V96gZGdiCJz1KgWJRNE1qnIJDOwaGdFecZdwcmajfQRtLRLED3w=="], - - "@types/bn.js": ["@types/bn.js@5.1.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w=="], - - "@types/chai": ["@types/chai@5.0.0", "", {}, "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA=="], - - "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - - "@types/mocha": ["@types/mocha@8.2.3", "", {}, "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw=="], - - "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], - - "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], - - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="], - - "@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="], - - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], - - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], - - "@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], - - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" } }, "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ=="], - - "@typescript-eslint/utils": ["@typescript-eslint/utils@6.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ=="], - - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], - - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - - "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], - - "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - - "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], - - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "anchor-bankrun": ["anchor-bankrun@0.3.0", "", { "peerDependencies": { "@coral-xyz/anchor": "^0.28.0", "@solana/web3.js": "^1.78.4", "solana-bankrun": "^0.2.0" } }, "sha512-PYBW5fWX+iGicIS5MIM/omhk1tQPUc0ELAnI/IkLKQJ6d75De/CQRh8MF2bU/TgGyFi6zEel80wUe3uRol9RrQ=="], - - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="], - - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], - - "assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="], - - "assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="], - - "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], - - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - - "axios": ["axios@1.11.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA=="], - - "axios-retry": ["axios-retry@3.9.1", "", { "dependencies": { "@babel/runtime": "^7.15.4", "is-retry-allowed": "^2.2.0" } }, "sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], - - "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - - "big.js": ["big.js@6.2.2", "", {}, "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ=="], - - "bigint-buffer": ["bigint-buffer@1.1.5", "", { "dependencies": { "bindings": "^1.3.0" } }, "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA=="], - - "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], - - "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], - - "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], - - "bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], - - "borsh": ["borsh@0.7.0", "", { "dependencies": { "bn.js": "^5.2.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" } }, "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA=="], - - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], - - "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - - "buffer-layout": ["buffer-layout@1.2.2", "", {}, "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA=="], - - "bufferutil": ["bufferutil@4.0.9", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw=="], - - "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - - "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], - - "chai": ["chai@4.4.1", "", { "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.0.8" } }, "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g=="], - - "chai-bn": ["chai-bn@0.2.2", "", { "peerDependencies": { "bn.js": "^4.11.0", "chai": "^4.0.0" } }, "sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg=="], - - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "check-error": ["check-error@1.0.3", "", { "dependencies": { "get-func-name": "^2.0.2" } }, "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg=="], - - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], - - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - - "commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], - - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - - "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "crypto-hash": ["crypto-hash@1.3.0", "", {}, "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg=="], - - "csvtojson": ["csvtojson@2.0.10", "", { "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.3", "strip-bom": "^2.0.0" }, "bin": { "csvtojson": "./bin/csvtojson" } }, "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ=="], - - "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], - - "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], - - "deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="], - - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], - - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], - - "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], - - "delay": ["delay@5.0.0", "", {}, "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw=="], - - "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - - "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], - - "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], - - "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], - - "dotenv": ["dotenv@16.4.5", "", {}, "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="], - - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - - "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - - "es6-promise": ["es6-promise@4.2.8", "", {}, "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="], - - "es6-promisify": ["es6-promisify@5.0.0", "", { "dependencies": { "es6-promise": "^4.0.3" } }, "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - - "eslint": ["eslint@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.0", "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ=="], - - "eslint-config-prettier": ["eslint-config-prettier@8.3.0", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew=="], - - "eslint-plugin-prettier": ["eslint-plugin-prettier@3.4.0", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0" }, "peerDependencies": { "eslint": ">=5.0.0", "prettier": ">=1.13.0" } }, "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw=="], - - "eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], - - "eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - - "espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], - - "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], - - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], - - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - - "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], - - "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], - - "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], - - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - - "fast-stable-stringify": ["fast-stable-stringify@1.0.0", "", {}, "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag=="], - - "fastestsmallesttextencoderdecoder": ["fastestsmallesttextencoderdecoder@1.0.22", "", {}, "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw=="], - - "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], - - "file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], - - "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "find": ["find@0.3.0", "", { "dependencies": { "traverse-chain": "~0.1.0" } }, "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw=="], - - "find-process": ["find-process@1.4.11", "", { "dependencies": { "chalk": "~4.1.2", "commander": "^12.1.0", "loglevel": "^1.9.2" }, "bin": { "find-process": "bin/find-process.js" } }, "sha512-mAOh9gGk9WZ4ip5UjV0o6Vb4SrfnAmtsFNzkMRH9HQiFXVQnDyQFrSHTK5UoG6E+KV+s+cIznbtwpfN41l2nFA=="], - - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], - - "flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], - - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], - - "follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="], - - "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], - - "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], - - "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - - "get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="], - - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - - "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - - "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], - - "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], - - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - - "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], - - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], - - "husky": ["husky@7.0.4", "", { "bin": { "husky": "lib/bin.js" } }, "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ=="], - - "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], - - "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], - - "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - - "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - - "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], - - "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], - - "is-retry-allowed": ["is-retry-allowed@2.2.0", "", {}, "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg=="], - - "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], - - "is-utf8": ["is-utf8@0.2.1", "", {}, "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "isomorphic-ws": ["isomorphic-ws@4.0.1", "", { "peerDependencies": { "ws": "*" } }, "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="], - - "jayson": ["jayson@3.7.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "JSONStream": "^1.3.5", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.20", "uuid": "^8.3.2", "ws": "^7.4.5" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ=="], - - "jito-ts": ["jito-ts@3.0.1", "", { "dependencies": { "@grpc/grpc-js": "^1.8.13", "@noble/ed25519": "^1.7.1", "@solana/web3.js": "~1.77.3", "agentkeepalive": "^4.3.0", "dotenv": "^16.0.3", "jayson": "^4.0.0", "node-fetch": "^2.6.7", "superstruct": "^1.0.3" } }, "sha512-TSofF7KqcwyaWGjPaSYC8RDoNBY1TPRNBHdrw24bdIi7mQ5bFEDdYK3D//llw/ml8YDvcZlgd644WxhjLTS9yg=="], - - "js-sha256": ["js-sha256@0.11.1", "", {}, "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg=="], - - "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], - - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], - - "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], - - "json2csv": ["json2csv@5.0.7", "", { "dependencies": { "commander": "^6.1.0", "jsonparse": "^1.3.1", "lodash.get": "^4.4.2" }, "bin": { "json2csv": "bin/json2csv.js" } }, "sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA=="], - - "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], - - "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], - - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - - "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], - - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], - - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], - - "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], - - "lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - - "loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="], - - "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], - - "loupe": ["loupe@2.3.7", "", { "dependencies": { "get-func-name": "^2.0.1" } }, "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA=="], - - "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], - - "lunr": ["lunr@2.3.9", "", {}, "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="], - - "marked": ["marked@4.3.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A=="], - - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "nanoid": ["nanoid@3.3.4", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="], - - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - - "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], - - "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - - "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], - - "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], - - "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - - "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - - "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], - - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], - - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - - "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], - - "pathval": ["pathval@1.1.1", "", {}, "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ=="], - - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], - - "prettier": ["prettier@3.0.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ=="], - - "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], - - "protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="], - - "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], - - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], - - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], - - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - - "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], - - "rpc-websockets": ["rpc-websockets@7.5.1", "", { "dependencies": { "@babel/runtime": "^7.17.2", "eventemitter3": "^4.0.7", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w=="], - - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], - - "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], - - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "shiki": ["shiki@0.11.1", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", "vscode-textmate": "^6.0.0" } }, "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA=="], - - "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - - "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], - - "solana-bankrun": ["solana-bankrun@0.3.0", "", { "dependencies": { "@solana/web3.js": "^1.68.0", "bs58": "^4.0.1" }, "optionalDependencies": { "solana-bankrun-darwin-arm64": "0.3.0", "solana-bankrun-darwin-universal": "0.3.0", "solana-bankrun-darwin-x64": "0.3.0", "solana-bankrun-linux-x64-gnu": "0.3.0", "solana-bankrun-linux-x64-musl": "0.3.0" } }, "sha512-YkH7sa8TB/AoRPzG17CXJtYsRIQHEkEqGLz1Vwc13taXhDBkjO7z6NI5JYw7n0ybRymDHwMYTc7sd+5J40TyVQ=="], - - "solana-bankrun-darwin-arm64": ["solana-bankrun-darwin-arm64@0.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+NbDncf0U6l3knuacRBiqpjZ2DSp+5lZaAU518gH7/x6qubbui/d000STaIBK+uNTPBS/AL/bCN+7PkXqmA3lA=="], - - "solana-bankrun-darwin-universal": ["solana-bankrun-darwin-universal@0.3.0", "", { "os": "darwin" }, "sha512-1/F0xdMa4qvc5o6z16FCCbZ5jbdvKvxpx5kyPcMWRiRPwyvi+zltMxciPAYMlg3wslQqGz88uFhrBEzq2eTumQ=="], - - "solana-bankrun-darwin-x64": ["solana-bankrun-darwin-x64@0.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U6CANjkmMl+lgNA7UH0GKs5V7LtVIUDzJBZefGGqLfqUNv3EjA/PrrToM0hAOWJgkxSwdz6zW+p5sw5FmnbXtg=="], - - "solana-bankrun-linux-x64-gnu": ["solana-bankrun-linux-x64-gnu@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-qJSkCFs0k2n4XtTnyxGMiZsuqO2TiqTYgWjQ+3mZhGNUAMys/Vq8bd7/SyBm6RR7EfVuRXRxZvh+F8oKZ77V4w=="], - - "solana-bankrun-linux-x64-musl": ["solana-bankrun-linux-x64-musl@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-xsS2CS2xb1Sw4ivNXM0gPz/qpW9BX0neSvt/pnok5L330Nu9xlTnKAY8FhzzqOP9P9sJlGRM787Y6d0yYwt6xQ=="], - - "spok": ["spok@1.5.5", "", { "dependencies": { "ansicolors": "~0.3.2", "find-process": "^1.4.7" } }, "sha512-IrJIXY54sCNFASyHPOY+jEirkiJ26JDqsGiI0Dvhwcnkl0PEWi1PSsrkYql0rzDw8LFVTcA7rdUCAJdE2HE+2Q=="], - - "stream-chain": ["stream-chain@2.2.5", "", {}, "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="], - - "stream-json": ["stream-json@1.9.1", "", { "dependencies": { "stream-chain": "^2.2.5" } }, "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw=="], - - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-bom": ["strip-bom@2.0.0", "", { "dependencies": { "is-utf8": "^0.2.0" } }, "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g=="], - - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], - - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], - - "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], - - "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], - - "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], - - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], - - "traverse-chain": ["traverse-chain@0.1.0", "", {}, "sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg=="], - - "ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], - - "ts-log": ["ts-log@2.2.7", "", {}, "sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - - "type-detect": ["type-detect@4.1.0", "", {}, "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="], - - "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], - - "typedoc": ["typedoc@0.23.23", "", { "dependencies": { "lunr": "^2.3.9", "marked": "^4.2.4", "minimatch": "^5.1.1", "shiki": "^0.11.1" }, "peerDependencies": { "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-cg1YQWj+/BU6wq74iott513U16fbrPCbyYs04PHZgvoKJIc6EY4xNobyDZh4KMfRGW8Yjv6wwIzQyoqopKOUGw=="], - - "typescript": ["typescript@5.4.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="], - - "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], - - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], - - "utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="], - - "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], - - "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], - - "vscode-oniguruma": ["vscode-oniguruma@1.7.0", "", {}, "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA=="], - - "vscode-textmate": ["vscode-textmate@6.0.0", "", {}, "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ=="], - - "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - - "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], - - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - - "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], - - "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - - "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], - - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - - "zstddec": ["zstddec@0.1.0", "", {}, "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="], - - "@coral-xyz/anchor-30/@coral-xyz/borsh": ["@coral-xyz/borsh@0.30.1", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ=="], - - "@ellipsis-labs/phoenix-sdk/@solana/spl-token": ["@solana/spl-token@0.3.11", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-metadata": "^0.1.2", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.88.0" } }, "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ=="], - - "@ellipsis-labs/phoenix-sdk/@types/node": ["@types/node@18.19.121", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-bHOrbyztmyYIi4f1R0s17QsPs1uyyYnGcXeZoGEd227oZjry0q6XQBQxd82X1I57zEfwO8h9Xo+Kl5gX1d9MwQ=="], - - "@ellipsis-labs/phoenix-sdk/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@metaplex-foundation/beet-solana/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], - - "@metaplex-foundation/solita/@metaplex-foundation/beet": ["@metaplex-foundation/beet@0.4.0", "", { "dependencies": { "ansicolors": "^0.3.2", "bn.js": "^5.2.0", "debug": "^4.3.3" } }, "sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA=="], - - "@metaplex-foundation/solita/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@metaplex-foundation/solita/js-sha256": ["js-sha256@0.9.0", "", {}, "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="], - - "@metaplex-foundation/solita/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - - "@project-serum/anchor/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@project-serum/anchor/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - - "@project-serum/anchor/js-sha256": ["js-sha256@0.9.0", "", {}, "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="], - - "@project-serum/common/superstruct": ["superstruct@0.8.3", "", { "dependencies": { "kind-of": "^6.0.2", "tiny-invariant": "^1.0.6" } }, "sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww=="], - - "@project-serum/serum/@solana/spl-token": ["@solana/spl-token@0.1.8", "", { "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" } }, "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ=="], - - "@pythnetwork/client/@coral-xyz/borsh": ["@coral-xyz/borsh@0.28.0", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@pythnetwork/solana-utils/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@pythnetwork/solana-utils/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], - - "@solana/buffer-layout-utils/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], - - "@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], - - "@solana/codecs-data-structures/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], - - "@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], - - "@solana/codecs-numbers/@solana/codecs-core": ["@solana/codecs-core@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw=="], - - "@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], - - "@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], - - "@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/errors/commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], - - "@solana/options/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], - - "@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], - - "@solana/web3.js/buffer": ["buffer@6.0.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ=="], - - "@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], - - "@switchboard-xyz/common/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@switchboard-xyz/common/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], - - "@switchboard-xyz/on-demand/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], - - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "find-process/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - - "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "jito-ts/@solana/web3.js": ["@solana/web3.js@1.77.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.3.0", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", "node-fetch": "^2.6.7", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } }, "sha512-XdN0Lh4jdY7J8FYMyucxCwzn6Ga2Sr1DHDWRbqVzk7ZPmmpSPOVWHzO67X1cVT+jNi1D6gZi2tgjHgDPuj6e9Q=="], - - "jito-ts/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "jito-ts/superstruct": ["superstruct@1.0.4", "", {}, "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ=="], - - "solana-bankrun/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "typedoc/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], - - "@ellipsis-labs/phoenix-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - - "@ellipsis-labs/phoenix-sdk/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@metaplex-foundation/beet-solana/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], - - "@metaplex-foundation/solita/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@metaplex-foundation/solita/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@metaplex-foundation/solita/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@project-serum/anchor/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@project-serum/anchor/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@project-serum/anchor/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js": ["@solana/web3.js@1.98.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A=="], - - "@project-serum/serum/@solana/spl-token/dotenv": ["dotenv@10.0.0", "", {}, "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@pythnetwork/solana-utils/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], - - "@pythnetwork/solana-utils/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@pythnetwork/solana-utils/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@pythnetwork/solana-utils/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@pythnetwork/solana-utils/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], - - "@solana/buffer-layout-utils/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@solana/buffer-layout-utils/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@solana/buffer-layout-utils/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@solana/codecs-core/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - - "@solana/codecs-data-structures/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - - "@solana/codecs-strings/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - - "@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], - - "@solana/options/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - - "@switchboard-xyz/common/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], - - "@switchboard-xyz/common/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@switchboard-xyz/common/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@switchboard-xyz/common/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@switchboard-xyz/common/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], - - "@switchboard-xyz/on-demand/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], - - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - - "jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "jito-ts/@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], - - "jito-ts/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "jito-ts/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "jito-ts/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "jito-ts/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "solana-bankrun/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "solana-bankrun/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "solana-bankrun/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "typedoc/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@metaplex-foundation/solita/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@metaplex-foundation/solita/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@metaplex-foundation/solita/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@project-serum/anchor/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@project-serum/anchor/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@project-serum/anchor/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@project-serum/anchor/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@project-serum/anchor/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@pythnetwork/solana-utils/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@pythnetwork/solana-utils/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@pythnetwork/solana-utils/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@solana/buffer-layout-utils/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@solana/buffer-layout-utils/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@solana/buffer-layout-utils/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@solana/codecs/@solana/codecs-numbers/@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - - "@switchboard-xyz/common/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@switchboard-xyz/common/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@switchboard-xyz/common/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@switchboard-xyz/common/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@switchboard-xyz/common/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "jito-ts/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "solana-bankrun/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "solana-bankrun/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "solana-bankrun/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "solana-bankrun/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "solana-bankrun/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@project-serum/anchor/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - - "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@switchboard-xyz/common/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "solana-bankrun/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - - "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="], - } -} diff --git a/sdk/src/accounts/userAccount/basicUserAccountSubscriber.ts b/sdk/src/accounts/basicUserAccountSubscriber.ts similarity index 92% rename from sdk/src/accounts/userAccount/basicUserAccountSubscriber.ts rename to sdk/src/accounts/basicUserAccountSubscriber.ts index 848840a9f3..c8731f82fa 100644 --- a/sdk/src/accounts/userAccount/basicUserAccountSubscriber.ts +++ b/sdk/src/accounts/basicUserAccountSubscriber.ts @@ -1,12 +1,8 @@ -import { - DataAndSlot, - UserAccountEvents, - UserAccountSubscriber, -} from '../types'; +import { DataAndSlot, UserAccountEvents, UserAccountSubscriber } from './types'; import { PublicKey } from '@solana/web3.js'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { UserAccount } from '../../types'; +import { UserAccount } from '../types'; /** * Basic implementation of UserAccountSubscriber. It will only take in UserAccount diff --git a/sdk/src/accounts/bulkAccountLoader/bulkAccountLoader.ts b/sdk/src/accounts/bulkAccountLoader.ts similarity index 96% rename from sdk/src/accounts/bulkAccountLoader/bulkAccountLoader.ts rename to sdk/src/accounts/bulkAccountLoader.ts index 76191c03ea..c76d91b1cd 100644 --- a/sdk/src/accounts/bulkAccountLoader/bulkAccountLoader.ts +++ b/sdk/src/accounts/bulkAccountLoader.ts @@ -1,9 +1,9 @@ import { Commitment, PublicKey } from '@solana/web3.js'; import { v4 as uuidv4 } from 'uuid'; -import { BufferAndSlot } from '../types'; -import { promiseTimeout } from '../../util/promiseTimeout'; -import { Connection } from '../../bankrun/bankrunConnection'; -import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../../constants/numericConstants'; +import { BufferAndSlot } from './types'; +import { promiseTimeout } from '../util/promiseTimeout'; +import { Connection } from '../bankrun/bankrunConnection'; +import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../constants/numericConstants'; export type AccountToLoad = { publicKey: PublicKey; diff --git a/sdk/src/accounts/bulkUserStatsSubscription.ts b/sdk/src/accounts/bulkUserStatsSubscription.ts index c2ebe62877..5dca75611a 100644 --- a/sdk/src/accounts/bulkUserStatsSubscription.ts +++ b/sdk/src/accounts/bulkUserStatsSubscription.ts @@ -1,6 +1,6 @@ import { UserStats } from '../userStats'; -import { BulkAccountLoader } from './bulkAccountLoader/bulkAccountLoader'; -import { PollingUserStatsAccountSubscriber } from './userStatsAccount/pollingUserStatsAccountSubscriber'; +import { BulkAccountLoader } from './bulkAccountLoader'; +import { PollingUserStatsAccountSubscriber } from './pollingUserStatsAccountSubscriber'; /** * @param userStats diff --git a/sdk/src/accounts/bulkUserSubscription.ts b/sdk/src/accounts/bulkUserSubscription.ts index c0831c63fe..3720486b3b 100644 --- a/sdk/src/accounts/bulkUserSubscription.ts +++ b/sdk/src/accounts/bulkUserSubscription.ts @@ -1,6 +1,6 @@ import { User } from '../user'; -import { BulkAccountLoader } from './bulkAccountLoader/bulkAccountLoader'; -import { PollingUserAccountSubscriber } from './userAccount/pollingUserAccountSubscriber'; +import { BulkAccountLoader } from './bulkAccountLoader'; +import { PollingUserAccountSubscriber } from './pollingUserAccountSubscriber'; /** * @param users diff --git a/sdk/src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader.ts b/sdk/src/accounts/customizedCadenceBulkAccountLoader.ts similarity index 98% rename from sdk/src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader.ts rename to sdk/src/accounts/customizedCadenceBulkAccountLoader.ts index c3774996da..c870cd6a66 100644 --- a/sdk/src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader.ts +++ b/sdk/src/accounts/customizedCadenceBulkAccountLoader.ts @@ -1,4 +1,4 @@ -import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../../constants/numericConstants'; +import { GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE } from '../constants/numericConstants'; import { BulkAccountLoader } from './bulkAccountLoader'; import { Commitment, Connection, PublicKey } from '@solana/web3.js'; import { v4 as uuidv4 } from 'uuid'; diff --git a/sdk/src/accounts/baseSubscribers/grpcAccountSubscriber.ts b/sdk/src/accounts/grpcAccountSubscriber.ts similarity index 98% rename from sdk/src/accounts/baseSubscribers/grpcAccountSubscriber.ts rename to sdk/src/accounts/grpcAccountSubscriber.ts index 53d99602d7..0ad6ba43cb 100644 --- a/sdk/src/accounts/baseSubscribers/grpcAccountSubscriber.ts +++ b/sdk/src/accounts/grpcAccountSubscriber.ts @@ -1,4 +1,4 @@ -import { ResubOpts, GrpcConfigs } from '../types'; +import { ResubOpts, GrpcConfigs } from './types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; import * as Buffer from 'buffer'; @@ -11,7 +11,7 @@ import { createClient, SubscribeRequest, SubscribeUpdate, -} from '../../isomorphic/grpc'; +} from '../isomorphic/grpc'; export class grpcAccountSubscriber extends WebSocketAccountSubscriber { private client: Client; diff --git a/sdk/src/accounts/driftClientAccount/grpcDriftClientAccountSubscriber.ts b/sdk/src/accounts/grpcDriftClientAccountSubscriber.ts similarity index 93% rename from sdk/src/accounts/driftClientAccount/grpcDriftClientAccountSubscriber.ts rename to sdk/src/accounts/grpcDriftClientAccountSubscriber.ts index ac60fda929..8545b5f029 100644 --- a/sdk/src/accounts/driftClientAccount/grpcDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/grpcDriftClientAccountSubscriber.ts @@ -1,20 +1,16 @@ import { WebSocketDriftClientAccountSubscriber } from './webSocketDriftClientAccountSubscriber'; -import { OracleInfo, OraclePriceData } from '../../oracles/types'; +import { OracleInfo, OraclePriceData } from '../oracles/types'; import { Program } from '@coral-xyz/anchor'; -import { findAllMarketAndOracles } from '../../config'; +import { findAllMarketAndOracles } from '../config'; import { getDriftStateAccountPublicKey, getPerpMarketPublicKey, getSpotMarketPublicKey, -} from '../../addresses/pda'; -import { DelistedMarketSetting, GrpcConfigs, ResubOpts } from '../types'; -import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; -import { - PerpMarketAccount, - SpotMarketAccount, - StateAccount, -} from '../../types'; -import { getOracleId } from '../../oracles/oracleId'; +} from '../addresses/pda'; +import { DelistedMarketSetting, GrpcConfigs, ResubOpts } from './types'; +import { grpcAccountSubscriber } from './grpcAccountSubscriber'; +import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types'; +import { getOracleId } from '../oracles/oracleId'; export class gprcDriftClientAccountSubscriber extends WebSocketDriftClientAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/insuranceFundStakeAccount/grpcInsuranceFundStakeAccountSubscriber.ts b/sdk/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts similarity index 89% rename from sdk/src/accounts/insuranceFundStakeAccount/grpcInsuranceFundStakeAccountSubscriber.ts rename to sdk/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts index 00ceae93fb..56aa167b7e 100644 --- a/sdk/src/accounts/insuranceFundStakeAccount/grpcInsuranceFundStakeAccountSubscriber.ts +++ b/sdk/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts @@ -1,9 +1,9 @@ -import { GrpcConfigs } from '../types'; +import { GrpcConfigs } from './types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; -import { InsuranceFundStake } from '../../types'; +import { InsuranceFundStake } from '../types'; import { WebSocketInsuranceFundStakeAccountSubscriber } from './webSocketInsuranceFundStakeAccountSubscriber'; -import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; +import { grpcAccountSubscriber } from './grpcAccountSubscriber'; export class grpcInsuranceFundStakeAccountSubscriber extends WebSocketInsuranceFundStakeAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/programAccount/grpcProgramAccountSubscriber.ts b/sdk/src/accounts/grpcProgramAccountSubscriber.ts similarity index 98% rename from sdk/src/accounts/programAccount/grpcProgramAccountSubscriber.ts rename to sdk/src/accounts/grpcProgramAccountSubscriber.ts index 8cc26fdfcd..73b6b1a077 100644 --- a/sdk/src/accounts/programAccount/grpcProgramAccountSubscriber.ts +++ b/sdk/src/accounts/grpcProgramAccountSubscriber.ts @@ -1,4 +1,4 @@ -import { ResubOpts, GrpcConfigs } from '../types'; +import { ResubOpts, GrpcConfigs } from './types'; import { Program } from '@coral-xyz/anchor'; import bs58 from 'bs58'; import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; @@ -11,7 +11,7 @@ import { createClient, SubscribeRequest, SubscribeUpdate, -} from '../../isomorphic/grpc'; +} from '../isomorphic/grpc'; export class grpcProgramAccountSubscriber< T, diff --git a/sdk/src/accounts/userAccount/grpcUserAccountSubscriber.ts b/sdk/src/accounts/grpcUserAccountSubscriber.ts similarity index 86% rename from sdk/src/accounts/userAccount/grpcUserAccountSubscriber.ts rename to sdk/src/accounts/grpcUserAccountSubscriber.ts index 68013565d1..db3ad1225a 100644 --- a/sdk/src/accounts/userAccount/grpcUserAccountSubscriber.ts +++ b/sdk/src/accounts/grpcUserAccountSubscriber.ts @@ -1,9 +1,9 @@ -import { ResubOpts, GrpcConfigs } from '../types'; +import { ResubOpts, GrpcConfigs } from './types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; -import { UserAccount } from '../../types'; +import { UserAccount } from '../types'; import { WebSocketUserAccountSubscriber } from './webSocketUserAccountSubscriber'; -import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; +import { grpcAccountSubscriber } from './grpcAccountSubscriber'; export class grpcUserAccountSubscriber extends WebSocketUserAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/userStatsAccount/grpcUserStatsAccountSubscriber.ts b/sdk/src/accounts/grpcUserStatsAccountSubscriber.ts similarity index 87% rename from sdk/src/accounts/userStatsAccount/grpcUserStatsAccountSubscriber.ts rename to sdk/src/accounts/grpcUserStatsAccountSubscriber.ts index 46d5abef45..2998e5884f 100644 --- a/sdk/src/accounts/userStatsAccount/grpcUserStatsAccountSubscriber.ts +++ b/sdk/src/accounts/grpcUserStatsAccountSubscriber.ts @@ -1,9 +1,9 @@ -import { ResubOpts, GrpcConfigs } from '../types'; +import { ResubOpts, GrpcConfigs } from './types'; import { Program } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; -import { UserStatsAccount } from '../../types'; +import { UserStatsAccount } from '../types'; import { WebSocketUserStatsAccountSubscriber } from './webSocketUserStatsAccountSubsriber'; -import { grpcAccountSubscriber } from '../baseSubscribers/grpcAccountSubscriber'; +import { grpcAccountSubscriber } from './grpcAccountSubscriber'; export class grpcUserStatsAccountSubscriber extends WebSocketUserStatsAccountSubscriber { private grpcConfigs: GrpcConfigs; diff --git a/sdk/src/accounts/userAccount/oneShotUserAccountSubscriber.ts b/sdk/src/accounts/oneShotUserAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/userAccount/oneShotUserAccountSubscriber.ts rename to sdk/src/accounts/oneShotUserAccountSubscriber.ts index b901020b0b..263bfa03d6 100644 --- a/sdk/src/accounts/userAccount/oneShotUserAccountSubscriber.ts +++ b/sdk/src/accounts/oneShotUserAccountSubscriber.ts @@ -1,8 +1,8 @@ import { Commitment, PublicKey } from '@solana/web3.js'; -import { UserAccount } from '../../types'; +import { UserAccount } from '../types'; import { BasicUserAccountSubscriber } from './basicUserAccountSubscriber'; import { Program } from '@coral-xyz/anchor'; -import { UserAccountSubscriber } from '../types'; +import { UserAccountSubscriber } from './types'; /** * Simple implementation of UserAccountSubscriber. It will fetch the UserAccount diff --git a/sdk/src/accounts/driftClientAccount/pollingDriftClientAccountSubscriber.ts b/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts similarity index 97% rename from sdk/src/accounts/driftClientAccount/pollingDriftClientAccountSubscriber.ts rename to sdk/src/accounts/pollingDriftClientAccountSubscriber.ts index 6277d47855..18da8df653 100644 --- a/sdk/src/accounts/driftClientAccount/pollingDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts @@ -6,7 +6,7 @@ import { DriftClientAccountSubscriber, NotSubscribedError, OraclesToPoll, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; @@ -16,20 +16,20 @@ import { StateAccount, UserAccount, OracleSource, -} from '../../types'; +} from '../types'; import { getDriftStateAccountPublicKey, getPerpMarketPublicKey, getSpotMarketPublicKey, -} from '../../addresses/pda'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; -import { capitalize, findDelistedPerpMarketsAndOracles } from '../utils'; +} from '../addresses/pda'; +import { BulkAccountLoader } from './bulkAccountLoader'; +import { capitalize, findDelistedPerpMarketsAndOracles } from './utils'; import { PublicKey } from '@solana/web3.js'; -import { OracleInfo, OraclePriceData } from '../../oracles/types'; -import { OracleClientCache } from '../../oracles/oracleClientCache'; -import { QUOTE_ORACLE_PRICE_DATA } from '../../oracles/quoteAssetOracleClient'; -import { findAllMarketAndOracles } from '../../config'; -import { getOracleId } from '../../oracles/oracleId'; +import { OracleInfo, OraclePriceData } from '../oracles/types'; +import { OracleClientCache } from '../oracles/oracleClientCache'; +import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient'; +import { findAllMarketAndOracles } from '../config'; +import { getOracleId } from '../oracles/oracleId'; const ORACLE_DEFAULT_ID = getOracleId( PublicKey.default, diff --git a/sdk/src/accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber.ts b/sdk/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber.ts rename to sdk/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts index 78009b6f5f..6b7f2a0f65 100644 --- a/sdk/src/accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber.ts +++ b/sdk/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, HighLeverageModeConfigAccountEvents, HighLeverageModeConfigAccountSubscriber, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; -import { HighLeverageModeConfig } from '../../types'; +import { BulkAccountLoader } from './bulkAccountLoader'; +import { HighLeverageModeConfig } from '../types'; export class PollingHighLeverageModeConfigAccountSubscriber implements HighLeverageModeConfigAccountSubscriber diff --git a/sdk/src/accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber.ts b/sdk/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber.ts rename to sdk/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts index 32bf6dc91a..bca1c04ac7 100644 --- a/sdk/src/accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber.ts +++ b/sdk/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, InsuranceFundStakeAccountEvents, InsuranceFundStakeAccountSubscriber, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; -import { InsuranceFundStake } from '../../types'; +import { BulkAccountLoader } from './bulkAccountLoader'; +import { InsuranceFundStake } from '../types'; export class PollingInsuranceFundStakeAccountSubscriber implements InsuranceFundStakeAccountSubscriber diff --git a/sdk/src/accounts/oracleAccount/pollingOracleAccountSubscriber.ts b/sdk/src/accounts/pollingOracleAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/oracleAccount/pollingOracleAccountSubscriber.ts rename to sdk/src/accounts/pollingOracleAccountSubscriber.ts index 49ee8c6405..bdb6e74343 100644 --- a/sdk/src/accounts/oracleAccount/pollingOracleAccountSubscriber.ts +++ b/sdk/src/accounts/pollingOracleAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, OracleEvents, OracleAccountSubscriber, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; -import { OracleClient, OraclePriceData } from '../../oracles/types'; +import { BulkAccountLoader } from './bulkAccountLoader'; +import { OracleClient, OraclePriceData } from '../oracles/types'; export class PollingOracleAccountSubscriber implements OracleAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/tokenAccount/pollingTokenAccountSubscriber.ts b/sdk/src/accounts/pollingTokenAccountSubscriber.ts similarity index 95% rename from sdk/src/accounts/tokenAccount/pollingTokenAccountSubscriber.ts rename to sdk/src/accounts/pollingTokenAccountSubscriber.ts index 37ad965943..398b462032 100644 --- a/sdk/src/accounts/tokenAccount/pollingTokenAccountSubscriber.ts +++ b/sdk/src/accounts/pollingTokenAccountSubscriber.ts @@ -3,14 +3,14 @@ import { NotSubscribedError, TokenAccountEvents, TokenAccountSubscriber, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { BulkAccountLoader } from './bulkAccountLoader'; import { Account } from '@solana/spl-token'; -import { parseTokenAccount } from '../../token'; +import { parseTokenAccount } from '../token'; export class PollingTokenAccountSubscriber implements TokenAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/userAccount/pollingUserAccountSubscriber.ts b/sdk/src/accounts/pollingUserAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/userAccount/pollingUserAccountSubscriber.ts rename to sdk/src/accounts/pollingUserAccountSubscriber.ts index 1af3f0f74b..d5d7bffeaa 100644 --- a/sdk/src/accounts/userAccount/pollingUserAccountSubscriber.ts +++ b/sdk/src/accounts/pollingUserAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, UserAccountEvents, UserAccountSubscriber, -} from '../types'; -import { Connection } from '../../bankrun/bankrunConnection'; +} from './types'; +import { Connection } from '../bankrun/bankrunConnection'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { UserAccount } from '../../types'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { UserAccount } from '../types'; +import { BulkAccountLoader } from './bulkAccountLoader'; export class PollingUserAccountSubscriber implements UserAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/userStatsAccount/pollingUserStatsAccountSubscriber.ts b/sdk/src/accounts/pollingUserStatsAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/userStatsAccount/pollingUserStatsAccountSubscriber.ts rename to sdk/src/accounts/pollingUserStatsAccountSubscriber.ts index 7a8cba922b..d146c31dc8 100644 --- a/sdk/src/accounts/userStatsAccount/pollingUserStatsAccountSubscriber.ts +++ b/sdk/src/accounts/pollingUserStatsAccountSubscriber.ts @@ -3,13 +3,13 @@ import { NotSubscribedError, UserStatsAccountSubscriber, UserStatsAccountEvents, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { PublicKey } from '@solana/web3.js'; -import { UserStatsAccount } from '../../types'; -import { BulkAccountLoader } from '../bulkAccountLoader/bulkAccountLoader'; +import { UserStatsAccount } from '../types'; +import { BulkAccountLoader } from './bulkAccountLoader'; export class PollingUserStatsAccountSubscriber implements UserStatsAccountSubscriber diff --git a/sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader.ts b/sdk/src/accounts/testBulkAccountLoader.ts similarity index 100% rename from sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader.ts rename to sdk/src/accounts/testBulkAccountLoader.ts diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index 5891da768f..a4b4425253 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -13,6 +13,7 @@ import { EventEmitter } from 'events'; import { Context, PublicKey } from '@solana/web3.js'; import { Account } from '@solana/spl-token'; import { OracleInfo, OraclePriceData } from '../oracles/types'; +import { User } from '../user'; import { ChannelOptions, CommitmentLevel } from '../isomorphic/grpc'; export interface AccountSubscriber { @@ -102,6 +103,12 @@ export interface UserAccountEvents { error: (e: Error) => void; } +export interface UserEvents { + userUpdate: (payload: User) => void; + update: void; + error: (e: Error) => void; +} + export interface UserAccountSubscriber { eventEmitter: StrictEventEmitter; isSubscribed: boolean; diff --git a/sdk/src/accounts/baseSubscribers/webSocketAccountSubscriber.ts b/sdk/src/accounts/webSocketAccountSubscriber.ts similarity index 99% rename from sdk/src/accounts/baseSubscribers/webSocketAccountSubscriber.ts rename to sdk/src/accounts/webSocketAccountSubscriber.ts index 08198c9a44..deee456166 100644 --- a/sdk/src/accounts/baseSubscribers/webSocketAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketAccountSubscriber.ts @@ -3,10 +3,10 @@ import { BufferAndSlot, AccountSubscriber, ResubOpts, -} from '../types'; +} from './types'; import { AnchorProvider, Program } from '@coral-xyz/anchor'; import { AccountInfo, Commitment, Context, PublicKey } from '@solana/web3.js'; -import { capitalize } from '../utils'; +import { capitalize } from './utils'; import * as Buffer from 'buffer'; export class WebSocketAccountSubscriber implements AccountSubscriber { diff --git a/sdk/src/accounts/driftClientAccount/webSocketDriftClientAccountSubscriber.ts b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts similarity index 96% rename from sdk/src/accounts/driftClientAccount/webSocketDriftClientAccountSubscriber.ts rename to sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts index 4d05f4ef2a..c49d425796 100644 --- a/sdk/src/accounts/driftClientAccount/webSocketDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts @@ -6,12 +6,8 @@ import { DriftClientAccountSubscriber, NotSubscribedError, ResubOpts, -} from '../types'; -import { - PerpMarketAccount, - SpotMarketAccount, - StateAccount, -} from '../../types'; +} from './types'; +import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; @@ -21,17 +17,17 @@ import { getPerpMarketPublicKeySync, getSpotMarketPublicKey, getSpotMarketPublicKeySync, -} from '../../addresses/pda'; -import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; +} from '../addresses/pda'; +import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { OracleInfo, OraclePriceData } from '../../oracles/types'; -import { OracleClientCache } from '../../oracles/oracleClientCache'; +import { OracleInfo, OraclePriceData } from '../oracles/types'; +import { OracleClientCache } from '../oracles/oracleClientCache'; import * as Buffer from 'buffer'; -import { QUOTE_ORACLE_PRICE_DATA } from '../../oracles/quoteAssetOracleClient'; -import { findAllMarketAndOracles } from '../../config'; -import { findDelistedPerpMarketsAndOracles } from '../utils'; -import { getOracleId } from '../../oracles/oracleId'; -import { OracleSource } from '../../types'; +import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient'; +import { findAllMarketAndOracles } from '../config'; +import { findDelistedPerpMarketsAndOracles } from './utils'; +import { getOracleId } from '../oracles/oracleId'; +import { OracleSource } from '../types'; const ORACLE_DEFAULT_ID = getOracleId( PublicKey.default, diff --git a/sdk/src/accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber.ts b/sdk/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts similarity index 95% rename from sdk/src/accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber.ts rename to sdk/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts index 735535b2d2..0742f6b1fa 100644 --- a/sdk/src/accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts @@ -4,13 +4,13 @@ import { NotSubscribedError, HighLeverageModeConfigAccountEvents, HighLeverageModeConfigAccountSubscriber, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; -import { HighLeverageModeConfig } from '../../types'; +import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; +import { HighLeverageModeConfig } from '../types'; export class WebSocketHighLeverageModeConfigAccountSubscriber implements HighLeverageModeConfigAccountSubscriber diff --git a/sdk/src/accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber.ts b/sdk/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts similarity index 95% rename from sdk/src/accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber.ts rename to sdk/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts index 0f0844481c..bc8042a7ae 100644 --- a/sdk/src/accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts @@ -4,13 +4,13 @@ import { NotSubscribedError, InsuranceFundStakeAccountEvents, InsuranceFundStakeAccountSubscriber, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; -import { InsuranceFundStake } from '../../types'; +import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; +import { InsuranceFundStake } from '../types'; export class WebSocketInsuranceFundStakeAccountSubscriber implements InsuranceFundStakeAccountSubscriber diff --git a/sdk/src/accounts/programAccount/webSocketProgramAccountSubscriber.ts b/sdk/src/accounts/webSocketProgramAccountSubscriber.ts similarity index 99% rename from sdk/src/accounts/programAccount/webSocketProgramAccountSubscriber.ts rename to sdk/src/accounts/webSocketProgramAccountSubscriber.ts index 63c7111d0f..d860477583 100644 --- a/sdk/src/accounts/programAccount/webSocketProgramAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketProgramAccountSubscriber.ts @@ -1,4 +1,4 @@ -import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from '../types'; +import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types'; import { AnchorProvider, Program } from '@coral-xyz/anchor'; import { Commitment, diff --git a/sdk/src/accounts/userAccount/webSocketUserAccountSubscriber.ts b/sdk/src/accounts/webSocketUserAccountSubscriber.ts similarity index 94% rename from sdk/src/accounts/userAccount/webSocketUserAccountSubscriber.ts rename to sdk/src/accounts/webSocketUserAccountSubscriber.ts index f2ad1fe772..79d6f9401f 100644 --- a/sdk/src/accounts/userAccount/webSocketUserAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketUserAccountSubscriber.ts @@ -5,13 +5,13 @@ import { UserAccountEvents, UserAccountSubscriber, ResubOpts, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; -import { UserAccount } from '../../types'; +import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; +import { UserAccount } from '../types'; export class WebSocketUserAccountSubscriber implements UserAccountSubscriber { isSubscribed: boolean; diff --git a/sdk/src/accounts/userStatsAccount/webSocketUserStatsAccountSubsriber.ts b/sdk/src/accounts/webSocketUserStatsAccountSubsriber.ts similarity index 93% rename from sdk/src/accounts/userStatsAccount/webSocketUserStatsAccountSubsriber.ts rename to sdk/src/accounts/webSocketUserStatsAccountSubsriber.ts index 29b0e7f1cc..fdc0be5507 100644 --- a/sdk/src/accounts/userStatsAccount/webSocketUserStatsAccountSubsriber.ts +++ b/sdk/src/accounts/webSocketUserStatsAccountSubsriber.ts @@ -5,13 +5,13 @@ import { UserStatsAccountSubscriber, UserStatsAccountEvents, ResubOpts, -} from '../types'; +} from './types'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { Commitment, PublicKey } from '@solana/web3.js'; -import { WebSocketAccountSubscriber } from '../baseSubscribers/webSocketAccountSubscriber'; -import { UserStatsAccount } from '../../types'; +import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber'; +import { UserStatsAccount } from '../types'; export class WebSocketUserStatsAccountSubscriber implements UserStatsAccountSubscriber diff --git a/sdk/src/auctionSubscriber/auctionSubscriber.ts b/sdk/src/auctionSubscriber/auctionSubscriber.ts index 75b35965e1..8ffc893713 100644 --- a/sdk/src/auctionSubscriber/auctionSubscriber.ts +++ b/sdk/src/auctionSubscriber/auctionSubscriber.ts @@ -5,7 +5,7 @@ import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserAccount } from '../types'; import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js'; -import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { ResubOpts } from '../accounts/types'; export class AuctionSubscriber { diff --git a/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts b/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts index ca83f90e49..75212d376e 100644 --- a/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts +++ b/sdk/src/auctionSubscriber/auctionSubscriberGrpc.ts @@ -5,9 +5,9 @@ import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { UserAccount } from '../types'; import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js'; -import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; -import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; export class AuctionSubscriberGrpc { private driftClient: DriftClient; diff --git a/sdk/src/config/index.ts b/sdk/src/config.ts similarity index 96% rename from sdk/src/config/index.ts rename to sdk/src/config.ts index 8231f130c7..0ff967b7ad 100644 --- a/sdk/src/config/index.ts +++ b/sdk/src/config.ts @@ -1,25 +1,24 @@ import { ConfirmOptions, PublicKey } from '@solana/web3.js'; -import { PerpMarketAccount, SpotMarketAccount } from '../types'; +import { PerpMarketAccount, SpotMarketAccount } from './types'; import { DevnetPerpMarkets, MainnetPerpMarkets, PerpMarketConfig, PerpMarkets, -} from '../constants/perpMarkets'; +} from './constants/perpMarkets'; import { SpotMarketConfig, SpotMarkets, DevnetSpotMarkets, MainnetSpotMarkets, -} from '../constants/spotMarkets'; -import { OracleInfo } from '../oracles/types'; +} from './constants/spotMarkets'; +import { OracleInfo } from './oracles/types'; import { Program, ProgramAccount } from '@coral-xyz/anchor'; import { ON_DEMAND_DEVNET_PID, ON_DEMAND_MAINNET_PID, } from '@switchboard-xyz/on-demand'; -import { getOracleId } from '../oracles/oracleId'; -import { DriftEnv } from './types'; +import { getOracleId } from './oracles/oracleId'; type DriftConfig = { ENV: DriftEnv; @@ -42,6 +41,8 @@ type DriftConfig = { SB_ON_DEMAND_PID: PublicKey; }; +export type DriftEnv = 'devnet' | 'mainnet-beta'; + export const DRIFT_PROGRAM_ID = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH'; export const DRIFT_ORACLE_RECEIVER_ID = 'G6EoTTTgpkNBtVXo96EQp2m6uwwVh2Kt6YidjkmQqoha'; diff --git a/sdk/src/config/types.ts b/sdk/src/config/types.ts deleted file mode 100644 index 392e884514..0000000000 --- a/sdk/src/config/types.ts +++ /dev/null @@ -1 +0,0 @@ -export type DriftEnv = 'devnet' | 'mainnet-beta'; diff --git a/sdk/src/constants/perpMarkets.ts b/sdk/src/constants/perpMarkets.ts index 97a0bb2d20..d23acbe648 100644 --- a/sdk/src/constants/perpMarkets.ts +++ b/sdk/src/constants/perpMarkets.ts @@ -1,6 +1,6 @@ import { PublicKey } from '@solana/web3.js'; import { OracleSource } from '../types'; -import { DriftEnv } from '../config/types'; +import { DriftEnv } from '../config'; export type PerpMarketConfig = { fullName?: string; diff --git a/sdk/src/constants/spotMarkets.ts b/sdk/src/constants/spotMarkets.ts index c6a99df420..a732ea2270 100644 --- a/sdk/src/constants/spotMarkets.ts +++ b/sdk/src/constants/spotMarkets.ts @@ -11,7 +11,7 @@ import { } from './numericConstants'; import { OracleSource } from '../types'; import { BN } from '@coral-xyz/anchor'; -import { DriftEnv } from '../config/types'; +import { DriftEnv } from '../config'; export type SpotMarketConfig = { symbol: string; diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index bd55479ec1..d679a89ce1 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -1,5 +1,4 @@ -import { NodeList } from './NodeList'; -import { getOrderSignature } from './utils'; +import { getOrderSignature, NodeList } from './NodeList'; import { BN } from '@coral-xyz/anchor'; import { BASE_PRECISION, @@ -9,8 +8,8 @@ import { ZERO, } from '../constants/numericConstants'; import { decodeName } from '../userName'; -import { DLOBNode, DLOBNodeType } from './DLOBNode'; -import { IDriftClient } from '../driftClient/types'; +import { DLOBNode, DLOBNodeType, TriggerOrderNode } from './DLOBNode'; +import { DriftClient } from '../driftClient'; import { calculateOrderBaseAssetAmount, getLimitPrice, @@ -34,18 +33,9 @@ import { } from '../types'; import { isUserProtectedMaker } from '../math/userStatus'; import { OraclePriceData } from '../oracles/types'; -import { - DLOBFilterFcn, - DLOBOrders, - IDLOB, - MarketNodeLists, - NodeToFill, - NodeToTrigger, - OrderBookCallback, - ProtectMakerParamsMap, -} from './types'; +import { ProtectMakerParamsMap } from './types'; import { SlotSubscriber } from '../slot/SlotSubscriber'; -import { IUserMap } from '../userMap/types'; +import { UserMap } from '../userMap/userMap'; import { PublicKey } from '@solana/web3.js'; import { ammPaused, exchangePaused, fillPaused } from '../math/exchangeStatus'; import { @@ -60,6 +50,59 @@ import { import { isFallbackAvailableLiquiditySource } from '../math/auction'; import { convertToNumber } from '../math/conversion'; +export type DLOBOrder = { user: PublicKey; order: Order }; +export type DLOBOrders = DLOBOrder[]; + +export type MarketNodeLists = { + restingLimit: { + ask: NodeList<'restingLimit'>; + bid: NodeList<'restingLimit'>; + }; + floatingLimit: { + ask: NodeList<'floatingLimit'>; + bid: NodeList<'floatingLimit'>; + }; + protectedFloatingLimit: { + ask: NodeList<'protectedFloatingLimit'>; + bid: NodeList<'protectedFloatingLimit'>; + }; + takingLimit: { + ask: NodeList<'takingLimit'>; + bid: NodeList<'takingLimit'>; + }; + market: { + ask: NodeList<'market'>; + bid: NodeList<'market'>; + }; + trigger: { + above: NodeList<'trigger'>; + below: NodeList<'trigger'>; + }; + signedMsg: { + ask: NodeList<'signedMsg'>; + bid: NodeList<'signedMsg'>; + }; +}; + +type OrderBookCallback = () => void; + +/** + * Receives a DLOBNode and is expected to return true if the node should + * be taken into account when generating, or false otherwise. + * + * Currently used in functions that rely on getBestNode + */ +export type DLOBFilterFcn = (node: DLOBNode) => boolean; + +export type NodeToFill = { + node: DLOBNode; + makerNodes: DLOBNode[]; +}; + +export type NodeToTrigger = { + node: TriggerOrderNode; +}; + const SUPPORTED_ORDER_TYPES = [ 'market', 'limit', @@ -68,7 +111,7 @@ const SUPPORTED_ORDER_TYPES = [ 'oracle', ]; -export class DLOB implements IDLOB { +export class DLOB { openOrders = new Map>(); orderLists = new Map>(); maxSlotForRestingLimitOrders = 0; @@ -123,7 +166,7 @@ export class DLOB implements IDLOB { * @returns a promise that resolves when the DLOB is initialized */ public async initFromUserMap( - userMap: IUserMap, + userMap: UserMap, slot: number ): Promise { if (this.initialized) { @@ -1585,7 +1628,7 @@ export class DLOB implements IDLOB { } public printTop( - driftClient: IDriftClient, + driftClient: DriftClient, slotSubscriber: SlotSubscriber, marketIndex: number, marketType: MarketType diff --git a/sdk/src/dlob/DLOBNode.ts b/sdk/src/dlob/DLOBNode.ts index fb5429eaeb..a789583ce1 100644 --- a/sdk/src/dlob/DLOBNode.ts +++ b/sdk/src/dlob/DLOBNode.ts @@ -8,7 +8,7 @@ import { getLimitPrice } from '../math/orders'; import { isVariant, Order, ProtectedMakerParams } from '../types'; import { OraclePriceData } from '../oracles/types'; import { convertToNumber } from '../math/conversion'; -import { getOrderSignature } from './utils'; +import { getOrderSignature } from './NodeList'; export interface DLOBNode { getPrice(oraclePriceData: OraclePriceData, slot: number): BN; diff --git a/sdk/src/dlob/DLOBSubscriber.ts b/sdk/src/dlob/DLOBSubscriber.ts index 111d231dc6..431302c011 100644 --- a/sdk/src/dlob/DLOBSubscriber.ts +++ b/sdk/src/dlob/DLOBSubscriber.ts @@ -5,10 +5,10 @@ import { DLOBSource, DLOBSubscriberEvents, DLOBSubscriptionConfig, - IDLOB, ProtectMakerParamsMap, SlotSource, } from './types'; +import { DriftClient } from '../driftClient'; import { isVariant, MarketType } from '../types'; import { DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS, @@ -19,15 +19,14 @@ import { L3OrderBook, } from './orderBookLevels'; import { getProtectedMakerParamsMap } from '../math/protectedMakerParams'; -import { IDriftClient } from '../driftClient/types'; export class DLOBSubscriber { - driftClient: IDriftClient; + driftClient: DriftClient; dlobSource: DLOBSource; slotSource: SlotSource; updateFrequency: number; intervalId?: ReturnType; - dlob: IDLOB; + dlob: DLOB; public eventEmitter: StrictEventEmitter; protectedMakerView: boolean; constructor(config: DLOBSubscriptionConfig) { @@ -70,7 +69,7 @@ export class DLOBSubscriber { ); } - public getDLOB(): IDLOB { + public getDLOB(): DLOB { return this.dlob; } diff --git a/sdk/src/dlob/NodeList.ts b/sdk/src/dlob/NodeList.ts index b53bf5eea5..4462446b3b 100644 --- a/sdk/src/dlob/NodeList.ts +++ b/sdk/src/dlob/NodeList.ts @@ -5,11 +5,17 @@ import { ProtectedMakerParams, } from '../types'; import { createNode, DLOBNode, DLOBNodeMap } from './DLOBNode'; -import { getOrderSignature } from './utils'; -import { BN } from '../index'; +import { BN } from '@coral-xyz/anchor'; export type SortDirection = 'asc' | 'desc'; +export function getOrderSignature( + orderId: number, + userAccount: string +): string { + return `${userAccount.toString()}-${orderId.toString()}`; +} + export interface DLOBNodeGenerator { getGenerator(): Generator; } diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index c5f290c2c4..41022f4998 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -24,8 +24,7 @@ import { } from '../types'; import { OraclePriceData } from '../oracles/types'; import { PublicKey } from '@solana/web3.js'; -import { standardizePrice } from '../math/orders'; -import { standardizeBaseAssetAmount } from '../math/utils'; +import { standardizeBaseAssetAmount, standardizePrice } from '../math/orders'; type liquiditySource = | 'serum' diff --git a/sdk/src/dlob/types.ts b/sdk/src/dlob/types.ts index 0e05c17184..6b3c158436 100644 --- a/sdk/src/dlob/types.ts +++ b/sdk/src/dlob/types.ts @@ -1,29 +1,10 @@ -import { - MarketType, - Order, - PerpMarketAccount, - PositionDirection, - ProtectedMakerParams, - SpotMarketAccount, - StateAccount, -} from '../types'; +import { DLOB } from './DLOB'; +import { DriftClient } from '../driftClient'; +import { ProtectedMakerParams } from '../types'; import { MarketTypeStr } from '../types'; -import { PublicKey } from '@solana/web3.js'; -import { DLOBNode, DLOBNodeType, TriggerOrderNode } from './DLOBNode'; -import { BN } from '@coral-xyz/anchor'; -import { OraclePriceData } from '../oracles/types'; -import { NodeList } from './NodeList'; -import { SlotSubscriber } from '../slot/SlotSubscriber'; -import { - L2OrderBook, - L2OrderBookGenerator, - L3OrderBook, -} from './orderBookLevels'; -import { IDriftClient } from '../driftClient/types'; -import { IUserMap } from '../userMap/types'; export type DLOBSubscriptionConfig = { - driftClient: IDriftClient; + driftClient: DriftClient; dlobSource: DLOBSource; slotSource: SlotSource; updateFrequency: number; @@ -31,7 +12,7 @@ export type DLOBSubscriptionConfig = { }; export interface DLOBSubscriberEvents { - update: (dlob: IDLOB) => void; + update: (dlob: DLOB) => void; error: (e: Error) => void; } @@ -39,7 +20,7 @@ export interface DLOBSource { getDLOB( slot: number, protectedMakerParamsMap?: ProtectMakerParamsMap - ): Promise; + ): Promise; } export interface SlotSource { @@ -49,406 +30,3 @@ export interface SlotSource { export type ProtectMakerParamsMap = { [marketType in MarketTypeStr]: Map; }; - -export type DLOBOrder = { user: PublicKey; order: Order }; -export type DLOBOrders = DLOBOrder[]; - -export type MarketNodeLists = { - restingLimit: { - ask: NodeList<'restingLimit'>; - bid: NodeList<'restingLimit'>; - }; - floatingLimit: { - ask: NodeList<'floatingLimit'>; - bid: NodeList<'floatingLimit'>; - }; - protectedFloatingLimit: { - ask: NodeList<'protectedFloatingLimit'>; - bid: NodeList<'protectedFloatingLimit'>; - }; - takingLimit: { - ask: NodeList<'takingLimit'>; - bid: NodeList<'takingLimit'>; - }; - market: { - ask: NodeList<'market'>; - bid: NodeList<'market'>; - }; - trigger: { - above: NodeList<'trigger'>; - below: NodeList<'trigger'>; - }; - signedMsg: { - ask: NodeList<'signedMsg'>; - bid: NodeList<'signedMsg'>; - }; -}; - -export type OrderBookCallback = () => void; - -/** - * Receives a DLOBNode and is expected to return true if the node should - * be taken into account when generating, or false otherwise. - * - * Currently used in functions that rely on getBestNode - */ -export type DLOBFilterFcn = (node: DLOBNode) => boolean; - -export type NodeToFill = { - node: DLOBNode; - makerNodes: DLOBNode[]; -}; - -export type NodeToTrigger = { - node: TriggerOrderNode; -}; - -export interface IDLOB { - // Properties - openOrders: Map>; - orderLists: Map>; - maxSlotForRestingLimitOrders: number; - initialized: boolean; - protectedMakerParamsMap: ProtectMakerParamsMap; - - // Methods - clear(): void; - - /** - * initializes a new DLOB instance - * - * @returns a promise that resolves when the DLOB is initialized - */ - initFromUserMap(userMap: IUserMap, slot: number): Promise; - - insertOrder( - order: Order, - userAccount: string, - slot: number, - isUserProtectedMaker: boolean, - baseAssetAmount: BN, - onInsert?: OrderBookCallback - ): void; - - insertSignedMsgOrder( - order: Order, - userAccount: string, - isUserProtectedMaker: boolean, - baseAssetAmount: BN, - onInsert?: OrderBookCallback - ): void; - - addOrderList(marketType: MarketTypeStr, marketIndex: number): void; - - delete( - order: Order, - userAccount: PublicKey, - slot: number, - isUserProtectedMaker: boolean, - onDelete?: OrderBookCallback - ): void; - - getListForOnChainOrder( - order: Order, - slot: number, - isProtectedMaker: boolean - ): NodeList | undefined; - - updateRestingLimitOrders(slot: number): void; - - updateRestingLimitOrdersForMarketType( - slot: number, - marketTypeStr: MarketTypeStr - ): void; - - getOrder(orderId: number, userAccount: PublicKey): Order | undefined; - - findNodesToFill( - marketIndex: number, - fallbackBid: BN | undefined, - fallbackAsk: BN | undefined, - slot: number, - ts: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - stateAccount: StateAccount, - marketAccount: PerpMarketAccount | SpotMarketAccount - ): NodeToFill[]; - - getMakerRebate( - marketType: MarketType, - stateAccount: StateAccount, - marketAccount: PerpMarketAccount | SpotMarketAccount - ): { makerRebateNumerator: number; makerRebateDenominator: number }; - - mergeNodesToFill( - restingLimitOrderNodesToFill: NodeToFill[], - takingOrderNodesToFill: NodeToFill[] - ): NodeToFill[]; - - findRestingLimitOrderNodesToFill( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - isAmmPaused: boolean, - minAuctionDuration: number, - makerRebateNumerator: number, - makerRebateDenominator: number, - fallbackAsk: BN | undefined, - fallbackBid: BN | undefined - ): NodeToFill[]; - - findTakingNodesToFill( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - isAmmPaused: boolean, - minAuctionDuration: number, - fallbackAsk: BN | undefined, - fallbackBid?: BN | undefined - ): NodeToFill[]; - - findTakingNodesCrossingMakerNodes( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - takerNodeGenerator: Generator, - makerNodeGeneratorFn: ( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData - ) => Generator, - doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean - ): NodeToFill[]; - - findNodesCrossingFallbackLiquidity( - marketType: MarketType, - slot: number, - oraclePriceData: OraclePriceData, - nodeGenerator: Generator, - doesCross: (nodePrice: BN | undefined) => boolean, - minAuctionDuration: number - ): NodeToFill[]; - - findExpiredNodesToFill( - marketIndex: number, - ts: number, - marketType: MarketType, - slot?: BN - ): NodeToFill[]; - - getTakingBids( - marketIndex: number, - marketType: MarketType, - slot: number, - oraclePriceData: OraclePriceData, - filterFcn?: DLOBFilterFcn - ): Generator; - - getTakingAsks( - marketIndex: number, - marketType: MarketType, - slot: number, - oraclePriceData: OraclePriceData, - filterFcn?: DLOBFilterFcn - ): Generator; - - getRestingLimitAsks( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - filterFcn?: DLOBFilterFcn - ): Generator; - - getRestingLimitBids( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - filterFcn?: DLOBFilterFcn - ): Generator; - - /** - * This will look at both the taking and resting limit asks - * @param marketIndex - * @param fallbackAsk - * @param slot - * @param marketType - * @param oraclePriceData - * @param filterFcn - */ - getAsks( - marketIndex: number, - fallbackAsk: BN | undefined, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - filterFcn?: DLOBFilterFcn - ): Generator; - - /** - * This will look at both the taking and resting limit bids - * @param marketIndex - * @param fallbackBid - * @param slot - * @param marketType - * @param oraclePriceData - * @param filterFcn - */ - getBids( - marketIndex: number, - fallbackBid: BN | undefined, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, - filterFcn?: DLOBFilterFcn - ): Generator; - - findCrossingRestingLimitOrders( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData - ): NodeToFill[]; - - determineMakerAndTaker( - askNode: DLOBNode, - bidNode: DLOBNode - ): { takerNode: DLOBNode; makerNode: DLOBNode } | undefined; - - getBestAsk( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData - ): BN | undefined; - - getBestBid( - marketIndex: number, - slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData - ): BN | undefined; - - getStopLosses( - marketIndex: number, - marketType: MarketType, - direction: PositionDirection - ): Generator; - - getStopLossMarkets( - marketIndex: number, - marketType: MarketType, - direction: PositionDirection - ): Generator; - - getStopLossLimits( - marketIndex: number, - marketType: MarketType, - direction: PositionDirection - ): Generator; - - getTakeProfits( - marketIndex: number, - marketType: MarketType, - direction: PositionDirection - ): Generator; - - getTakeProfitMarkets( - marketIndex: number, - marketType: MarketType, - direction: PositionDirection - ): Generator; - - getTakeProfitLimits( - marketIndex: number, - marketType: MarketType, - direction: PositionDirection - ): Generator; - - findNodesToTrigger( - marketIndex: number, - slot: number, - oraclePrice: BN, - marketType: MarketType, - stateAccount: StateAccount - ): NodeToTrigger[]; - - printTop( - driftClient: IDriftClient, - slotSubscriber: SlotSubscriber, - marketIndex: number, - marketType: MarketType - ): void; - - getDLOBOrders(): DLOBOrders; - - getNodeLists(): Generator>; - - /** - * Get an L2 view of the order book for a given market. - * - * @param marketIndex - * @param marketType - * @param slot - * @param oraclePriceData - * @param depth how many levels of the order book to return - * @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber} - */ - getL2(params: { - marketIndex: number; - marketType: MarketType; - slot: number; - oraclePriceData: OraclePriceData; - depth: number; - fallbackL2Generators?: L2OrderBookGenerator[]; - }): L2OrderBook; - - /** - * Get an L3 view of the order book for a given market. Does not include fallback liquidity sources - * - * @param marketIndex - * @param marketType - * @param slot - * @param oraclePriceData - */ - getL3(params: { - marketIndex: number; - marketType: MarketType; - slot: number; - oraclePriceData: OraclePriceData; - }): L3OrderBook; - - /** - * - * @param param.marketIndex the index of the market - * @param param.marketType the type of the market - * @param param.baseAmount the base amount in to estimate - * @param param.orderDirection the direction of the trade - * @param param.slot current slot for estimating dlob node price - * @param param.oraclePriceData the oracle price data - * @returns the estimated quote amount filled: QUOTE_PRECISION - */ - estimateFillWithExactBaseAmount(params: { - marketIndex: number; - marketType: MarketType; - baseAmount: BN; - orderDirection: PositionDirection; - slot: number; - oraclePriceData: OraclePriceData; - }): BN; - - getBestMakers(params: { - marketIndex: number; - marketType: MarketType; - direction: PositionDirection; - slot: number; - oraclePriceData: OraclePriceData; - numMakers: number; - }): PublicKey[]; -} diff --git a/sdk/src/dlob/utils.ts b/sdk/src/dlob/utils.ts deleted file mode 100644 index 736f4f68ef..0000000000 --- a/sdk/src/dlob/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function getOrderSignature( - orderId: number, - userAccount: string -): string { - return `${userAccount.toString()}-${orderId.toString()}`; -} diff --git a/sdk/src/driftClient/index.ts b/sdk/src/driftClient.ts similarity index 99% rename from sdk/src/driftClient/index.ts rename to sdk/src/driftClient.ts index 1bede7816e..2fbfc96d1a 100644 --- a/sdk/src/driftClient/index.ts +++ b/sdk/src/driftClient.ts @@ -64,8 +64,8 @@ import { ProtectedMakerModeConfig, SignedMsgOrderParamsDelegateMessage, TokenProgramFlag, -} from '../types'; -import driftIDL from '../idl/drift.json'; +} from './types'; +import driftIDL from './idl/drift.json'; import { AccountMeta, @@ -87,7 +87,7 @@ import { VersionedTransaction, } from '@solana/web3.js'; -import { TokenFaucet } from '../tokenFaucet'; +import { TokenFaucet } from './tokenFaucet'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; import { @@ -112,14 +112,14 @@ import { getUserStatsAccountPublicKey, getSignedMsgWsDelegatesAccountPublicKey, getIfRebalanceConfigPublicKey, -} from '../addresses/pda'; +} from './addresses/pda'; import { DataAndSlot, DelistedMarketSetting, DriftClientAccountEvents, DriftClientAccountSubscriber, -} from '../accounts/types'; -import { TxSender, TxSigAndSlot } from '../tx/types'; +} from './accounts/types'; +import { TxSender, TxSigAndSlot } from './tx/types'; import { BASE_PRECISION, GOV_SPOT_MARKET_INDEX, @@ -129,45 +129,44 @@ import { QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, ZERO, -} from '../constants/numericConstants'; -import { findDirectionToClose, positionIsAvailable } from '../math/position'; -import { getSignedTokenAmount, getTokenAmount } from '../math/spotBalance'; -import { decodeName, DEFAULT_USER_NAME, encodeName } from '../userName'; -import { OraclePriceData } from '../oracles/types'; -import { DriftClientConfig } from '../driftClientConfig'; -import { PollingDriftClientAccountSubscriber } from '../accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; -import { WebSocketDriftClientAccountSubscriber } from '../accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; -import { RetryTxSender } from '../tx/retryTxSender'; -import { User } from '../user'; -import { UserSubscriptionConfig } from '../user/types'; +} from './constants/numericConstants'; +import { findDirectionToClose, positionIsAvailable } from './math/position'; +import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance'; +import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName'; +import { OraclePriceData } from './oracles/types'; +import { DriftClientConfig } from './driftClientConfig'; +import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; +import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; +import { RetryTxSender } from './tx/retryTxSender'; +import { User } from './user'; +import { UserSubscriptionConfig } from './userConfig'; import { configs, DRIFT_ORACLE_RECEIVER_ID, DEFAULT_CONFIRMATION_OPTS, DRIFT_PROGRAM_ID, + DriftEnv, PYTH_LAZER_STORAGE_ACCOUNT_KEY, -} from '../config'; -import { DriftEnv } from '../config/types'; -import { WRAPPED_SOL_MINT } from '../constants/spotMarkets'; -import { IUserStats } from '../userStats/types'; -import { UserStats } from '../userStats'; -import { isSpotPositionAvailable } from '../math/spotPosition'; -import { calculateMarketMaxAvailableInsurance } from '../math/market'; -import { fetchUserStatsAccount } from '../accounts/fetch'; -import { castNumberToSpotPrecision } from '../math/spotMarket'; +} from './config'; +import { WRAPPED_SOL_MINT } from './constants/spotMarkets'; +import { UserStats } from './userStats'; +import { isSpotPositionAvailable } from './math/spotPosition'; +import { calculateMarketMaxAvailableInsurance } from './math/market'; +import { fetchUserStatsAccount } from './accounts/fetch'; +import { castNumberToSpotPrecision } from './math/spotMarket'; import { JupiterClient, QuoteResponse, SwapMode, -} from '../jupiter/jupiterClient'; -import { getNonIdleUserFilter } from '../memcmp'; -import { UserStatsSubscriptionConfig } from '../userStatsConfig'; -import { getMarinadeDepositIx, getMarinadeFinanceProgram } from '../marinade'; -import { getOrderParams, isUpdateHighLeverageMode } from '../orderParams'; -import { numberToSafeBN } from '../math/utils'; -import { TransactionParamProcessor } from '../tx/txParamProcessor'; -import { isOracleValid, trimVaaSignatures } from '../math/oracles'; -import { TxHandler } from '../tx/txHandler'; +} from './jupiter/jupiterClient'; +import { getNonIdleUserFilter } from './memcmp'; +import { UserStatsSubscriptionConfig } from './userStatsConfig'; +import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade'; +import { getOrderParams, isUpdateHighLeverageMode } from './orderParams'; +import { numberToSafeBN } from './math/utils'; +import { TransactionParamProcessor } from './tx/txParamProcessor'; +import { isOracleValid, trimVaaSignatures } from './math/oracles'; +import { TxHandler } from './tx/txHandler'; import { DEFAULT_RECEIVER_PROGRAM_ID, wormholeCoreBridgeIdl, @@ -179,22 +178,21 @@ import { } from '@pythnetwork/pyth-solana-receiver/lib/address'; import { WormholeCoreBridgeSolana } from '@pythnetwork/pyth-solana-receiver/lib/idl/wormhole_core_bridge_solana'; import { PythSolanaReceiver } from '@pythnetwork/pyth-solana-receiver/lib/idl/pyth_solana_receiver'; -import { getFeedIdUint8Array, trimFeedId } from '../util/pythOracleUtils'; -import { createMinimalEd25519VerifyIx } from '../util/ed25519Utils'; +import { getFeedIdUint8Array, trimFeedId } from './util/pythOracleUtils'; +import { createMinimalEd25519VerifyIx } from './util/ed25519Utils'; import { createNativeInstructionDiscriminatorBuffer, isVersionedTransaction, -} from '../tx/utils'; -import pythSolanaReceiverIdl from '../idl/pyth_solana_receiver.json'; +} from './tx/utils'; +import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json'; import { asV0Tx, PullFeed, AnchorUtils } from '@switchboard-xyz/on-demand'; -import { gprcDriftClientAccountSubscriber } from '../accounts/driftClientAccount/grpcDriftClientAccountSubscriber'; +import { gprcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber'; import nacl from 'tweetnacl'; -import { Slothash } from '../slot/SlothashSubscriber'; -import { getOracleId } from '../oracles/oracleId'; -import { SignedMsgOrderParams } from '../types'; +import { Slothash } from './slot/SlothashSubscriber'; +import { getOracleId } from './oracles/oracleId'; +import { SignedMsgOrderParams } from './types'; import { sha256 } from '@noble/hashes/sha256'; -import { getOracleConfidenceFromMMOracleData } from '../oracles/utils'; -import { IDriftClient } from './types'; +import { getOracleConfidenceFromMMOracleData } from './oracles/utils'; type RemainingAccountParams = { userAccounts: UserAccount[]; @@ -209,7 +207,7 @@ type RemainingAccountParams = { * # DriftClient * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more. */ -export class DriftClient implements IDriftClient { +export class DriftClient { connection: Connection; wallet: IWallet; public program: Program; @@ -218,7 +216,7 @@ export class DriftClient implements IDriftClient { opts?: ConfirmOptions; useHotWalletAdmin?: boolean; users = new Map(); - userStats?: IUserStats; + userStats?: UserStats; activeSubAccountId: number; userAccountSubscriptionConfig: UserSubscriptionConfig; userStatsAccountSubscriptionConfig: UserStatsSubscriptionConfig; @@ -2052,7 +2050,7 @@ export class DriftClient implements IDriftClient { ); } - public getUserStats(): IUserStats { + public getUserStats(): UserStats { return this.userStats; } diff --git a/sdk/src/driftClient/types.ts b/sdk/src/driftClient/types.ts deleted file mode 100644 index 08ae38611e..0000000000 --- a/sdk/src/driftClient/types.ts +++ /dev/null @@ -1,2204 +0,0 @@ -import { AnchorProvider, BN, Program, ProgramAccount } from '@coral-xyz/anchor'; -import { Program as Program30, Idl as Idl30 } from '@coral-xyz/anchor-30'; -import { - AddressLookupTableAccount, - BlockhashWithExpiryBlockHeight, - ConfirmOptions, - Connection, - PublicKey, - Signer, - TransactionInstruction, - TransactionSignature, - TransactionVersion, - Transaction, - VersionedTransaction, - AccountMeta, - Keypair, -} from '@solana/web3.js'; -import { - DriftClientMetricsEvents, - HighLeverageModeConfig, - IWallet, - MakerInfo, - MarketType, - OpenbookV2FulfillmentConfigAccount, - OptionalOrderParams, - OracleSource, - Order, - OrderParams, - PerpMarketAccount, - PerpMarketExtendedInfo, - PhoenixV1FulfillmentConfigAccount, - PlaceAndTakeOrderSuccessCondition, - PositionDirection, - ReferrerInfo, - ReferrerNameAccount, - SerumV3FulfillmentConfigAccount, - SettlePnlMode, - SpotMarketAccount, - SpotPosition, - StateAccount, - SignedMsgOrderParamsMessage, - TakerInfo, - TxParams, - UserAccount, - UserStatsAccount, - ProtectedMakerModeConfig, - SignedMsgOrderParamsDelegateMessage, - SignedMsgOrderParams, - SwapReduceOnly, - OrderTriggerCondition, - ModifyOrderPolicy, -} from '../types'; -import { EventEmitter } from 'events'; -import StrictEventEmitter from 'strict-event-emitter-types'; -import { - DataAndSlot, - DriftClientAccountEvents, - DriftClientAccountSubscriber, -} from '../accounts/types'; -import { TxSender, TxSigAndSlot } from '../tx/types'; -import { OraclePriceData } from '../oracles/types'; -import { UserSubscriptionConfig } from '../user/types'; -import { DriftEnv } from '../config/types'; -import { IUserStats } from '../userStats/types'; -import { UserStatsSubscriptionConfig } from '../userStatsConfig'; -import { PythSolanaReceiver } from '@pythnetwork/pyth-solana-receiver/lib/idl/pyth_solana_receiver'; -import { WormholeCoreBridgeSolana } from '@pythnetwork/pyth-solana-receiver/lib/idl/wormhole_core_bridge_solana'; -import { Slothash } from '../slot/SlothashSubscriber'; -import { TokenFaucet } from '../tokenFaucet'; -import { - JupiterClient, - QuoteResponse, - SwapMode, -} from '../jupiter/jupiterClient'; -import { TxHandler } from '../tx/txHandler'; -import { IUser } from '../user/types'; - -type RemainingAccountParams = { - userAccounts: UserAccount[]; - writablePerpMarketIndexes?: number[]; - writableSpotMarketIndexes?: number[]; - readablePerpMarketIndex?: number | number[]; - readableSpotMarketIndexes?: number[]; - useMarketLastSlotCache?: boolean; -}; - -export interface IDriftClient { - // Properties - connection: Connection; - wallet: IWallet; - program: Program; - provider: AnchorProvider; - env: DriftEnv; - opts?: ConfirmOptions; - useHotWalletAdmin?: boolean; - users: Map; - userStats?: IUserStats; - activeSubAccountId: number; - userAccountSubscriptionConfig: UserSubscriptionConfig; - userStatsAccountSubscriptionConfig: UserStatsSubscriptionConfig; - accountSubscriber: DriftClientAccountSubscriber; - eventEmitter: StrictEventEmitter; - metricsEventEmitter: StrictEventEmitter< - EventEmitter, - DriftClientMetricsEvents - >; - txSender: TxSender; - perpMarketLastSlotCache: Map; - spotMarketLastSlotCache: Map; - mustIncludePerpMarketIndexes: Set; - mustIncludeSpotMarketIndexes: Set; - authority: PublicKey; - /** @deprecated use marketLookupTables */ - marketLookupTable: PublicKey; - /** @deprecated use lookupTableAccounts */ - lookupTableAccount: AddressLookupTableAccount; - marketLookupTables: PublicKey[]; - lookupTableAccounts: AddressLookupTableAccount[]; - includeDelegates?: boolean; - authoritySubAccountMap?: Map; - skipLoadUsers?: boolean; - txVersion: TransactionVersion; - txParams: TxParams; - enableMetricsEvents?: boolean; - receiverProgram?: Program; - wormholeProgram?: Program; - sbOnDemandProgramdId: PublicKey; - sbOnDemandProgram?: Program30; - sbProgramFeedConfigs?: Map; - statePublicKey?: PublicKey; - signerPublicKey?: PublicKey; - - get isSubscribed(): boolean; - set isSubscribed(val: boolean); - - // Methods - getUserMapKey(subAccountId: number, authority: PublicKey): string; - createUser( - subAccountId: number, - accountSubscriptionConfig: UserSubscriptionConfig, - authority?: PublicKey - ): IUser; - subscribe(): Promise; - subscribeUsers(): Promise[]; - - /** - * Forces the accountSubscriber to fetch account updates from rpc - */ - fetchAccounts(): Promise; - - unsubscribe(): Promise; - unsubscribeUsers(): Promise[]; - getStatePublicKey(): Promise; - getSignerPublicKey(): PublicKey; - getStateAccount(): StateAccount; - - /** - * Forces a fetch to rpc before returning accounts. Useful for anchor tests. - */ - forceGetStateAccount(): Promise; - - getPerpMarketAccount(marketIndex: number): PerpMarketAccount | undefined; - - /** - * Forces a fetch to rpc before returning accounts. Useful for anchor tests. - * @param marketIndex - */ - forceGetPerpMarketAccount( - marketIndex: number - ): Promise; - - getPerpMarketAccounts(): PerpMarketAccount[]; - getSpotMarketAccount(marketIndex: number): SpotMarketAccount | undefined; - - /** - * Forces a fetch to rpc before returning accounts. Useful for anchor tests. - * @param marketIndex - */ - forceGetSpotMarketAccount( - marketIndex: number - ): Promise; - - getSpotMarketAccounts(): SpotMarketAccount[]; - getQuoteSpotMarketAccount(): SpotMarketAccount; - getOraclePriceDataAndSlot( - oraclePublicKey: PublicKey, - oracleSource: OracleSource - ): DataAndSlot | undefined; - getSerumV3FulfillmentConfig( - serumMarket: PublicKey - ): Promise; - getSerumV3FulfillmentConfigs(): Promise; - getPhoenixV1FulfillmentConfig( - phoenixMarket: PublicKey - ): Promise; - getPhoenixV1FulfillmentConfigs(): Promise< - PhoenixV1FulfillmentConfigAccount[] - >; - getOpenbookV2FulfillmentConfig( - openbookMarket: PublicKey - ): Promise; - getOpenbookV2FulfillmentConfigs(): Promise< - OpenbookV2FulfillmentConfigAccount[] - >; - - /** @deprecated use fetchAllLookupTableAccounts() */ - fetchMarketLookupTableAccount(): Promise; - - fetchAllLookupTableAccounts(): Promise; - - /** - * Update the wallet to use for drift transactions and linked user account - * @param newWallet - * @param subAccountIds - * @param activeSubAccountId - * @param includeDelegates - */ - updateWallet( - newWallet: IWallet, - subAccountIds?: number[], - activeSubAccountId?: number, - includeDelegates?: boolean, - authoritySubaccountMap?: Map - ): Promise; - - /** - * Update the subscribed accounts to a given authority, while leaving the - * connected wallet intact. This allows a user to emulate another user's - * account on the UI and sign permissionless transactions with their own wallet. - * @param emulateAuthority - */ - emulateAccount(emulateAuthority: PublicKey): Promise; - - switchActiveUser(subAccountId: number, authority?: PublicKey): Promise; - addUser( - subAccountId: number, - authority?: PublicKey, - userAccount?: UserAccount - ): Promise; - - /** - * Adds and subscribes to users based on params set by the constructor or by updateWallet. - */ - addAndSubscribeToUsers(authority?: PublicKey): Promise; - - /** - * Returns the instructions to initialize a user account and the public key of the user account. - * @param subAccountId - * @param name - * @param referrerInfo - * @returns [instructions, userAccountPublicKey] - */ - getInitializeUserAccountIxs( - subAccountId?: number, - name?: string, - referrerInfo?: ReferrerInfo, - poolId?: number - ): Promise<[TransactionInstruction[], PublicKey]>; - - /** - * Initializes a user account and returns the transaction signature and the public key of the user account. - * @param subAccountId - * @param name - * @param referrerInfo - * @param txParams - * @returns [transactionSignature, userAccountPublicKey] - */ - initializeUserAccount( - subAccountId?: number, - name?: string, - referrerInfo?: ReferrerInfo, - txParams?: TxParams - ): Promise<[TransactionSignature, PublicKey]>; - - getInitializeUserStatsIx(): Promise; - initializeSignedMsgUserOrders( - authority: PublicKey, - numOrders: number, - txParams?: TxParams - ): Promise<[TransactionSignature, PublicKey]>; - getInitializeSignedMsgUserOrdersAccountIx( - authority: PublicKey, - numOrders: number - ): Promise<[PublicKey, TransactionInstruction]>; - resizeSignedMsgUserOrders( - authority: PublicKey, - numOrders: number, - userSubaccountId?: number, - txParams?: TxParams - ): Promise; - getResizeSignedMsgUserOrdersInstruction( - authority: PublicKey, - numOrders: number, - userSubaccountId?: number - ): Promise; - initializeSignedMsgWsDelegatesAccount( - authority: PublicKey, - delegates?: PublicKey[], - txParams?: TxParams - ): Promise; - getInitializeSignedMsgWsDelegatesAccountIx( - authority: PublicKey, - delegates?: PublicKey[] - ): Promise; - addSignedMsgWsDelegate( - authority: PublicKey, - delegate: PublicKey, - txParams?: TxParams - ): Promise; - getAddSignedMsgWsDelegateIx( - authority: PublicKey, - delegate: PublicKey - ): Promise; - removeSignedMsgWsDelegate( - authority: PublicKey, - delegate: PublicKey, - txParams?: TxParams - ): Promise; - getRemoveSignedMsgWsDelegateIx( - authority: PublicKey, - delegate: PublicKey - ): Promise; - initializeFuelOverflow(authority?: PublicKey): Promise; - getInitializeFuelOverflowIx( - authority?: PublicKey - ): Promise; - sweepFuel(authority?: PublicKey): Promise; - getSweepFuelIx(authority?: PublicKey): Promise; - getNextSubAccountId(): Promise; - initializeReferrerName(name: string): Promise; - updateUserName( - name: string, - subAccountId?: number - ): Promise; - updateUserCustomMarginRatio( - updates: { marginRatio: number; subAccountId: number }[], - txParams?: TxParams - ): Promise; - getUpdateUserCustomMarginRatioIx( - marginRatio: number, - subAccountId?: number - ): Promise; - getUpdateUserMarginTradingEnabledIx( - marginTradingEnabled: boolean, - subAccountId?: number, - userAccountPublicKey?: PublicKey - ): Promise; - updateUserMarginTradingEnabled( - updates: { marginTradingEnabled: boolean; subAccountId: number }[] - ): Promise; - updateUserDelegate( - delegate: PublicKey, - subAccountId?: number - ): Promise; - updateUserAdvancedLp( - updates: { advancedLp: boolean; subAccountId: number }[] - ): Promise; - getUpdateAdvancedDlpIx( - advancedLp: boolean, - subAccountId: number - ): Promise; - updateUserReduceOnly( - updates: { reduceOnly: boolean; subAccountId: number }[] - ): Promise; - getUpdateUserReduceOnlyIx( - reduceOnly: boolean, - subAccountId: number - ): Promise; - updateUserPoolId( - updates: { poolId: number; subAccountId: number }[] - ): Promise; - getUpdateUserPoolIdIx( - poolId: number, - subAccountId: number - ): Promise; - fetchAllUserAccounts( - includeIdle?: boolean - ): Promise[]>; - getUserAccountsForDelegate(delegate: PublicKey): Promise; - getUserAccountsAndAddressesForAuthority( - authority: PublicKey - ): Promise[]>; - getUserAccountsForAuthority(authority: PublicKey): Promise; - getReferredUserStatsAccountsByReferrer( - referrer: PublicKey - ): Promise; - getReferrerNameAccountsForAuthority( - authority: PublicKey - ): Promise; - deleteUser( - subAccountId?: number, - txParams?: TxParams - ): Promise; - getUserDeletionIx( - userAccountPublicKey: PublicKey - ): Promise; - forceDeleteUser( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - txParams?: TxParams - ): Promise; - getForceDeleteUserIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount - ): Promise; - deleteSignedMsgUserOrders(txParams?: TxParams): Promise; - getSignedMsgUserOrdersDeletionIx( - authority: PublicKey - ): Promise; - - /** - * Checks if a SignedMsg User Orders account exists for the given authority. - * The account pubkey is derived using the program ID and authority as seeds. - * Makes an RPC call to check if the account exists on-chain. - * - * @param authority The authority public key to check for - * @returns Promise that resolves to true if the account exists, false otherwise - */ - isSignedMsgUserOrdersAccountInitialized( - authority: PublicKey - ): Promise; - - reclaimRent( - subAccountId?: number, - txParams?: TxParams - ): Promise; - getReclaimRentIx( - userAccountPublicKey: PublicKey - ): Promise; - getUser(subAccountId?: number, authority?: PublicKey): IUser; - hasUser(subAccountId?: number, authority?: PublicKey): boolean; - getUsers(): IUser[]; - getUserStats(): IUserStats; - fetchReferrerNameAccount( - name: string - ): Promise; - getUserStatsAccountPublicKey(): PublicKey; - getUserAccountPublicKey( - subAccountId?: number, - authority?: PublicKey - ): Promise; - getUserAccount( - subAccountId?: number, - authority?: PublicKey - ): UserAccount | undefined; - - /** - * Forces a fetch to rpc before returning accounts. Useful for anchor tests. - * @param subAccountId - */ - forceGetUserAccount( - subAccountId?: number, - authority?: PublicKey - ): Promise; - - getUserAccountAndSlot( - subAccountId?: number, - authority?: PublicKey - ): DataAndSlot | undefined; - getSpotPosition( - marketIndex: number, - subAccountId?: number - ): SpotPosition | undefined; - getQuoteAssetTokenAmount(): BN; - - /** - * Returns the token amount for a given market. The spot market precision is based on the token mint decimals. - * Positive if it is a deposit, negative if it is a borrow. - * @param marketIndex - */ - getTokenAmount(marketIndex: number): BN; - - /** - * Converts an amount to the spot precision for a given market. The spot market precision is based on the token mint decimals. - * @param marketIndex - * @param amount - */ - convertToSpotPrecision(marketIndex: number, amount: BN | number): BN; - - /** - * Converts an amount to the perp precision. The perp market precision is {@link BASE_PRECISION} (1e9). - * @param amount - */ - convertToPerpPrecision(amount: BN | number): BN; - - /** - * Converts an amount to the price precision. The perp market precision is {@link PRICE_PRECISION} (1e6). - * @param amount - */ - convertToPricePrecision(amount: BN | number): BN; - - /** - * Each drift instruction must include perp and sport market accounts in the ix remaining accounts. - * Use this function to force a subset of markets to be included in the remaining accounts for every ix - * - * @param perpMarketIndexes - * @param spotMarketIndexes - */ - mustIncludeMarketsInIx(params: { - perpMarketIndexes: number[]; - spotMarketIndexes: number[]; - }): void; - - getRemainingAccounts(params: RemainingAccountParams): AccountMeta[]; - addPerpMarketToRemainingAccountMaps( - marketIndex: number, - writable: boolean, - oracleAccountMap: Map, - spotMarketAccountMap: Map, - perpMarketAccountMap: Map - ): void; - addSpotMarketToRemainingAccountMaps( - marketIndex: number, - writable: boolean, - oracleAccountMap: Map, - spotMarketAccountMap: Map - ): void; - getRemainingAccountMapsForUsers(userAccounts: UserAccount[]): { - oracleAccountMap: Map; - spotMarketAccountMap: Map; - perpMarketAccountMap: Map; - }; - getOrder(orderId: number, subAccountId?: number): Order | undefined; - getOrderByUserId( - userOrderId: number, - subAccountId?: number - ): Order | undefined; - - /** - * Get the associated token address for the given spot market - * @param marketIndex - * @param useNative - * @param tokenProgram - */ - getAssociatedTokenAccount( - marketIndex: number, - useNative?: boolean, - tokenProgram?: PublicKey - ): Promise; - - createAssociatedTokenAccountIdempotentInstruction( - account: PublicKey, - payer: PublicKey, - owner: PublicKey, - mint: PublicKey, - tokenProgram?: PublicKey - ): TransactionInstruction; - getDepositTxnIx( - amount: BN, - marketIndex: number, - associatedTokenAccount: PublicKey, - subAccountId?: number, - reduceOnly?: boolean - ): Promise; - createDepositTxn( - amount: BN, - marketIndex: number, - associatedTokenAccount: PublicKey, - subAccountId?: number, - reduceOnly?: boolean, - txParams?: TxParams, - initSwiftAccount?: boolean - ): Promise; - - /** - * Deposit funds into the given spot market - * - * @param amount to deposit - * @param marketIndex spot market index to deposit into - * @param associatedTokenAccount can be the wallet public key if using native sol - * @param subAccountId subaccountId to deposit - * @param reduceOnly if true, deposit must not increase account risk - */ - deposit( - amount: BN, - marketIndex: number, - associatedTokenAccount: PublicKey, - subAccountId?: number, - reduceOnly?: boolean, - txParams?: TxParams, - initSwiftAccount?: boolean - ): Promise; - - getDepositInstruction( - amount: BN, - marketIndex: number, - userTokenAccount: PublicKey, - subAccountId?: number, - reduceOnly?: boolean, - userInitialized?: boolean - ): Promise; - getWrappedSolAccountCreationIxs( - amount: BN, - includeRent?: boolean - ): Promise<{ - ixs: TransactionInstruction[]; - /** @deprecated - this array is always going to be empty, in the current implementation */ signers: Signer[]; - pubkey: PublicKey; - }>; - getTokenProgramForSpotMarket(spotMarketAccount: SpotMarketAccount): PublicKey; - isToken2022(spotMarketAccount: SpotMarketAccount): boolean; - isTransferHook(spotMarketAccount: SpotMarketAccount): boolean; - addTokenMintToRemainingAccounts( - spotMarketAccount: SpotMarketAccount, - remainingAccounts: AccountMeta[] - ): void; - addExtraAccountMetasToRemainingAccounts( - mint: PublicKey, - remainingAccounts: AccountMeta[] - ): Promise; - getAssociatedTokenAccountCreationIx( - tokenMintAddress: PublicKey, - associatedTokenAddress: PublicKey, - tokenProgram: PublicKey - ): TransactionInstruction; - createInitializeUserAccountAndDepositCollateralIxs( - amount: BN, - userTokenAccount: PublicKey, - marketIndex?: number, - subAccountId?: number, - name?: string, - fromSubAccountId?: number, - referrerInfo?: ReferrerInfo, - donateAmount?: BN, - customMaxMarginRatio?: number, - poolId?: number - ): Promise<{ - ixs: TransactionInstruction[]; - userAccountPublicKey: PublicKey; - }>; - createInitializeUserAccountAndDepositCollateral( - amount: BN, - userTokenAccount: PublicKey, - marketIndex?: number, - subAccountId?: number, - name?: string, - fromSubAccountId?: number, - referrerInfo?: ReferrerInfo, - donateAmount?: BN, - txParams?: TxParams, - customMaxMarginRatio?: number, - poolId?: number - ): Promise<[Transaction | VersionedTransaction, PublicKey]>; - - /** - * Creates the User account for a user, and deposits some initial collateral - * @param amount - * @param userTokenAccount - * @param marketIndex - * @param subAccountId - * @param name - * @param fromSubAccountId - * @param referrerInfo - * @param donateAmount - * @param txParams - * @returns - */ - initializeUserAccountAndDepositCollateral( - amount: BN, - userTokenAccount: PublicKey, - marketIndex?: number, - subAccountId?: number, - name?: string, - fromSubAccountId?: number, - referrerInfo?: ReferrerInfo, - donateAmount?: BN, - txParams?: TxParams, - customMaxMarginRatio?: number, - poolId?: number - ): Promise<[TransactionSignature, PublicKey]>; - - initializeUserAccountForDevnet( - subAccountId?: number, - name?: string, - marketIndex?: number, - tokenFaucet?: TokenFaucet, - amount?: BN, - referrerInfo?: ReferrerInfo, - txParams?: TxParams - ): Promise<[TransactionSignature, PublicKey]>; - getWithdrawalIxs( - amount: BN, - marketIndex: number, - associatedTokenAddress: PublicKey, - reduceOnly?: boolean, - subAccountId?: number, - updateFuel?: boolean - ): Promise; - - /** - * Withdraws from a user account. If deposit doesn't already exist, creates a borrow - * @param amount - * @param marketIndex - * @param associatedTokenAddress - the token account to withdraw to. can be the wallet public key if using native sol - * @param reduceOnly - */ - withdraw( - amount: BN, - marketIndex: number, - associatedTokenAddress: PublicKey, - reduceOnly?: boolean, - subAccountId?: number, - txParams?: TxParams, - updateFuel?: boolean - ): Promise; - - withdrawAllDustPositions( - subAccountId?: number, - txParams?: TxParams, - opts?: { dustPositionCountCallback?: (count: number) => void } - ): Promise; - getWithdrawIx( - amount: BN, - marketIndex: number, - userTokenAccount: PublicKey, - reduceOnly?: boolean, - subAccountId?: number - ): Promise; - - /** - * Withdraws from the fromSubAccount and deposits into the toSubAccount - * @param amount - * @param marketIndex - * @param fromSubAccountId - * @param toSubAccountId - * @param txParams - */ - transferDeposit( - amount: BN, - marketIndex: number, - fromSubAccountId: number, - toSubAccountId: number, - txParams?: TxParams - ): Promise; - - getTransferDepositIx( - amount: BN, - marketIndex: number, - fromSubAccountId: number, - toSubAccountId: number - ): Promise; - transferPools( - depositFromMarketIndex: number, - depositToMarketIndex: number, - borrowFromMarketIndex: number, - borrowToMarketIndex: number, - depositAmount: BN | undefined, - borrowAmount: BN | undefined, - fromSubAccountId: number, - toSubAccountId: number, - txParams?: TxParams - ): Promise; - getTransferPoolsIx( - depositFromMarketIndex: number, - depositToMarketIndex: number, - borrowFromMarketIndex: number, - borrowToMarketIndex: number, - depositAmount: BN | undefined, - borrowAmount: BN | undefined, - fromSubAccountId: number, - toSubAccountId: number, - isToNewSubAccount?: boolean - ): Promise; - transferPerpPosition( - fromSubAccountId: number, - toSubAccountId: number, - marketIndex: number, - amount: BN, - txParams?: TxParams - ): Promise; - getTransferPerpPositionIx( - fromSubAccountId: number, - toSubAccountId: number, - marketIndex: number, - amount: BN - ): Promise; - updateSpotMarketCumulativeInterest( - marketIndex: number, - txParams?: TxParams - ): Promise; - updateSpotMarketCumulativeInterestIx( - marketIndex: number - ): Promise; - settleLP( - settleeUserAccountPublicKey: PublicKey, - marketIndex: number, - txParams?: TxParams - ): Promise; - settleLPIx( - settleeUserAccountPublicKey: PublicKey, - marketIndex: number - ): Promise; - removePerpLpShares( - marketIndex: number, - sharesToBurn?: BN, - txParams?: TxParams, - subAccountId?: number - ): Promise; - removePerpLpSharesInExpiringMarket( - marketIndex: number, - userAccountPublicKey: PublicKey, - sharesToBurn?: BN, - txParams?: TxParams - ): Promise; - getRemovePerpLpSharesInExpiringMarket( - marketIndex: number, - userAccountPublicKey: PublicKey, - sharesToBurn?: BN - ): Promise; - getRemovePerpLpSharesIx( - marketIndex: number, - sharesToBurn?: BN, - subAccountId?: number - ): Promise; - addPerpLpShares( - amount: BN, - marketIndex: number, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getAddPerpLpSharesIx( - amount: BN, - marketIndex: number, - subAccountId?: number - ): Promise; - getQuoteValuePerLpShare(marketIndex: number): BN; - - /** - * @deprecated use {@link placePerpOrder} or {@link placeAndTakePerpOrder} instead - */ - openPosition( - direction: PositionDirection, - amount: BN, - marketIndex: number, - limitPrice?: BN, - subAccountId?: number - ): Promise; - - sendSignedTx( - tx: Transaction | VersionedTransaction, - opts?: ConfirmOptions - ): Promise; - prepareMarketOrderTxs( - orderParams: OptionalOrderParams, - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - makerInfo?: MakerInfo | MakerInfo[], - txParams?: TxParams, - bracketOrdersParams?: OptionalOrderParams[], - referrerInfo?: ReferrerInfo, - cancelExistingOrders?: boolean, - settlePnl?: boolean - ): Promise<{ - cancelExistingOrdersTx?: Transaction | VersionedTransaction; - settlePnlTx?: Transaction | VersionedTransaction; - fillTx?: Transaction | VersionedTransaction; - marketOrderTx: Transaction | VersionedTransaction; - }>; - - /** - * Sends a market order and returns a signed tx which can fill the order against the vamm, which the caller can use to fill their own order if required. - * @param orderParams - * @param userAccountPublicKey - * @param userAccount - * @param makerInfo - * @param txParams - * @param bracketOrdersParams - * @param cancelExistingOrders - Builds and returns an extra transaciton to cancel the existing orders in the same perp market. Intended use is to auto-cancel TP/SL orders when closing a position. Ignored if orderParams.marketType is not MarketType.PERP - * @returns - */ - sendMarketOrderAndGetSignedFillTx( - orderParams: OptionalOrderParams, - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - makerInfo?: MakerInfo | MakerInfo[], - txParams?: TxParams, - bracketOrdersParams?: OptionalOrderParams[], - referrerInfo?: ReferrerInfo, - cancelExistingOrders?: boolean, - settlePnl?: boolean - ): Promise<{ - txSig: TransactionSignature; - signedFillTx?: Transaction; - signedCancelExistingOrdersTx?: Transaction; - signedSettlePnlTx?: Transaction; - }>; - - placePerpOrder( - orderParams: OptionalOrderParams, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getPlacePerpOrderIx( - orderParams: OptionalOrderParams, - subAccountId?: number, - depositToTradeArgs?: { - isMakingNewAccount: boolean; - depositMarketIndex: number; - } - ): Promise; - updateAMMs( - marketIndexes: number[], - txParams?: TxParams - ): Promise; - getUpdateAMMsIx(marketIndexes: number[]): Promise; - settleExpiredMarket( - marketIndex: number, - txParams?: TxParams - ): Promise; - getSettleExpiredMarketIx( - marketIndex: number - ): Promise; - settleExpiredMarketPoolsToRevenuePool( - marketIndex: number, - txParams?: TxParams - ): Promise; - getSettleExpiredMarketPoolsToRevenuePoolIx( - perpMarketIndex: number - ): Promise; - cancelOrder( - orderId?: number, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getCancelOrderIx( - orderId?: number, - subAccountId?: number - ): Promise; - cancelOrderByUserId( - userOrderId: number, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getCancelOrderByUserIdIx( - userOrderId: number, - subAccountId?: number - ): Promise; - cancelOrdersByIds( - orderIds?: number[], - txParams?: TxParams, - subAccountId?: number - ): Promise; - getCancelOrdersByIdsIx( - orderIds?: number[], - subAccountId?: number - ): Promise; - cancelOrders( - marketType?: MarketType, - marketIndex?: number, - direction?: PositionDirection, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getCancelOrdersIx( - marketType: MarketType | null, - marketIndex: number | null, - direction: PositionDirection | null, - subAccountId?: number - ): Promise; - cancelAndPlaceOrders( - cancelOrderParams: { - marketType?: MarketType; - marketIndex?: number; - direction?: PositionDirection; - }, - placeOrderParams: OrderParams[], - txParams?: TxParams, - subAccountId?: number - ): Promise; - placeOrders( - params: OrderParams[], - txParams?: TxParams, - subAccountId?: number, - optionalIxs?: TransactionInstruction[] - ): Promise; - preparePlaceOrdersTx( - params: OrderParams[], - txParams?: TxParams, - subAccountId?: number, - optionalIxs?: TransactionInstruction[] - ): Promise<{ - placeOrdersTx: Transaction | VersionedTransaction; - }>; - getPlaceOrdersIx( - params: OptionalOrderParams[], - subAccountId?: number - ): Promise; - fillPerpOrder( - userAccountPublicKey: PublicKey, - user: UserAccount, - order?: Pick, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - txParams?: TxParams, - fillerSubAccountId?: number, - fillerAuthority?: PublicKey - ): Promise; - getFillPerpOrderIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - order: Pick, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - fillerSubAccountId?: number, - isSignedMsg?: boolean, - fillerAuthority?: PublicKey - ): Promise; - getRevertFillIx(fillerPublicKey?: PublicKey): Promise; - placeSpotOrder( - orderParams: OptionalOrderParams, - txParams?: TxParams, - subAccountId?: number - ): Promise; - preparePlaceSpotOrderTx( - orderParams: OptionalOrderParams, - txParams?: TxParams, - subAccountId?: number - ): Promise<{ - placeSpotOrderTx: Transaction | VersionedTransaction; - }>; - getPlaceSpotOrderIx( - orderParams: OptionalOrderParams, - subAccountId?: number - ): Promise; - fillSpotOrder( - userAccountPublicKey: PublicKey, - user: UserAccount, - order?: Pick, - fulfillmentConfig?: - | SerumV3FulfillmentConfigAccount - | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - txParams?: TxParams - ): Promise; - getFillSpotOrderIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - order?: Pick, - fulfillmentConfig?: - | SerumV3FulfillmentConfigAccount - | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - fillerPublicKey?: PublicKey - ): Promise; - addSpotFulfillmentAccounts( - marketIndex: number, - remainingAccounts: AccountMeta[], - fulfillmentConfig?: - | SerumV3FulfillmentConfigAccount - | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount - ): void; - addSerumRemainingAccounts( - marketIndex: number, - remainingAccounts: AccountMeta[], - fulfillmentConfig: SerumV3FulfillmentConfigAccount - ): void; - addPhoenixRemainingAccounts( - marketIndex: number, - remainingAccounts: AccountMeta[], - fulfillmentConfig: PhoenixV1FulfillmentConfigAccount - ): void; - addOpenbookRemainingAccounts( - marketIndex: number, - remainingAccounts: AccountMeta[], - fulfillmentConfig: OpenbookV2FulfillmentConfigAccount - ): void; - - /** - * Swap tokens in drift account using jupiter - * @param jupiterClient jupiter client to find routes and jupiter instructions - * @param outMarketIndex the market index of the token you're buying - * @param inMarketIndex the market index of the token you're selling - * @param outAssociatedTokenAccount the token account to receive the token being sold on jupiter - * @param inAssociatedTokenAccount the token account to - * @param amount the amount of TokenIn, regardless of swapMode - * @param slippageBps the max slippage passed to jupiter api - * @param swapMode jupiter swapMode (ExactIn or ExactOut), default is ExactIn - * @param route the jupiter route to use for the swap - * @param reduceOnly specify if In or Out token on the drift account must reduceOnly, checked at end of swap - * @param v6 pass in the quote response from Jupiter quote's API (deprecated, use quote instead) - * @param quote pass in the quote response from Jupiter quote's API - * @param txParams - */ - swap({ - jupiterClient, - outMarketIndex, - inMarketIndex, - outAssociatedTokenAccount, - inAssociatedTokenAccount, - amount, - slippageBps, - swapMode, - reduceOnly, - txParams, - v6, - quote, - onlyDirectRoutes, - }: { - jupiterClient: JupiterClient; - outMarketIndex: number; - inMarketIndex: number; - outAssociatedTokenAccount?: PublicKey; - inAssociatedTokenAccount?: PublicKey; - amount: BN; - slippageBps?: number; - swapMode?: SwapMode; - reduceOnly?: SwapReduceOnly; - txParams?: TxParams; - onlyDirectRoutes?: boolean; - v6?: { - quote?: QuoteResponse; - }; - quote?: QuoteResponse; - }): Promise; - - getJupiterSwapIxV6({ - jupiterClient, - outMarketIndex, - inMarketIndex, - outAssociatedTokenAccount, - inAssociatedTokenAccount, - amount, - slippageBps, - swapMode, - onlyDirectRoutes, - quote, - reduceOnly, - userAccountPublicKey, - }: { - jupiterClient: JupiterClient; - outMarketIndex: number; - inMarketIndex: number; - outAssociatedTokenAccount?: PublicKey; - inAssociatedTokenAccount?: PublicKey; - amount: BN; - slippageBps?: number; - swapMode?: SwapMode; - onlyDirectRoutes?: boolean; - quote?: QuoteResponse; - reduceOnly?: SwapReduceOnly; - userAccountPublicKey?: PublicKey; - }): Promise<{ - ixs: TransactionInstruction[]; - lookupTables: AddressLookupTableAccount[]; - }>; - - /** - * Get the drift begin_swap and end_swap instructions - * - * @param outMarketIndex the market index of the token you're buying - * @param inMarketIndex the market index of the token you're selling - * @param amountIn the amount of the token to sell - * @param inTokenAccount the token account to move the tokens being sold - * @param outTokenAccount the token account to receive the tokens being bought - * @param limitPrice the limit price of the swap - * @param reduceOnly - * @param userAccountPublicKey optional, specify a custom userAccountPublicKey to use instead of getting the current user account; can be helpful if the account is being created within the current tx - */ - getSwapIx({ - outMarketIndex, - inMarketIndex, - amountIn, - inTokenAccount, - outTokenAccount, - limitPrice, - reduceOnly, - userAccountPublicKey, - }: { - outMarketIndex: number; - inMarketIndex: number; - amountIn: BN; - inTokenAccount: PublicKey; - outTokenAccount: PublicKey; - limitPrice?: BN; - reduceOnly?: SwapReduceOnly; - userAccountPublicKey?: PublicKey; - }): Promise<{ - beginSwapIx: TransactionInstruction; - endSwapIx: TransactionInstruction; - }>; - - stakeForMSOL(params: { amount: BN }): Promise; - getStakeForMSOLIx({ - amount, - userAccountPublicKey, - }: { - amount: BN; - userAccountPublicKey?: PublicKey; - }): Promise; - triggerOrder( - userAccountPublicKey: PublicKey, - user: UserAccount, - order: Order, - txParams?: TxParams, - fillerPublicKey?: PublicKey - ): Promise; - getTriggerOrderIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - order: Order, - fillerPublicKey?: PublicKey - ): Promise; - forceCancelOrders( - userAccountPublicKey: PublicKey, - user: UserAccount, - txParams?: TxParams, - fillerPublicKey?: PublicKey - ): Promise; - getForceCancelOrdersIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - fillerPublicKey?: PublicKey - ): Promise; - updateUserIdle( - userAccountPublicKey: PublicKey, - user: UserAccount, - txParams?: TxParams, - fillerPublicKey?: PublicKey - ): Promise; - getUpdateUserIdleIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - fillerPublicKey?: PublicKey - ): Promise; - logUserBalances( - userAccountPublicKey: PublicKey, - txParams?: TxParams - ): Promise; - getLogUserBalancesIx( - userAccountPublicKey: PublicKey - ): Promise; - updateUserFuelBonus( - userAccountPublicKey: PublicKey, - user: UserAccount, - userAuthority: PublicKey, - txParams?: TxParams - ): Promise; - getUpdateUserFuelBonusIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - userAuthority: PublicKey - ): Promise; - updateUserStatsReferrerStatus( - userAuthority: PublicKey, - txParams?: TxParams - ): Promise; - getUpdateUserStatsReferrerStatusIx( - userAuthority: PublicKey - ): Promise; - updateUserOpenOrdersCount( - userAccountPublicKey: PublicKey, - user: UserAccount, - txParams?: TxParams, - fillerPublicKey?: PublicKey - ): Promise; - getUpdateUserOpenOrdersCountIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - fillerPublicKey?: PublicKey - ): Promise; - placeAndTakePerpOrder( - orderParams: OptionalOrderParams, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - successCondition?: PlaceAndTakeOrderSuccessCondition, - auctionDurationPercentage?: number, - txParams?: TxParams, - subAccountId?: number - ): Promise; - preparePlaceAndTakePerpOrderWithAdditionalOrders( - orderParams: OptionalOrderParams, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - bracketOrdersParams?: OptionalOrderParams[], - txParams?: TxParams, - subAccountId?: number, - cancelExistingOrders?: boolean, - settlePnl?: boolean, - exitEarlyIfSimFails?: boolean, - auctionDurationPercentage?: number, - optionalIxs?: TransactionInstruction[] - ): Promise<{ - placeAndTakeTx: Transaction | VersionedTransaction; - cancelExistingOrdersTx: Transaction | VersionedTransaction; - settlePnlTx: Transaction | VersionedTransaction; - }>; - placeAndTakePerpWithAdditionalOrders( - orderParams: OptionalOrderParams, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - bracketOrdersParams?: OptionalOrderParams[], - txParams?: TxParams, - subAccountId?: number, - cancelExistingOrders?: boolean, - settlePnl?: boolean, - exitEarlyIfSimFails?: boolean - ): Promise<{ - txSig: TransactionSignature; - signedCancelExistingOrdersTx?: Transaction; - signedSettlePnlTx?: Transaction; - }>; - getPlaceAndTakePerpOrderIx( - orderParams: OptionalOrderParams, - makerInfo?: MakerInfo | MakerInfo[], - referrerInfo?: ReferrerInfo, - successCondition?: PlaceAndTakeOrderSuccessCondition, - auctionDurationPercentage?: number, - subAccountId?: number - ): Promise; - placeAndMakePerpOrder( - orderParams: OptionalOrderParams, - takerInfo: TakerInfo, - referrerInfo?: ReferrerInfo, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getPlaceAndMakePerpOrderIx( - orderParams: OptionalOrderParams, - takerInfo: TakerInfo, - referrerInfo?: ReferrerInfo, - subAccountId?: number - ): Promise; - signSignedMsgOrderParamsMessage( - orderParamsMessage: - | SignedMsgOrderParamsMessage - | SignedMsgOrderParamsDelegateMessage, - delegateSigner?: boolean - ): SignedMsgOrderParams; - - /* - * Borsh encode signedMsg taker order params - */ - encodeSignedMsgOrderParamsMessage( - orderParamsMessage: - | SignedMsgOrderParamsMessage - | SignedMsgOrderParamsDelegateMessage, - delegateSigner?: boolean - ): Buffer; - - /* - * Decode signedMsg taker order params from borsh buffer - */ - decodeSignedMsgOrderParamsMessage( - encodedMessage: Buffer, - delegateSigner?: boolean - ): SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage; - - signMessage(message: Uint8Array, keypair?: Keypair): Buffer; - placeSignedMsgTakerOrder( - signedSignedMsgOrderParams: SignedMsgOrderParams, - marketIndex: number, - takerInfo: { - taker: PublicKey; - takerStats: PublicKey; - takerUserAccount: UserAccount; - signingAuthority: PublicKey; - }, - precedingIxs?: TransactionInstruction[], - overrideCustomIxIndex?: number, - txParams?: TxParams - ): Promise; - getPlaceSignedMsgTakerPerpOrderIxs( - signedSignedMsgOrderParams: SignedMsgOrderParams, - marketIndex: number, - takerInfo: { - taker: PublicKey; - takerStats: PublicKey; - takerUserAccount: UserAccount; - signingAuthority: PublicKey; - }, - precedingIxs?: TransactionInstruction[], - overrideCustomIxIndex?: number - ): Promise; - placeAndMakeSignedMsgPerpOrder( - signedSignedMsgOrderParams: SignedMsgOrderParams, - signedMsgOrderUuid: Uint8Array, - takerInfo: { - taker: PublicKey; - takerStats: PublicKey; - takerUserAccount: UserAccount; - signingAuthority: PublicKey; - }, - orderParams: OptionalOrderParams, - referrerInfo?: ReferrerInfo, - txParams?: TxParams, - subAccountId?: number, - precedingIxs?: TransactionInstruction[], - overrideCustomIxIndex?: number - ): Promise; - getPlaceAndMakeSignedMsgPerpOrderIxs( - signedSignedMsgOrderParams: SignedMsgOrderParams, - signedMsgOrderUuid: Uint8Array, - takerInfo: { - taker: PublicKey; - takerStats: PublicKey; - takerUserAccount: UserAccount; - signingAuthority: PublicKey; - }, - orderParams: OptionalOrderParams, - referrerInfo?: ReferrerInfo, - subAccountId?: number, - precedingIxs?: TransactionInstruction[], - overrideCustomIxIndex?: number - ): Promise; - preparePlaceAndTakeSpotOrder( - orderParams: OptionalOrderParams, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, - makerInfo?: MakerInfo, - referrerInfo?: ReferrerInfo, - txParams?: TxParams, - subAccountId?: number - ): Promise<{ - placeAndTakeSpotOrderTx: Transaction | VersionedTransaction; - }>; - placeAndTakeSpotOrder( - orderParams: OptionalOrderParams, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, - makerInfo?: MakerInfo, - referrerInfo?: ReferrerInfo, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getPlaceAndTakeSpotOrderIx( - orderParams: OptionalOrderParams, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, - makerInfo?: MakerInfo, - referrerInfo?: ReferrerInfo, - subAccountId?: number - ): Promise; - placeAndMakeSpotOrder( - orderParams: OptionalOrderParams, - takerInfo: TakerInfo, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, - referrerInfo?: ReferrerInfo, - txParams?: TxParams, - subAccountId?: number - ): Promise; - getPlaceAndMakeSpotOrderIx( - orderParams: OptionalOrderParams, - takerInfo: TakerInfo, - fulfillmentConfig?: SerumV3FulfillmentConfigAccount, - referrerInfo?: ReferrerInfo, - subAccountId?: number - ): Promise; - - /** - * @deprecated use {@link placePerpOrder} or {@link placeAndTakePerpOrder} instead - */ - closePosition( - marketIndex: number, - limitPrice?: BN, - subAccountId?: number - ): Promise; - - /** - * Modifies an open order by closing it and replacing it with a new order. - * @deprecated use modifyOrder instead - * @param orderId: The open order to modify - * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. - * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. - * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. - * @returns - */ - modifyPerpOrder( - orderId: number, - newBaseAmount?: BN, - newLimitPrice?: BN, - newOraclePriceOffset?: number - ): Promise; - - /** - * Modifies an open order by closing it and replacing it with a new order. - * @deprecated use modifyOrderByUserOrderId instead - * @param userOrderId: The open order to modify - * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. - * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. - * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided. - * @returns - */ - modifyPerpOrderByUserOrderId( - userOrderId: number, - newBaseAmount?: BN, - newLimitPrice?: BN, - newOraclePriceOffset?: number - ): Promise; - - /** - * Modifies an open order (spot or perp) by closing it and replacing it with a new order. - * @param orderParams.orderId: The open order to modify - * @param orderParams.newDirection: The new direction for the order - * @param orderParams.newBaseAmount: The new base amount for the order - * @param orderParams.newLimitPice: The new limit price for the order - * @param orderParams.newOraclePriceOffset: The new oracle price offset for the order - * @param orderParams.newTriggerPrice: Optional - Thew new trigger price for the order. - * @param orderParams.auctionDuration: - * @param orderParams.auctionStartPrice: - * @param orderParams.auctionEndPrice: - * @param orderParams.reduceOnly: - * @param orderParams.postOnly: - * @param orderParams.bitFlags: - * @param orderParams.policy: - * @param orderParams.maxTs: - * @returns - */ - modifyOrder( - orderParams: { - orderId: number; - newDirection?: PositionDirection; - newBaseAmount?: BN; - newLimitPrice?: BN; - newOraclePriceOffset?: number; - newTriggerPrice?: BN; - newTriggerCondition?: OrderTriggerCondition; - auctionDuration?: number; - auctionStartPrice?: BN; - auctionEndPrice?: BN; - reduceOnly?: boolean; - postOnly?: boolean; - bitFlags?: number; - maxTs?: BN; - policy?: number; - }, - txParams?: TxParams, - subAccountId?: number - ): Promise; - - getModifyOrderIx( - orderParams: { - orderId: number; - newDirection?: PositionDirection; - newBaseAmount?: BN; - newLimitPrice?: BN; - newOraclePriceOffset?: number; - newTriggerPrice?: BN; - newTriggerCondition?: OrderTriggerCondition; - auctionDuration?: number; - auctionStartPrice?: BN; - auctionEndPrice?: BN; - reduceOnly?: boolean; - postOnly?: boolean; - bitFlags?: number; - maxTs?: BN; - policy?: number; - }, - subAccountId?: number - ): Promise; - - /** - * Modifies an open order by closing it and replacing it with a new order. - * @param orderParams.userOrderId: The open order to modify - * @param orderParams.newDirection: The new direction for the order - * @param orderParams.newBaseAmount: The new base amount for the order - * @param orderParams.newLimitPice: The new limit price for the order - * @param orderParams.newOraclePriceOffset: The new oracle price offset for the order - * @param orderParams.newTriggerPrice: Optional - Thew new trigger price for the order. - * @param orderParams.auctionDuration: Only required if order type changed to market from something else - * @param orderParams.auctionStartPrice: Only required if order type changed to market from something else - * @param orderParams.auctionEndPrice: Only required if order type changed to market from something else - * @param orderParams.reduceOnly: - * @param orderParams.postOnly: - * @param orderParams.bitFlags: - * @param orderParams.policy: - * @param orderParams.maxTs: - * @returns - */ - modifyOrderByUserOrderId( - orderParams: { - userOrderId: number; - newDirection?: PositionDirection; - newBaseAmount?: BN; - newLimitPrice?: BN; - newOraclePriceOffset?: number; - newTriggerPrice?: BN; - newTriggerCondition?: OrderTriggerCondition; - auctionDuration?: number; - auctionStartPrice?: BN; - auctionEndPrice?: BN; - reduceOnly?: boolean; - postOnly?: boolean; - bitFlags?: number; - policy?: ModifyOrderPolicy; - maxTs?: BN; - }, - txParams?: TxParams, - subAccountId?: number - ): Promise; - - getModifyOrderByUserIdIx( - orderParams: { - userOrderId: number; - newDirection?: PositionDirection; - newBaseAmount?: BN; - newLimitPrice?: BN; - newOraclePriceOffset?: number; - newTriggerPrice?: BN; - newTriggerCondition?: OrderTriggerCondition; - auctionDuration?: number; - auctionStartPrice?: BN; - auctionEndPrice?: BN; - reduceOnly?: boolean; - postOnly?: boolean; - bitFlags?: number; - policy?: ModifyOrderPolicy; - maxTs?: BN; - txParams?: TxParams; - }, - subAccountId?: number - ): Promise; - settlePNLs( - users: { - settleeUserAccountPublicKey: PublicKey; - settleeUserAccount: UserAccount; - }[], - marketIndexes: number[], - opts?: { - filterInvalidMarkets?: boolean; - }, - txParams?: TxParams - ): Promise; - getSettlePNLsIxs( - users: { - settleeUserAccountPublicKey: PublicKey; - settleeUserAccount: UserAccount; - }[], - marketIndexes: number[] - ): Promise; - settlePNL( - settleeUserAccountPublicKey: PublicKey, - settleeUserAccount: UserAccount, - marketIndex: number, - txParams?: TxParams, - optionalIxs?: TransactionInstruction[] - ): Promise; - settlePNLIx( - settleeUserAccountPublicKey: PublicKey, - settleeUserAccount: UserAccount, - marketIndex: number - ): Promise; - settleMultiplePNLs( - settleeUserAccountPublicKey: PublicKey, - settleeUserAccount: UserAccount, - marketIndexes: number[], - mode: SettlePnlMode, - txParams?: TxParams - ): Promise; - settleMultiplePNLsMultipleTxs( - settleeUserAccountPublicKey: PublicKey, - settleeUserAccount: UserAccount, - marketIndexes: number[], - mode: SettlePnlMode, - txParams?: TxParams, - optionalIxs?: TransactionInstruction[] - ): Promise; - settleMultiplePNLsIx( - settleeUserAccountPublicKey: PublicKey, - settleeUserAccount: UserAccount, - marketIndexes: number[], - mode: SettlePnlMode - ): Promise; - getSetUserStatusToBeingLiquidatedIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount - ): Promise; - setUserStatusToBeingLiquidated( - userAccountPublicKey: PublicKey, - userAccount: UserAccount - ): Promise; - liquidatePerp( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - maxBaseAssetAmount: BN, - limitPrice?: BN, - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getLiquidatePerpIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - maxBaseAssetAmount: BN, - limitPrice?: BN, - liquidatorSubAccountId?: number - ): Promise; - liquidatePerpWithFill( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - makerInfos: MakerInfo[], - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getLiquidatePerpWithFillIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - makerInfos: MakerInfo[], - liquidatorSubAccountId?: number - ): Promise; - liquidateSpot( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - assetMarketIndex: number, - liabilityMarketIndex: number, - maxLiabilityTransfer: BN, - limitPrice?: BN, - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getLiquidateSpotIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - assetMarketIndex: number, - liabilityMarketIndex: number, - maxLiabilityTransfer: BN, - limitPrice?: BN, - liquidatorSubAccountId?: number - ): Promise; - getJupiterLiquidateSpotWithSwapIxV6(params: { - jupiterClient: JupiterClient; - liabilityMarketIndex: number; - assetMarketIndex: number; - swapAmount: BN; - assetTokenAccount?: PublicKey; - liabilityTokenAccount?: PublicKey; - slippageBps?: number; - swapMode?: SwapMode; - onlyDirectRoutes?: boolean; - quote?: QuoteResponse; - userAccount: UserAccount; - userAccountPublicKey: PublicKey; - userStatsAccountPublicKey: PublicKey; - liquidatorSubAccountId?: number; - maxAccounts?: number; - }): Promise<{ - ixs: TransactionInstruction[]; - lookupTables: AddressLookupTableAccount[]; - }>; - - /** - * Get the drift liquidate_spot_with_swap instructions - * - * @param liabilityMarketIndex the market index of the token you're buying - * @param assetMarketIndex the market index of the token you're selling - * @param amountIn the amount of the token to sell - * @param assetTokenAccount the token account to move the tokens being sold - * @param liabilityTokenAccount the token account to receive the tokens being bought - * @param userAccount - * @param userAccountPublicKey - * @param userStatsAccountPublicKey - */ - getLiquidateSpotWithSwapIx(params: { - liabilityMarketIndex: number; - assetMarketIndex: number; - swapAmount: BN; - assetTokenAccount: PublicKey; - liabilityTokenAccount: PublicKey; - userAccount: UserAccount; - userAccountPublicKey: PublicKey; - userStatsAccountPublicKey: PublicKey; - liquidatorSubAccountId?: number; - }): Promise<{ - beginSwapIx: TransactionInstruction; - endSwapIx: TransactionInstruction; - }>; - - getInsuranceFundSwapIx(params: { - inMarketIndex: number; - outMarketIndex: number; - amountIn: BN; - inTokenAccount: PublicKey; - outTokenAccount: PublicKey; - }): Promise<{ - beginSwapIx: TransactionInstruction; - endSwapIx: TransactionInstruction; - }>; - liquidateBorrowForPerpPnl( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - perpMarketIndex: number, - liabilityMarketIndex: number, - maxLiabilityTransfer: BN, - limitPrice?: BN, - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getLiquidateBorrowForPerpPnlIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - perpMarketIndex: number, - liabilityMarketIndex: number, - maxLiabilityTransfer: BN, - limitPrice?: BN, - liquidatorSubAccountId?: number - ): Promise; - liquidatePerpPnlForDeposit( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - perpMarketIndex: number, - assetMarketIndex: number, - maxPnlTransfer: BN, - limitPrice?: BN, - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getLiquidatePerpPnlForDepositIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - perpMarketIndex: number, - assetMarketIndex: number, - maxPnlTransfer: BN, - limitPrice?: BN, - liquidatorSubAccountId?: number - ): Promise; - resolvePerpBankruptcy( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getResolvePerpBankruptcyIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - liquidatorSubAccountId?: number - ): Promise; - resolveSpotBankruptcy( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - txParams?: TxParams, - liquidatorSubAccountId?: number - ): Promise; - getResolveSpotBankruptcyIx( - userAccountPublicKey: PublicKey, - userAccount: UserAccount, - marketIndex: number, - liquidatorSubAccountId?: number - ): Promise; - updateFundingRate( - perpMarketIndex: number, - oracle: PublicKey, - txParams?: TxParams - ): Promise; - getUpdateFundingRateIx( - perpMarketIndex: number, - oracle: PublicKey - ): Promise; - updatePrelaunchOracle( - perpMarketIndex: number, - txParams?: TxParams - ): Promise; - getUpdatePrelaunchOracleIx( - perpMarketIndex: number - ): Promise; - updatePerpBidAskTwap( - perpMarketIndex: number, - makers: [PublicKey, PublicKey][], - txParams?: TxParams - ): Promise; - getUpdatePerpBidAskTwapIx( - perpMarketIndex: number, - makers: [PublicKey, PublicKey][] - ): Promise; - settleFundingPayment( - userAccountPublicKey: PublicKey, - txParams?: TxParams - ): Promise; - getSettleFundingPaymentIx( - userAccountPublicKey: PublicKey - ): Promise; - triggerEvent(eventName: keyof DriftClientAccountEvents, data?: any): void; - getOracleDataForPerpMarket(marketIndex: number): OraclePriceData; - getMMOracleDataForPerpMarket(marketIndex: number): OraclePriceData; - getOracleDataForSpotMarket(marketIndex: number): OraclePriceData; - initializeInsuranceFundStake( - marketIndex: number, - txParams?: TxParams - ): Promise; - getInitializeInsuranceFundStakeIx( - marketIndex: number - ): Promise; - getAddInsuranceFundStakeIx( - marketIndex: number, - amount: BN, - collateralAccountPublicKey: PublicKey - ): Promise; - - /** - * Add to an insurance fund stake and optionally initialize the account - */ - addInsuranceFundStake(params: { - /** - * Spot market index - */ - marketIndex: number; - amount: BN; - /** - * The account where the funds to stake come from. Usually an associated token account - */ - collateralAccountPublicKey: PublicKey; - /** - * Add instructions to initialize the staking account -- required if its the first time the currrent authority has staked in this market - */ - initializeStakeAccount?: boolean; - /** - * Optional -- withdraw from current subaccount to fund stake amount, instead of wallet balance - */ - fromSubaccount?: boolean; - txParams?: TxParams; - }): Promise; - - /** - * Get instructions to add to an insurance fund stake and optionally initialize the account - */ - getAddInsuranceFundStakeIxs(params: { - /** - * Spot market index - */ - marketIndex: number; - amount: BN; - /** - * The account where the funds to stake come from. Usually an associated token account - */ - collateralAccountPublicKey: PublicKey; - /** - * Add instructions to initialize the staking account -- required if its the first time the currrent authority has staked in this market - */ - initializeStakeAccount?: boolean; - /** - * Optional -- withdraw from current subaccount to fund stake amount, instead of wallet balance - */ - fromSubaccount?: boolean; - }): Promise; - - requestRemoveInsuranceFundStake( - marketIndex: number, - amount: BN, - txParams?: TxParams - ): Promise; - cancelRequestRemoveInsuranceFundStake( - marketIndex: number, - txParams?: TxParams - ): Promise; - removeInsuranceFundStake( - marketIndex: number, - collateralAccountPublicKey: PublicKey, - txParams?: TxParams - ): Promise; - updateUserQuoteAssetInsuranceStake( - authority: PublicKey, - txParams?: TxParams - ): Promise; - getUpdateUserQuoteAssetInsuranceStakeIx( - authority: PublicKey - ): Promise; - updateUserGovTokenInsuranceStake( - authority: PublicKey, - txParams?: TxParams, - env?: DriftEnv - ): Promise; - getUpdateUserGovTokenInsuranceStakeIx( - authority: PublicKey - ): Promise; - getUpdateUserGovTokenInsuranceStakeDevnetIx( - authority: PublicKey, - amount?: BN - ): Promise; - settleRevenueToInsuranceFund( - spotMarketIndex: number, - txParams?: TxParams - ): Promise; - getSettleRevenueToInsuranceFundIx( - spotMarketIndex: number - ): Promise; - resolvePerpPnlDeficit( - spotMarketIndex: number, - perpMarketIndex: number, - txParams?: TxParams - ): Promise; - getResolvePerpPnlDeficitIx( - spotMarketIndex: number, - perpMarketIndex: number - ): Promise; - getDepositIntoSpotMarketRevenuePoolIx( - marketIndex: number, - amount: BN, - userTokenAccountPublicKey: PublicKey - ): Promise; - - /** - * This ix will donate your funds to drift revenue pool. It does not deposit into your user account - * @param marketIndex - * @param amount - * @param userTokenAccountPublicKey - * @returns - */ - depositIntoSpotMarketRevenuePool( - marketIndex: number, - amount: BN, - userTokenAccountPublicKey: PublicKey - ): Promise; - - getPerpMarketExtendedInfo(marketIndex: number): PerpMarketExtendedInfo; - - /** - * Calculates taker / maker fee (as a percentage, e.g. .001 = 10 basis points) for particular marketType - * @param marketType - * @param positionMarketIndex - * @returns : {takerFee: number, makerFee: number} Precision None - */ - getMarketFees( - marketType: MarketType, - marketIndex?: number, - user?: IUser, - enteringHighLeverageMode?: boolean - ): { takerFee: number; makerFee: number }; - - /** - * Returns the market index and type for a given market name - * E.g. "SOL-PERP" -> { marketIndex: 0, marketType: MarketType.PERP } - * - * @param name - */ - getMarketIndexAndType( - name: string - ): { marketIndex: number; marketType: MarketType } | undefined; - - getReceiverProgram(): Program; - getSwitchboardOnDemandProgram(): Promise>; - postPythPullOracleUpdateAtomic( - vaaString: string, - feedId: string - ): Promise; - postMultiPythPullOracleUpdatesAtomic( - vaaString: string, - feedIds: string[] - ): Promise; - getPostPythPullOracleUpdateAtomicIxs( - vaaString: string, - feedIds: string | string[], - numSignatures?: number - ): Promise; - updatePythPullOracle( - vaaString: string, - feedId: string - ): Promise; - getUpdatePythPullOracleIxs( - params: { - merklePriceUpdate: { - message: Buffer; - proof: number[][]; - }; - }, - feedId: string, - encodedVaaAddress: PublicKey - ): Promise; - postPythLazerOracleUpdate( - feedIds: number[], - pythMessageHex: string - ): Promise; - getPostPythLazerOracleUpdateIxs( - feedIds: number[], - pythMessageHex: string, - precedingIxs?: TransactionInstruction[], - overrideCustomIxIndex?: number - ): Promise; - getPostManySwitchboardOnDemandUpdatesAtomicIxs( - feeds: PublicKey[], - recentSlothash?: Slothash, - numSignatures?: number - ): Promise; - - // @deprecated use getPostManySwitchboardOnDemandUpdatesAtomicIxs instead. This function no longer returns the required ixs due to upstream sdk changes. - getPostSwitchboardOnDemandUpdateAtomicIx( - feed: PublicKey, - recentSlothash?: Slothash, - numSignatures?: number - ): Promise; - - postSwitchboardOnDemandUpdate( - feed: PublicKey, - recentSlothash?: Slothash, - numSignatures?: number - ): Promise; - enableUserHighLeverageMode( - subAccountId: number, - txParams?: TxParams - ): Promise; - getEnableHighLeverageModeIx( - subAccountId: number, - depositToTradeArgs?: { - isMakingNewAccount: boolean; - depositMarketIndex: number; - orderMarketIndex: number; - } - ): Promise; - disableUserHighLeverageMode( - user: PublicKey, - userAccount?: UserAccount, - txParams?: TxParams - ): Promise; - getDisableHighLeverageModeIx( - user: PublicKey, - userAccount?: UserAccount - ): Promise; - fetchHighLeverageModeConfig(): Promise; - fetchProtectedMakerModeConfig(): Promise; - updateUserProtectedMakerOrders( - subAccountId: number, - protectedOrders: boolean, - authority?: PublicKey, - txParams?: TxParams - ): Promise; - getUpdateUserProtectedMakerOrdersIx( - subAccountId: number, - protectedOrders: boolean, - authority?: PublicKey - ): Promise; - getPauseSpotMarketDepositWithdrawIx( - spotMarketIndex: number - ): Promise; - pauseSpotMarketDepositWithdraw( - spotMarketIndex: number, - txParams?: TxParams - ): Promise; - updateMmOracleNative( - marketIndex: number, - oraclePrice: BN, - oracleSequenceId: BN - ): Promise; - getUpdateMmOracleNativeIx( - marketIndex: number, - oraclePrice: BN, - oracleSequenceId: BN - ): Promise; - updateAmmSpreadAdjustmentNative( - marketIndex: number, - ammSpreadAdjustment: number - ): Promise; - getUpdateAmmSpreadAdjustmentNativeIx( - marketIndex: number, - ammSpreadAdjustment: number - ): TransactionInstruction; - - /** - * Send a transaction. - * - * @param tx - * @param additionalSigners - * @param opts :: Will fallback to DriftClient's opts if not provided - * @param preSigned - * @returns - */ - sendTransaction( - tx: Transaction | VersionedTransaction, - additionalSigners?: Signer[], - opts?: ConfirmOptions, - preSigned?: boolean - ): Promise; - - buildTransaction( - instructions: TransactionInstruction | TransactionInstruction[], - txParams?: TxParams, - txVersion?: TransactionVersion, - lookupTables?: AddressLookupTableAccount[], - forceVersionedTransaction?: boolean, - recentBlockhash?: BlockhashWithExpiryBlockHeight, - optionalIxs?: TransactionInstruction[] - ): Promise; - buildBulkTransactions( - instructions: (TransactionInstruction | TransactionInstruction[])[], - txParams?: TxParams, - txVersion?: TransactionVersion, - lookupTables?: AddressLookupTableAccount[], - forceVersionedTransaction?: boolean - ): Promise<(Transaction | VersionedTransaction)[]>; - buildTransactionsMap( - instructionsMap: Record< - string, - TransactionInstruction | TransactionInstruction[] - >, - txParams?: TxParams, - txVersion?: TransactionVersion, - lookupTables?: AddressLookupTableAccount[], - forceVersionedTransaction?: boolean - ): ReturnType; - buildAndSignTransactionsMap( - instructionsMap: Record< - string, - TransactionInstruction | TransactionInstruction[] - >, - txParams?: TxParams, - txVersion?: TransactionVersion, - lookupTables?: AddressLookupTableAccount[], - forceVersionedTransaction?: boolean - ): ReturnType; -} diff --git a/sdk/src/driftClientConfig.ts b/sdk/src/driftClientConfig.ts index 07201c51d5..e833bd54e4 100644 --- a/sdk/src/driftClientConfig.ts +++ b/sdk/src/driftClientConfig.ts @@ -7,8 +7,8 @@ import { } from '@solana/web3.js'; import { IWallet, TxParams } from './types'; import { OracleInfo } from './oracles/types'; -import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; -import { DriftEnv } from './config/types'; +import { BulkAccountLoader } from './accounts/bulkAccountLoader'; +import { DriftEnv } from './config'; import { TxSender } from './tx/types'; import { TxHandler, TxHandlerConfig } from './tx/txHandler'; import { DelistedMarketSetting, GrpcConfigs } from './accounts/types'; diff --git a/sdk/src/events/types.ts b/sdk/src/events/types.ts index 2671344ceb..3c4c92548c 100644 --- a/sdk/src/events/types.ts +++ b/sdk/src/events/types.ts @@ -21,7 +21,7 @@ import { FuelSeasonRecord, InsuranceFundSwapRecord, TransferProtocolIfSharesToRevenuePoolRecord, -} from '../types'; +} from '../index'; import { EventEmitter } from 'events'; export type EventSubscriptionOptions = { diff --git a/sdk/src/index.ts b/sdk/src/index.ts index aa09c8c4e4..cd39f6abe3 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -9,36 +9,35 @@ export * from './oracles/strictOraclePrice'; export * from './types'; export * from './constants/perpMarkets'; export * from './accounts/fetch'; -export * from './accounts/driftClientAccount/webSocketDriftClientAccountSubscriber'; -export * from './accounts/insuranceFundStakeAccount/webSocketInsuranceFundStakeAccountSubscriber'; -export * from './accounts/highLeverageModeConfigAccount/webSocketHighLeverageModeConfigAccountSubscriber'; -export * from './accounts/bulkAccountLoader/bulkAccountLoader'; +export * from './accounts/webSocketDriftClientAccountSubscriber'; +export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber'; +export * from './accounts/webSocketHighLeverageModeConfigAccountSubscriber'; +export * from './accounts/bulkAccountLoader'; export * from './accounts/bulkUserSubscription'; export * from './accounts/bulkUserStatsSubscription'; -export { CustomizedCadenceBulkAccountLoader } from './accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader'; -export * from './accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; -export * from './accounts/oracleAccount/pollingOracleAccountSubscriber'; -export * from './accounts/tokenAccount/pollingTokenAccountSubscriber'; -export * from './accounts/userAccount/pollingUserAccountSubscriber'; -export * from './accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; -export * from './accounts/insuranceFundStakeAccount/pollingInsuranceFundStakeAccountSubscriber'; -export * from './accounts/highLeverageModeConfigAccount/pollingHighLeverageModeConfigAccountSubscriber'; -export * from './accounts/userAccount/basicUserAccountSubscriber'; -export * from './accounts/userAccount/oneShotUserAccountSubscriber'; +export { CustomizedCadenceBulkAccountLoader } from './accounts/customizedCadenceBulkAccountLoader'; +export * from './accounts/pollingDriftClientAccountSubscriber'; +export * from './accounts/pollingOracleAccountSubscriber'; +export * from './accounts/pollingTokenAccountSubscriber'; +export * from './accounts/pollingUserAccountSubscriber'; +export * from './accounts/pollingUserStatsAccountSubscriber'; +export * from './accounts/pollingInsuranceFundStakeAccountSubscriber'; +export * from './accounts/pollingHighLeverageModeConfigAccountSubscriber'; +export * from './accounts/basicUserAccountSubscriber'; +export * from './accounts/oneShotUserAccountSubscriber'; export * from './accounts/types'; export * from './addresses/pda'; export * from './adminClient'; export * from './assert/assert'; export * from './testClient'; export * from './user'; -export * from './user/types'; +export * from './userConfig'; export * from './userStats'; export * from './userName'; export * from './userStatsConfig'; export * from './decode/user'; export * from './decode/customCoder'; export * from './driftClient'; -export * from './driftClient/types'; export * from './factory/oracleClient'; export * from './factory/bigNum'; export * from './events/types'; @@ -77,11 +76,7 @@ export * from './keypair'; export * from './types'; export * from './math/utils'; export * from './math/fuel'; - -/** Config */ export * from './config'; -export * from './config/types'; - export * from './constants/numericConstants'; export * from './serum/serumSubscriber'; export * from './serum/serumFulfillmentConfigMap'; @@ -114,24 +109,16 @@ export * from './util/pythOracleUtils'; export * from './math/spotBalance'; export * from './constants/spotMarkets'; export * from './driftClientConfig'; - -/** DLOB */ export * from './dlob/DLOB'; export * from './dlob/DLOBNode'; export * from './dlob/NodeList'; export * from './dlob/DLOBSubscriber'; export * from './dlob/types'; export * from './dlob/orderBookLevels'; -export * from './dlob/utils'; - -/** UserMap */ export * from './userMap/userMap'; export * from './userMap/referrerMap'; export * from './userMap/userStatsMap'; export * from './userMap/userMapConfig'; -export * from './userMap/events'; -export * from './userMap/types'; - export * from './math/bankruptcy'; export * from './orderSubscriber'; export * from './orderSubscriber/types'; diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index ed26de9be7..cf46acfd73 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -24,7 +24,7 @@ import { } from '../types'; import { assert } from '../assert/assert'; import { squareRootBN, sigNum, clampBN } from './utils'; -import { standardizeBaseAssetAmount } from './utils'; +import { standardizeBaseAssetAmount } from './orders'; import { OraclePriceData } from '../oracles/types'; import { diff --git a/sdk/src/math/insurance.ts b/sdk/src/math/insurance.ts index db846e0e4f..093a780073 100644 --- a/sdk/src/math/insurance.ts +++ b/sdk/src/math/insurance.ts @@ -1,7 +1,6 @@ import { PERCENTAGE_PRECISION, ZERO } from '../constants/numericConstants'; -import { SpotMarketAccount, SpotBalanceType } from '../types'; +import { BN, SpotMarketAccount, SpotBalanceType } from '../index'; import { getTokenAmount } from '../math/spotBalance'; -import { BN } from '@coral-xyz/anchor'; export function nextRevenuePoolSettleApr( spotMarket: SpotMarketAccount, diff --git a/sdk/src/math/margin.ts b/sdk/src/math/margin.ts index 26fa69e3df..cba701015e 100644 --- a/sdk/src/math/margin.ts +++ b/sdk/src/math/margin.ts @@ -15,7 +15,8 @@ import { BN } from '@coral-xyz/anchor'; import { OraclePriceData } from '../oracles/types'; import { calculateMarketMarginRatio } from './market'; import { calculateScaledInitialAssetWeight } from './spotBalance'; -import { OneShotUserAccountSubscriber } from '../accounts/userAccount/oneShotUserAccountSubscriber'; +import { DriftClient } from '../driftClient'; +import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber'; import { PerpMarketAccount, PerpPosition, @@ -26,7 +27,6 @@ import { PublicKey } from '@solana/web3.js'; import { User } from '../user'; import { isVariant } from '../types'; import { assert } from '../assert/assert'; -import { IDriftClient } from '../driftClient/types'; export function calculateSizePremiumLiabilityWeight( size: BN, // AMM_RESERVE_PRECISION @@ -216,7 +216,7 @@ export function calculatePerpLiabilityValue( * @returns */ export function calculateMarginUSDCRequiredForTrade( - driftClient: IDriftClient, + driftClient: DriftClient, targetMarketIndex: number, baseSize: BN, userMaxMarginRatio?: number, @@ -256,7 +256,7 @@ export function calculateMarginUSDCRequiredForTrade( * Returns collateral required in the precision of the target collateral market. */ export function calculateCollateralDepositRequiredForTrade( - driftClient: IDriftClient, + driftClient: DriftClient, targetMarketIndex: number, baseSize: BN, collateralIndex: number, @@ -298,7 +298,7 @@ export function calculateCollateralDepositRequiredForTrade( } export function calculateCollateralValueOfDeposit( - driftClient: IDriftClient, + driftClient: DriftClient, collateralIndex: number, baseSize: BN ): BN { @@ -345,7 +345,7 @@ export function calculateLiquidationPrice( } export function calculateUserMaxPerpOrderSize( - driftClient: IDriftClient, + driftClient: DriftClient, userAccountKey: PublicKey, userAccount: UserAccount, targetMarketIndex: number, diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index 11c3bdc422..c7c9b30d4e 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -28,7 +28,7 @@ import { QUOTE_SPOT_MARKET_INDEX, } from '../constants/numericConstants'; import { getTokenAmount } from './spotBalance'; -import { IDLOB } from '../dlob/types'; +import { DLOB } from '../dlob/DLOB'; import { assert } from '../assert/assert'; /** @@ -295,7 +295,7 @@ export function calculateNetUserPnlImbalance( export function calculateAvailablePerpLiquidity( market: PerpMarketAccount, oraclePriceData: OraclePriceData, - dlob: IDLOB, + dlob: DLOB, slot: number ): { bids: BN; asks: BN } { let [bids, asks] = calculateMarketOpenBidAsk( diff --git a/sdk/src/math/oracles.ts b/sdk/src/math/oracles.ts index 8873c66150..bfbd88702c 100644 --- a/sdk/src/math/oracles.ts +++ b/sdk/src/math/oracles.ts @@ -1,11 +1,4 @@ -import { - AMM, - HistoricalOracleData, - OracleGuardRails, - OracleSource, - PerpMarketAccount, - isVariant, -} from '../types'; +import { AMM, OracleGuardRails, isVariant } from '../types'; import { OraclePriceData } from '../oracles/types'; import { BID_ASK_SPREAD_PRECISION, @@ -16,8 +9,13 @@ import { FIVE_MINUTE, PERCENTAGE_PRECISION, } from '../constants/numericConstants'; +import { + BN, + HistoricalOracleData, + OracleSource, + PerpMarketAccount, +} from '../index'; import { assert } from '../assert/assert'; -import { BN } from '@coral-xyz/anchor'; export function oraclePriceBands( market: PerpMarketAccount, diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index c829a673a4..7be65e58ce 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -1,3 +1,4 @@ +import { User } from '../user'; import { isOneOfVariant, isVariant, @@ -20,10 +21,8 @@ import { calculateMaxBaseAssetAmountToTrade, calculateUpdatedAMM, } from './amm'; -import { standardizeBaseAssetAmount } from './utils'; -import { IUser } from '../user/types'; -export function isOrderRiskIncreasing(user: IUser, order: Order): boolean { +export function isOrderRiskIncreasing(user: User, order: Order): boolean { if (!isVariant(order.status, 'open')) { return false; } @@ -62,7 +61,7 @@ export function isOrderRiskIncreasing(user: IUser, order: Order): boolean { } export function isOrderRiskIncreasingInSameDirection( - user: IUser, + user: User, order: Order ): boolean { if (!isVariant(order.status, 'open')) { @@ -94,7 +93,7 @@ export function isOrderRiskIncreasingInSameDirection( return false; } -export function isOrderReduceOnly(user: IUser, order: Order): boolean { +export function isOrderReduceOnly(user: User, order: Order): boolean { if (!isVariant(order.status, 'open')) { return false; } @@ -122,6 +121,14 @@ export function isOrderReduceOnly(user: IUser, order: Order): boolean { return true; } +export function standardizeBaseAssetAmount( + baseAssetAmount: BN, + stepSize: BN +): BN { + const remainder = baseAssetAmount.mod(stepSize); + return baseAssetAmount.sub(remainder); +} + export function standardizePrice( price: BN, tickSize: BN, diff --git a/sdk/src/math/utils.ts b/sdk/src/math/utils.ts index e5ad56b67a..fcebbdb78d 100644 --- a/sdk/src/math/utils.ts +++ b/sdk/src/math/utils.ts @@ -119,11 +119,3 @@ export function numberToSafeBN(number: number, precision: BN): BN { } } } - -export function standardizeBaseAssetAmount( - baseAssetAmount: BN, - stepSize: BN -): BN { - const remainder = baseAssetAmount.mod(stepSize); - return baseAssetAmount.sub(remainder); -} diff --git a/sdk/src/openbook/openbookV2Subscriber.ts b/sdk/src/openbook/openbookV2Subscriber.ts index 696862dfae..39348252ca 100644 --- a/sdk/src/openbook/openbookV2Subscriber.ts +++ b/sdk/src/openbook/openbookV2Subscriber.ts @@ -1,5 +1,5 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; +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'; diff --git a/sdk/src/orderSubscriber/OrderSubscriber.ts b/sdk/src/orderSubscriber/OrderSubscriber.ts index 26d44341c6..2c0b95bb69 100644 --- a/sdk/src/orderSubscriber/OrderSubscriber.ts +++ b/sdk/src/orderSubscriber/OrderSubscriber.ts @@ -1,4 +1,4 @@ -import { IDriftClient } from '../driftClient/types'; +import { DriftClient } from '../driftClient'; import { UserAccount } from '../types'; import { getNonIdleUserFilter, @@ -8,24 +8,20 @@ import { import { Commitment, PublicKey, RpcResponseAndContext } from '@solana/web3.js'; import { Buffer } from 'buffer'; import { DLOB } from '../dlob/DLOB'; -import { - IOrderSubscriber, - OrderSubscriberConfig, - OrderSubscriberEvents, -} from './types'; +import { OrderSubscriberConfig, OrderSubscriberEvents } from './types'; import { PollingSubscription } from './PollingSubscription'; import { WebsocketSubscription } from './WebsocketSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { calculateOrderBaseAssetAmount, ZERO } from '../index'; +import { BN } from '@coral-xyz/anchor'; +import { ProtectMakerParamsMap } from '../dlob/types'; import { decodeUser } from '../decode/user'; import { grpcSubscription } from './grpcSubscription'; import { isUserProtectedMaker } from '../math/userStatus'; -import { BN } from '@coral-xyz/anchor'; -import { ProtectMakerParamsMap } from '../dlob/types'; -export class OrderSubscriber implements IOrderSubscriber { - driftClient: IDriftClient; +export class OrderSubscriber { + driftClient: DriftClient; usersAccounts = new Map(); subscription: PollingSubscription | WebsocketSubscription | grpcSubscription; commitment: Commitment; diff --git a/sdk/src/orderSubscriber/PollingSubscription.ts b/sdk/src/orderSubscriber/PollingSubscription.ts index e19dd67764..cc6c8a0cf2 100644 --- a/sdk/src/orderSubscriber/PollingSubscription.ts +++ b/sdk/src/orderSubscriber/PollingSubscription.ts @@ -1,7 +1,7 @@ -import { IOrderSubscriber } from './types'; +import { OrderSubscriber } from './OrderSubscriber'; export class PollingSubscription { - private orderSubscriber: IOrderSubscriber; + private orderSubscriber: OrderSubscriber; private frequency: number; intervalId?: ReturnType; @@ -10,7 +10,7 @@ export class PollingSubscription { orderSubscriber, frequency, }: { - orderSubscriber: IOrderSubscriber; + orderSubscriber: OrderSubscriber; frequency: number; }) { this.orderSubscriber = orderSubscriber; diff --git a/sdk/src/orderSubscriber/WebsocketSubscription.ts b/sdk/src/orderSubscriber/WebsocketSubscription.ts index 769fc474bf..ade8efc6ae 100644 --- a/sdk/src/orderSubscriber/WebsocketSubscription.ts +++ b/sdk/src/orderSubscriber/WebsocketSubscription.ts @@ -1,12 +1,12 @@ +import { OrderSubscriber } from './OrderSubscriber'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; -import { IOrderSubscriber } from './types'; export class WebsocketSubscription { - private orderSubscriber: IOrderSubscriber; + private orderSubscriber: OrderSubscriber; private commitment: Commitment; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; @@ -25,7 +25,7 @@ export class WebsocketSubscription { resyncIntervalMs, decoded = true, }: { - orderSubscriber: IOrderSubscriber; + orderSubscriber: OrderSubscriber; commitment: Commitment; skipInitialLoad?: boolean; resubOpts?: ResubOpts; diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index da4d7d4e6d..41101435ab 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -1,13 +1,13 @@ import { Context, PublicKey } from '@solana/web3.js'; import { Buffer } from 'buffer'; -import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; +import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; import { getUserFilter, getNonIdleUserFilter } from '../memcmp'; -import { IOrderSubscriber } from './types'; export class grpcSubscription { - private orderSubscriber: IOrderSubscriber; + private orderSubscriber: OrderSubscriber; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; private resyncIntervalMs?: number; @@ -28,7 +28,7 @@ export class grpcSubscription { decoded = true, }: { grpcConfigs: GrpcConfigs; - orderSubscriber: IOrderSubscriber; + orderSubscriber: OrderSubscriber; skipInitialLoad?: boolean; resubOpts?: ResubOpts; resyncIntervalMs?: number; diff --git a/sdk/src/orderSubscriber/types.ts b/sdk/src/orderSubscriber/types.ts index 0315c5b595..4e441e0cbf 100644 --- a/sdk/src/orderSubscriber/types.ts +++ b/sdk/src/orderSubscriber/types.ts @@ -1,14 +1,10 @@ import { Commitment, PublicKey } from '@solana/web3.js'; import { Order, UserAccount } from '../types'; -import { IDriftClient } from '../driftClient/types'; +import { DriftClient } from '../driftClient'; import { GrpcConfigs } from '../accounts/types'; -import { Buffer } from 'buffer'; -import StrictEventEmitter from 'strict-event-emitter-types'; -import { EventEmitter } from 'events'; -import { ProtectMakerParamsMap, IDLOB } from '../dlob/types'; export type OrderSubscriberConfig = { - driftClient: IDriftClient; + driftClient: DriftClient; subscriptionConfig: | { type: 'polling'; @@ -57,40 +53,3 @@ export interface OrderSubscriberEvents { dataType: 'raw' | 'decoded' | 'buffer' ) => void; } - -export interface IOrderSubscriber { - driftClient: IDriftClient; - usersAccounts: Map; - commitment: Commitment; - eventEmitter: StrictEventEmitter; - fetchPromise?: Promise; - fetchPromiseResolver: () => void; - mostRecentSlot: number; - decodeFn: (name: string, data: Buffer) => UserAccount; - decodeData?: boolean; - fetchAllNonIdleUsers?: boolean; - - subscribe(): Promise; - - fetch(): Promise; - - tryUpdateUserAccount( - key: string, - dataType: 'raw' | 'decoded' | 'buffer', - data: string[] | UserAccount | Buffer, - slot: number - ): void; - - getDLOB( - slot: number, - protectedMakerParamsMap?: ProtectMakerParamsMap - ): Promise; - - getSlot(): number; - - addPubkey(userAccountPublicKey: PublicKey): Promise; - - mustGetUserAccount(key: string): Promise; - - unsubscribe(): Promise; -} diff --git a/sdk/src/phoenix/phoenixSubscriber.ts b/sdk/src/phoenix/phoenixSubscriber.ts index 8a729de09c..aafcc43190 100644 --- a/sdk/src/phoenix/phoenixSubscriber.ts +++ b/sdk/src/phoenix/phoenixSubscriber.ts @@ -1,5 +1,5 @@ import { Connection, PublicKey, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; import { Client, deserializeClockData, diff --git a/sdk/src/serum/serumSubscriber.ts b/sdk/src/serum/serumSubscriber.ts index f2b0d81a56..9806afea9b 100644 --- a/sdk/src/serum/serumSubscriber.ts +++ b/sdk/src/serum/serumSubscriber.ts @@ -1,5 +1,5 @@ import { Connection, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; import { Market, Orderbook } from '@project-serum/serum'; import { SerumMarketSubscriberConfig } from './types'; import { BN } from '@coral-xyz/anchor'; diff --git a/sdk/src/serum/types.ts b/sdk/src/serum/types.ts index 88e0c0b910..d42b37b0c6 100644 --- a/sdk/src/serum/types.ts +++ b/sdk/src/serum/types.ts @@ -1,5 +1,5 @@ import { Connection, PublicKey } from '@solana/web3.js'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; export type SerumMarketSubscriberConfig = { connection: Connection; diff --git a/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts b/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts index c2ab64dd59..ece3be3b92 100644 --- a/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts +++ b/sdk/src/swift/grpcSignedMsgUserAccountSubscriber.ts @@ -1,5 +1,5 @@ import { Commitment, Context, PublicKey } from '@solana/web3.js'; -import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; +import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { SignedMsgUserOrdersAccount } from '../types'; import { getSignedMsgUserOrdersFilter } from '../memcmp'; diff --git a/sdk/src/swift/signedMsgUserAccountSubscriber.ts b/sdk/src/swift/signedMsgUserAccountSubscriber.ts index 840a936ef0..6575b5b309 100644 --- a/sdk/src/swift/signedMsgUserAccountSubscriber.ts +++ b/sdk/src/swift/signedMsgUserAccountSubscriber.ts @@ -1,5 +1,5 @@ import { getSignedMsgUserOrdersFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { SignedMsgOrderId, SignedMsgUserOrdersAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; diff --git a/sdk/src/swift/swiftOrderSubscriber.ts b/sdk/src/swift/swiftOrderSubscriber.ts index 25bd7ea887..bab3032795 100644 --- a/sdk/src/swift/swiftOrderSubscriber.ts +++ b/sdk/src/swift/swiftOrderSubscriber.ts @@ -2,8 +2,8 @@ import { DevnetPerpMarkets, MainnetPerpMarkets, } from '../constants/perpMarkets'; -import { IDriftClient } from '../driftClient/types'; -import { DriftEnv } from '../config/types'; +import { DriftClient } from '../driftClient'; +import { DriftEnv } from '../config'; import { getUserAccountPublicKey, getUserStatsAccountPublicKey, @@ -28,7 +28,7 @@ export interface AccountGetter { } export type SwiftOrderSubscriberConfig = { - driftClient: IDriftClient; + driftClient: DriftClient; userAccountGetter?: AccountGetter; driftEnv: DriftEnv; endpoint?: string; @@ -45,7 +45,7 @@ export class SwiftOrderSubscriber { private heartbeatTimeout: ReturnType | null = null; private readonly heartbeatIntervalMs = 60000; private ws: WebSocket | null = null; - private driftClient: IDriftClient; + private driftClient: DriftClient; public userAccountGetter?: AccountGetter; // In practice, this for now is just an OrderSubscriber or a UserMap public onOrder: ( orderMessageRaw: any, diff --git a/sdk/src/testClient.ts b/sdk/src/testClient.ts index 9c73f3a6b5..33af3b4831 100644 --- a/sdk/src/testClient.ts +++ b/sdk/src/testClient.ts @@ -1,7 +1,7 @@ import { AdminClient } from './adminClient'; import { ConfirmOptions, Signer, Transaction } from '@solana/web3.js'; import { TxSigAndSlot } from './tx/types'; -import { PollingDriftClientAccountSubscriber } from './accounts/driftClientAccount/pollingDriftClientAccountSubscriber'; +import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; import { DriftClientConfig } from './driftClientConfig'; export class TestClient extends AdminClient { diff --git a/sdk/src/user/index.ts b/sdk/src/user.ts similarity index 98% rename from sdk/src/user/index.ts rename to sdk/src/user.ts index 5bf7d26a29..215d0450fd 100644 --- a/sdk/src/user/index.ts +++ b/sdk/src/user.ts @@ -1,6 +1,7 @@ import { PublicKey } from '@solana/web3.js'; import { EventEmitter } from 'events'; import StrictEventEmitter from 'strict-event-emitter-types'; +import { DriftClient } from './driftClient'; import { HealthComponent, HealthComponents, @@ -13,12 +14,12 @@ import { UserAccount, UserStatus, UserStatsAccount, -} from '../types'; +} from './types'; import { calculateEntryPrice, calculateUnsettledFundingPnl, positionIsAvailable, -} from '../math/position'; +} from './math/position'; import { AMM_RESERVE_PRECISION, AMM_RESERVE_PRECISION_EXP, @@ -39,43 +40,41 @@ import { TWO, ZERO, FUEL_START_TS, -} from '../constants/numericConstants'; +} from './constants/numericConstants'; import { DataAndSlot, UserAccountEvents, UserAccountSubscriber, -} from '../accounts/types'; -import { BigNum } from '../factory/bigNum'; +} from './accounts/types'; +import { BigNum } from './factory/bigNum'; import { BN } from '@coral-xyz/anchor'; -import { - calculateBaseAssetValue, - calculatePositionPNL, -} from '../math/position'; +import { calculateBaseAssetValue, calculatePositionPNL } from './math/position'; import { calculateMarketMarginRatio, calculateReservePrice, calculateUnrealizedAssetWeight, -} from '../math/market'; +} from './math/market'; import { calculatePerpLiabilityValue, calculateWorstCasePerpLiabilityValue, -} from '../math/margin'; -import { calculateSpotMarketMarginRatio } from '../math/spotMarket'; -import { divCeil, sigNum } from '../math/utils'; +} from './math/margin'; +import { calculateSpotMarketMarginRatio } from './math/spotMarket'; +import { divCeil, sigNum } from './math/utils'; import { getBalance, getSignedTokenAmount, getStrictTokenValue, getTokenValue, -} from '../math/spotBalance'; -import { getUser30dRollingVolumeEstimate } from '../math/trade'; +} from './math/spotBalance'; +import { getUser30dRollingVolumeEstimate } from './math/trade'; import { MarketType, PositionDirection, SpotBalanceType, SpotMarketAccount, -} from '../types'; -import { standardizeBaseAssetAmount } from '../math/utils'; +} from './types'; +import { standardizeBaseAssetAmount } from './math/orders'; +import { UserStats } from './userStats'; import { calculateAssetWeight, calculateLiabilityWeight, @@ -83,47 +82,35 @@ import { getSpotAssetValue, getSpotLiabilityValue, getTokenAmount, -} from '../math/spotBalance'; -import { calculateMarketOpenBidAsk } from '../math/amm'; +} from './math/spotBalance'; +import { calculateMarketOpenBidAsk } from './math/amm'; import { calculateBaseAssetValueWithOracle, calculateCollateralDepositRequiredForTrade, calculateMarginUSDCRequiredForTrade, calculateWorstCaseBaseAssetAmount, -} from '../math/margin'; -import { OraclePriceData } from '../oracles/types'; -import { UserSubscriptionConfig } from './types'; -import { PollingUserAccountSubscriber } from '../accounts/userAccount/pollingUserAccountSubscriber'; -import { WebSocketUserAccountSubscriber } from '../accounts/userAccount/webSocketUserAccountSubscriber'; +} from './math/margin'; +import { OraclePriceData } from './oracles/types'; +import { UserConfig } from './userConfig'; +import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber'; +import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber'; import { calculateWeightedTokenValue, getWorstCaseTokenAmounts, isSpotPositionAvailable, -} from '../math/spotPosition'; +} from './math/spotPosition'; import { calculateLiveOracleTwap, getMultipleBetweenOracleSources, -} from '../math/oracles'; -import { - getPerpMarketTierNumber, - getSpotMarketTierNumber, -} from '../math/tiers'; -import { StrictOraclePrice } from '../oracles/strictOraclePrice'; - -import { calculateSpotFuelBonus, calculatePerpFuelBonus } from '../math/fuel'; -import { grpcUserAccountSubscriber } from '../accounts/userAccount/grpcUserAccountSubscriber'; -import { IUserStats } from '../userStats/types'; -import { IUser } from './types'; -import { IDriftClient } from '../driftClient/types'; - -export type UserConfig = { - accountSubscription?: UserSubscriptionConfig; - driftClient: IDriftClient; - userAccountPublicKey: PublicKey; -}; +} from './math/oracles'; +import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers'; +import { StrictOraclePrice } from './oracles/strictOraclePrice'; + +import { calculateSpotFuelBonus, calculatePerpFuelBonus } from './math/fuel'; +import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber'; -export class User implements IUser { - driftClient: IDriftClient; +export class User { + driftClient: DriftClient; userAccountPublicKey: PublicKey; accountSubscriber: UserAccountSubscriber; _isSubscribed = false; @@ -948,7 +935,7 @@ export class User implements IUser { now: BN, includeSettled = true, includeUnsettled = true, - givenUserStats?: IUserStats + givenUserStats?: UserStats ): { depositFuel: BN; borrowFuel: BN; diff --git a/sdk/src/user/types.ts b/sdk/src/user/types.ts deleted file mode 100644 index dfba936f50..0000000000 --- a/sdk/src/user/types.ts +++ /dev/null @@ -1,754 +0,0 @@ -import { Commitment, PublicKey } from '@solana/web3.js'; -import { EventEmitter } from 'events'; -import StrictEventEmitter from 'strict-event-emitter-types'; -import { - HealthComponent, - HealthComponents, - MarginCategory, - Order, - PerpPosition, - SpotPosition, - UserAccount, - UserStatus, - MarketType, - PositionDirection, - SpotMarketAccount, - PerpMarketAccount, - FeeTier, -} from '../types'; -import { - DataAndSlot, - GrpcConfigs, - UserAccountEvents, - UserAccountSubscriber, -} from '../accounts/types'; -import { BN } from '@coral-xyz/anchor'; -import { OraclePriceData } from '../oracles/types'; -import { StrictOraclePrice } from '../oracles/strictOraclePrice'; -import { IUserStats } from '../userStats/types'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; - -export type UserSubscriptionConfig = - | { - type: 'grpc'; - resubTimeoutMs?: number; - logResubMessages?: boolean; - grpcConfigs: GrpcConfigs; - } - | { - type: 'websocket'; - resubTimeoutMs?: number; - logResubMessages?: boolean; - commitment?: Commitment; - } - | { - type: 'polling'; - accountLoader: BulkAccountLoader; - } - | { - type: 'custom'; - userAccountSubscriber: UserAccountSubscriber; - }; - -export interface IUser { - userAccountPublicKey: PublicKey; - accountSubscriber: UserAccountSubscriber; - eventEmitter: StrictEventEmitter; - isSubscribed: boolean; - - /** - * Subscribe to User state accounts - * @returns SusbcriptionSuccess result - */ - subscribe(userAccount?: UserAccount): Promise; - - /** - * Forces the accountSubscriber to fetch account updates from rpc - */ - fetchAccounts(): Promise; - - unsubscribe(): Promise; - - getUserAccount(): UserAccount; - - forceGetUserAccount(): Promise; - - getUserAccountAndSlot(): DataAndSlot | undefined; - - getPerpPositionForUserAccount( - userAccount: UserAccount, - marketIndex: number - ): PerpPosition | undefined; - - /** - * Gets the user's current position for a given perp market. If the user has no position returns undefined - * @param marketIndex - * @returns userPerpPosition - */ - getPerpPosition(marketIndex: number): PerpPosition | undefined; - - getPerpPositionAndSlot( - marketIndex: number - ): DataAndSlot; - - getSpotPositionForUserAccount( - userAccount: UserAccount, - marketIndex: number - ): SpotPosition | undefined; - - /** - * Gets the user's current position for a given spot market. If the user has no position returns undefined - * @param marketIndex - * @returns userSpotPosition - */ - getSpotPosition(marketIndex: number): SpotPosition | undefined; - - getSpotPositionAndSlot( - marketIndex: number - ): DataAndSlot; - - getEmptySpotPosition(marketIndex: number): SpotPosition; - - /** - * Returns the token amount for a given market. The spot market precision is based on the token mint decimals. - * Positive if it is a deposit, negative if it is a borrow. - * - * @param marketIndex - */ - getTokenAmount(marketIndex: number): BN; - - getEmptyPosition(marketIndex: number): PerpPosition; - - getClonedPosition(position: PerpPosition): PerpPosition; - - getOrderForUserAccount( - userAccount: UserAccount, - orderId: number - ): Order | undefined; - - /** - * @param orderId - * @returns Order - */ - getOrder(orderId: number): Order | undefined; - - getOrderAndSlot(orderId: number): DataAndSlot; - - getOrderByUserIdForUserAccount( - userAccount: UserAccount, - userOrderId: number - ): Order | undefined; - - /** - * @param userOrderId - * @returns Order - */ - getOrderByUserOrderId(userOrderId: number): Order | undefined; - - getOrderByUserOrderIdAndSlot( - userOrderId: number - ): DataAndSlot; - - getOpenOrdersForUserAccount(userAccount?: UserAccount): Order[]; - - getOpenOrders(): Order[]; - - getOpenOrdersAndSlot(): DataAndSlot; - - getUserAccountPublicKey(): PublicKey; - - exists(): Promise; - - /** - * calculates the total open bids/asks in a perp market (including lps) - * @returns : open bids - * @returns : open asks - */ - getPerpBidAsks(marketIndex: number): [BN, BN]; - - /** - * calculates the open bids and asks for an lp - * optionally pass in lpShares to see what bid/asks a user *would* take on - * @returns : lp open bids - * @returns : lp open asks - */ - getLPBidAsks(marketIndex: number, lpShares?: BN): [BN, BN]; - - /** - * calculates the market position if the lp position was settled - * @returns : the settled userPosition - * @returns : the dust base asset amount (ie, < stepsize) - * @returns : pnl from settle - */ - getPerpPositionWithLPSettle( - marketIndex: number, - originalPosition?: PerpPosition, - burnLpShares?: boolean, - includeRemainderInBaseAmount?: boolean - ): [PerpPosition, BN, BN]; - - /** - * calculates Buying Power = free collateral / initial margin ratio - * @returns : Precision QUOTE_PRECISION - */ - getPerpBuyingPower( - marketIndex: number, - collateralBuffer?: BN, - enterHighLeverageMode?: boolean - ): BN; - - getPerpBuyingPowerFromFreeCollateralAndBaseAssetAmount( - marketIndex: number, - freeCollateral: BN, - baseAssetAmount: BN, - enterHighLeverageMode?: boolean - ): BN; - - /** - * calculates Free Collateral = Total collateral - margin requirement - * @returns : Precision QUOTE_PRECISION - */ - getFreeCollateral( - marginCategory?: MarginCategory, - enterHighLeverageMode?: boolean - ): BN; - - /** - * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION - */ - getMarginRequirement( - marginCategory: MarginCategory, - liquidationBuffer?: BN, - strict?: boolean, - includeOpenOrders?: boolean, - enteringHighLeverage?: boolean - ): BN; - - /** - * @returns The initial margin requirement in USDC. : QUOTE_PRECISION - */ - getInitialMarginRequirement(enterHighLeverageMode?: boolean): BN; - - /** - * @returns The maintenance margin requirement in USDC. : QUOTE_PRECISION - */ - getMaintenanceMarginRequirement(liquidationBuffer?: BN): BN; - - getActivePerpPositionsForUserAccount( - userAccount: UserAccount - ): PerpPosition[]; - - getActivePerpPositions(): PerpPosition[]; - - getActivePerpPositionsAndSlot(): DataAndSlot; - - getActiveSpotPositionsForUserAccount( - userAccount: UserAccount - ): SpotPosition[]; - - getActiveSpotPositions(): SpotPosition[]; - - getActiveSpotPositionsAndSlot(): DataAndSlot; - - /** - * calculates unrealized position price pnl - * @returns : Precision QUOTE_PRECISION - */ - getUnrealizedPNL( - withFunding?: boolean, - marketIndex?: number, - withWeightMarginCategory?: MarginCategory, - strict?: boolean, - liquidationBuffer?: BN - ): BN; - - /** - * calculates unrealized funding payment pnl - * @returns : Precision QUOTE_PRECISION - */ - getUnrealizedFundingPNL(marketIndex?: number): BN; - - getFuelBonus( - now: BN, - includeSettled?: boolean, - includeUnsettled?: boolean, - givenUserStats?: IUserStats - ): { - depositFuel: BN; - borrowFuel: BN; - positionFuel: BN; - takerFuel: BN; - makerFuel: BN; - insuranceFuel: BN; - }; - - getSpotMarketAssetAndLiabilityValue( - marketIndex?: number, - marginCategory?: MarginCategory, - liquidationBuffer?: BN, - includeOpenOrders?: boolean, - strict?: boolean, - now?: BN - ): { totalAssetValue: BN; totalLiabilityValue: BN }; - - getSpotMarketLiabilityValue( - marketIndex?: number, - marginCategory?: MarginCategory, - liquidationBuffer?: BN, - includeOpenOrders?: boolean, - strict?: boolean, - now?: BN - ): BN; - - getSpotLiabilityValue( - tokenAmount: BN, - strictOraclePrice: StrictOraclePrice, - spotMarketAccount: SpotMarketAccount, - marginCategory?: MarginCategory, - liquidationBuffer?: BN - ): BN; - - getSpotMarketAssetValue( - marketIndex?: number, - marginCategory?: MarginCategory, - includeOpenOrders?: boolean, - strict?: boolean, - now?: BN - ): BN; - - getSpotAssetValue( - tokenAmount: BN, - strictOraclePrice: StrictOraclePrice, - spotMarketAccount: SpotMarketAccount, - marginCategory?: MarginCategory - ): BN; - - getSpotPositionValue( - marketIndex: number, - marginCategory?: MarginCategory, - includeOpenOrders?: boolean, - strict?: boolean, - now?: BN - ): BN; - - getNetSpotMarketValue(withWeightMarginCategory?: MarginCategory): BN; - - /** - * calculates TotalCollateral: collateral + unrealized pnl - * @returns : Precision QUOTE_PRECISION - */ - getTotalCollateral( - marginCategory?: MarginCategory, - strict?: boolean, - includeOpenOrders?: boolean, - liquidationBuffer?: BN - ): BN; - - getLiquidationBuffer(): BN | undefined; - - /** - * calculates User Health by comparing total collateral and maint. margin requirement - * @returns : number (value from [0, 100]) - */ - getHealth(): number; - - calculateWeightedPerpPositionLiability( - perpPosition: PerpPosition, - marginCategory?: MarginCategory, - liquidationBuffer?: BN, - includeOpenOrders?: boolean, - strict?: boolean, - enteringHighLeverage?: boolean - ): BN; - - /** - * calculates position value of a single perp market in margin system - * @returns : Precision QUOTE_PRECISION - */ - getPerpMarketLiabilityValue( - marketIndex: number, - marginCategory?: MarginCategory, - liquidationBuffer?: BN, - includeOpenOrders?: boolean, - strict?: boolean - ): BN; - - /** - * calculates sum of position value across all positions in margin system - * @returns : Precision QUOTE_PRECISION - */ - getTotalPerpPositionLiability( - marginCategory?: MarginCategory, - liquidationBuffer?: BN, - includeOpenOrders?: boolean, - strict?: boolean, - enteringHighLeverage?: boolean - ): BN; - - /** - * calculates position value based on oracle - * @returns : Precision QUOTE_PRECISION - */ - getPerpPositionValue( - marketIndex: number, - oraclePriceData: OraclePriceData, - includeOpenOrders?: boolean - ): BN; - - /** - * calculates position liabiltiy value in margin system - * @returns : Precision QUOTE_PRECISION - */ - getPerpLiabilityValue( - marketIndex: number, - oraclePriceData: OraclePriceData, - includeOpenOrders?: boolean - ): BN; - - getPositionSide( - currentPosition: Pick - ): PositionDirection | undefined; - - /** - * calculates average exit price (optionally for closing up to 100% of position) - * @returns : Precision PRICE_PRECISION - */ - getPositionEstimatedExitPriceAndPnl( - position: PerpPosition, - amountToClose?: BN, - useAMMClose?: boolean - ): [BN, BN]; - - /** - * calculates current user leverage which is (total liability size) / (net asset value) - * @returns : Precision TEN_THOUSAND - */ - getLeverage(includeOpenOrders?: boolean): BN; - - calculateLeverageFromComponents({ - perpLiabilityValue, - perpPnl, - spotAssetValue, - spotLiabilityValue, - }: { - perpLiabilityValue: BN; - perpPnl: BN; - spotAssetValue: BN; - spotLiabilityValue: BN; - }): BN; - - getLeverageComponents( - includeOpenOrders?: boolean, - marginCategory?: MarginCategory - ): { - perpLiabilityValue: BN; - perpPnl: BN; - spotAssetValue: BN; - spotLiabilityValue: BN; - }; - - isDustDepositPosition(spotMarketAccount: SpotMarketAccount): boolean; - - getSpotMarketAccountsWithDustPosition(): SpotMarketAccount[]; - - getTotalLiabilityValue(marginCategory?: MarginCategory): BN; - - getTotalAssetValue(marginCategory?: MarginCategory): BN; - - getNetUsdValue(): BN; - - /** - * Calculates the all time P&L of the user. - * - * Net withdraws + Net spot market value + Net unrealized P&L - - */ - getTotalAllTimePnl(): BN; - - /** - * calculates max allowable leverage exceeding hitting requirement category - * for large sizes where imf factor activates, result is a lower bound - * @param marginCategory {Initial, Maintenance} - * @param isLp if calculating max leveraging for adding lp, need to add buffer - * @param enterHighLeverageMode can pass this as true to calculate max leverage if the user was to enter high leverage mode - * @returns : Precision TEN_THOUSAND - */ - getMaxLeverageForPerp( - perpMarketIndex: number, - _marginCategory?: MarginCategory, - isLp?: boolean, - enterHighLeverageMode?: boolean - ): BN; - - /** - * calculates max allowable leverage exceeding hitting requirement category - * @param spotMarketIndex - * @param direction - * @returns : Precision TEN_THOUSAND - */ - getMaxLeverageForSpot( - spotMarketIndex: number, - direction: PositionDirection - ): BN; - - /** - * calculates margin ratio: 1 / leverage - * @returns : Precision TEN_THOUSAND - */ - getMarginRatio(): BN; - - canBeLiquidated(): { - canBeLiquidated: boolean; - marginRequirement: BN; - totalCollateral: BN; - }; - - isBeingLiquidated(): boolean; - - hasStatus(status: UserStatus): boolean; - - isBankrupt(): boolean; - - isHighLeverageMode(marginCategory: MarginCategory): boolean; - - /** - * Checks if any user position cumulative funding differs from respective market cumulative funding - * @returns - */ - needsToSettleFundingPayment(): boolean; - - /** - * Calculate the liquidation price of a spot position - * @param marketIndex - * @returns Precision : PRICE_PRECISION - */ - spotLiquidationPrice(marketIndex: number, positionBaseSizeChange?: BN): BN; - - /** - * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade - * @param marketIndex - * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^9 - * @param estimatedEntryPrice - * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking - * @param includeOpenOrders - * @param offsetCollateral // allows calculating the liquidation price after this offset collateral is added to the user's account (e.g. : what will the liquidation price be for this position AFTER I deposit $x worth of collateral) - * @returns Precision : PRICE_PRECISION - */ - liquidationPrice( - marketIndex: number, - positionBaseSizeChange?: BN, - estimatedEntryPrice?: BN, - marginCategory?: MarginCategory, - includeOpenOrders?: boolean, - offsetCollateral?: BN, - enteringHighLeverage?: boolean - ): BN; - - calculateEntriesEffectOnFreeCollateral( - market: PerpMarketAccount, - oraclePrice: BN, - perpPosition: PerpPosition, - positionBaseSizeChange: BN, - estimatedEntryPrice: BN, - includeOpenOrders: boolean, - enteringHighLeverage?: boolean, - marginCategory?: MarginCategory - ): BN; - - calculateFreeCollateralDeltaForPerp( - market: PerpMarketAccount, - perpPosition: PerpPosition, - positionBaseSizeChange: BN, - oraclePrice: BN, - marginCategory?: MarginCategory, - includeOpenOrders?: boolean, - enteringHighLeverage?: boolean - ): BN | undefined; - - calculateFreeCollateralDeltaForSpot( - market: SpotMarketAccount, - signedTokenAmount: BN, - marginCategory?: MarginCategory - ): BN; - - /** - * Calculates the estimated liquidation price for a position after closing a quote amount of the position. - * @param positionMarketIndex - * @param closeQuoteAmount - * @returns : Precision PRICE_PRECISION - */ - liquidationPriceAfterClose( - positionMarketIndex: number, - closeQuoteAmount: BN, - estimatedEntryPrice?: BN - ): BN; - - getMarginUSDCRequiredForTrade( - targetMarketIndex: number, - baseSize: BN, - estEntryPrice?: BN - ): BN; - - getCollateralDepositRequiredForTrade( - targetMarketIndex: number, - baseSize: BN, - collateralIndex: number - ): BN; - - /** - * Separates the max trade size into two parts: - * - tradeSize: The maximum trade size for target direction - * - oppositeSideTradeSize: the trade size for closing the opposite direction - * @param targetMarketIndex - * @param tradeSide - * @param isLp - * @returns { tradeSize: BN, oppositeSideTradeSize: BN} : Precision QUOTE_PRECISION - */ - getMaxTradeSizeUSDCForPerp( - targetMarketIndex: number, - tradeSide: PositionDirection, - isLp?: boolean, - enterHighLeverageMode?: boolean - ): { tradeSize: BN; oppositeSideTradeSize: BN }; - - /** - * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc. - * - * @param targetMarketIndex - * @param direction - * @param currentQuoteAssetValue - * @param currentSpotMarketNetValue - * @returns tradeSizeAllowed : Precision QUOTE_PRECISION - */ - getMaxTradeSizeUSDCForSpot( - targetMarketIndex: number, - direction: PositionDirection, - currentQuoteAssetValue?: BN, - currentSpotMarketNetValue?: BN - ): BN; - - /** - * Calculates the max amount of token that can be swapped from inMarket to outMarket - * Assumes swap happens at oracle price - * - * @param inMarketIndex - * @param outMarketIndex - * @param calculateSwap function to similate in to out swa - * @param iterationLimit how long to run appromixation before erroring out - */ - getMaxSwapAmount({ - inMarketIndex, - outMarketIndex, - calculateSwap, - iterationLimit, - }: { - inMarketIndex: number; - outMarketIndex: number; - calculateSwap?: (inAmount: BN) => BN; - iterationLimit?: number; - }): { inAmount: BN; outAmount: BN; leverage: BN }; - - cloneAndUpdateSpotPosition( - position: SpotPosition, - tokenAmount: BN, - market: SpotMarketAccount - ): SpotPosition; - - calculateSpotPositionFreeCollateralContribution( - spotPosition: SpotPosition, - strictOraclePrice: StrictOraclePrice - ): BN; - - calculateSpotPositionLeverageContribution( - spotPosition: SpotPosition, - strictOraclePrice: StrictOraclePrice - ): { - totalAssetValue: BN; - totalLiabilityValue: BN; - }; - - /** - * Estimates what the user leverage will be after swap - * @param inMarketIndex - * @param outMarketIndex - * @param inAmount - * @param outAmount - */ - accountLeverageAfterSwap({ - inMarketIndex, - outMarketIndex, - inAmount, - outAmount, - }: { - inMarketIndex: number; - outMarketIndex: number; - inAmount: BN; - outAmount: BN; - }): BN; - - /** - * Returns the leverage ratio for the account after adding (or subtracting) the given quote size to the given position - * @param targetMarketIndex - * @param: targetMarketType - * @param tradeQuoteAmount - * @param tradeSide - * @param includeOpenOrders - * @returns leverageRatio : Precision TEN_THOUSAND - */ - accountLeverageRatioAfterTrade( - targetMarketIndex: number, - targetMarketType: MarketType, - tradeQuoteAmount: BN, - tradeSide: PositionDirection, - includeOpenOrders?: boolean - ): BN; - - getUserFeeTier(marketType: MarketType, now?: BN): FeeTier; - - /** - * Calculates how much perp fee will be taken for a given sized trade - * @param quoteAmount - * @returns feeForQuote : Precision QUOTE_PRECISION - */ - calculateFeeForQuoteAmount( - quoteAmount: BN, - marketIndex?: number, - enteringHighLeverageMode?: boolean - ): BN; - - /** - * Calculates a user's max withdrawal amounts for a spot market. If reduceOnly is true, - * it will return the max withdrawal amount without opening a liability for the user - * @param marketIndex - * @returns withdrawalLimit : Precision is the token precision for the chosen SpotMarket - */ - getWithdrawalLimit(marketIndex: number, reduceOnly?: boolean): BN; - - canBypassWithdrawLimits(marketIndex: number): { - canBypass: boolean; - netDeposits: BN; - depositAmount: BN; - maxDepositAmount: BN; - }; - - canMakeIdle(slot: BN): boolean; - - getSafestTiers(): { perpTier: number; spotTier: number }; - - getPerpPositionHealth({ - marginCategory, - perpPosition, - oraclePriceData, - quoteOraclePriceData, - }: { - marginCategory: MarginCategory; - perpPosition: PerpPosition; - oraclePriceData?: OraclePriceData; - quoteOraclePriceData?: OraclePriceData; - }): HealthComponent; - - getHealthComponents({ - marginCategory, - }: { - marginCategory: MarginCategory; - }): HealthComponents; -} diff --git a/sdk/src/userConfig.ts b/sdk/src/userConfig.ts new file mode 100644 index 0000000000..f575ea2b58 --- /dev/null +++ b/sdk/src/userConfig.ts @@ -0,0 +1,32 @@ +import { DriftClient } from './driftClient'; +import { Commitment, PublicKey } from '@solana/web3.js'; +import { BulkAccountLoader } from './accounts/bulkAccountLoader'; +import { GrpcConfigs, UserAccountSubscriber } from './accounts/types'; + +export type UserConfig = { + accountSubscription?: UserSubscriptionConfig; + driftClient: DriftClient; + userAccountPublicKey: PublicKey; +}; + +export type UserSubscriptionConfig = + | { + type: 'grpc'; + resubTimeoutMs?: number; + logResubMessages?: boolean; + grpcConfigs: GrpcConfigs; + } + | { + type: 'websocket'; + resubTimeoutMs?: number; + logResubMessages?: boolean; + commitment?: Commitment; + } + | { + type: 'polling'; + accountLoader: BulkAccountLoader; + } + | { + type: 'custom'; + userAccountSubscriber: UserAccountSubscriber; + }; diff --git a/sdk/src/userMap/PollingSubscription.ts b/sdk/src/userMap/PollingSubscription.ts index 1e53623e0f..e398e61e08 100644 --- a/sdk/src/userMap/PollingSubscription.ts +++ b/sdk/src/userMap/PollingSubscription.ts @@ -1,7 +1,7 @@ -import { IUserMap } from './types'; +import { UserMap } from './userMap'; export class PollingSubscription { - private userMap: IUserMap; + private userMap: UserMap; private frequency: number; private skipInitialLoad: boolean; @@ -12,7 +12,7 @@ export class PollingSubscription { frequency, skipInitialLoad = false, }: { - userMap: IUserMap; + userMap: UserMap; frequency: number; skipInitialLoad?: boolean; includeIdle?: boolean; diff --git a/sdk/src/userMap/WebsocketSubscription.ts b/sdk/src/userMap/WebsocketSubscription.ts index 19832eb8e4..f689cfb7d7 100644 --- a/sdk/src/userMap/WebsocketSubscription.ts +++ b/sdk/src/userMap/WebsocketSubscription.ts @@ -1,12 +1,12 @@ +import { UserMap } from './userMap'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; -import { IUserMap } from './types'; export class WebsocketSubscription { - private userMap: IUserMap; + private userMap: UserMap; private commitment: Commitment; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; @@ -25,7 +25,7 @@ export class WebsocketSubscription { decodeFn, additionalFilters = undefined, }: { - userMap: IUserMap; + userMap: UserMap; commitment: Commitment; skipInitialLoad?: boolean; resubOpts?: ResubOpts; diff --git a/sdk/src/userMap/events.ts b/sdk/src/userMap/events.ts deleted file mode 100644 index 522e693a4a..0000000000 --- a/sdk/src/userMap/events.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IUser } from '../user/types'; - -export interface UserEvents { - userUpdate: (payload: IUser) => void; - update: void; - error: (e: Error) => void; -} diff --git a/sdk/src/userMap/grpcSubscription.ts b/sdk/src/userMap/grpcSubscription.ts index d1df8b63f3..a39f8a543c 100644 --- a/sdk/src/userMap/grpcSubscription.ts +++ b/sdk/src/userMap/grpcSubscription.ts @@ -1,14 +1,14 @@ +import { UserMap } from './userMap'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/programAccount/webSocketProgramAccountSubscriber'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; -import { grpcProgramAccountSubscriber } from '../accounts/programAccount/grpcProgramAccountSubscriber'; -import { IUserMap } from './types'; +import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber'; export class grpcSubscription { private grpcConfigs: GrpcConfigs; - private userMap: IUserMap; + private userMap: UserMap; private skipInitialLoad: boolean; private resubOpts?: ResubOpts; private includeIdle?: boolean; @@ -27,7 +27,7 @@ export class grpcSubscription { additionalFilters = undefined, }: { grpcConfigs: GrpcConfigs; - userMap: IUserMap; + userMap: UserMap; skipInitialLoad?: boolean; resubOpts?: ResubOpts; includeIdle?: boolean; diff --git a/sdk/src/userMap/types.ts b/sdk/src/userMap/types.ts deleted file mode 100644 index 44b2b797f5..0000000000 --- a/sdk/src/userMap/types.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { IDriftClient } from '../driftClient/types'; -import { UserAccount, OrderRecord } from '../types'; -import { WrappedEvent } from '../events/types'; -import { DataAndSlot } from '../accounts/types'; -import { IDLOB, ProtectMakerParamsMap } from '../dlob/types'; -import { PublicKey } from '@solana/web3.js'; -import { UserAccountFilterCriteria as UserFilterCriteria } from './userMapConfig'; -import StrictEventEmitter from 'strict-event-emitter-types'; -import { EventEmitter } from 'events'; -import { UserEvents } from './events'; -import { IUser, UserSubscriptionConfig } from '../user/types'; - -export interface IUserMap { - driftClient: IDriftClient; - eventEmitter: StrictEventEmitter; - - subscribe(): Promise; - - addPubkey( - userAccountPublicKey: PublicKey, - userAccount?: UserAccount, - slot?: number, - accountSubscription?: UserSubscriptionConfig - ): Promise; - - has(key: string): boolean; - - /** - * gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned - * @param key userAccountPublicKey to get User for - * @returns user User | undefined - */ - get(key: string): IUser | undefined; - - getWithSlot(key: string): DataAndSlot | undefined; - - /** - * gets the User for a particular userAccountPublicKey, if no User exists, new one is created - * @param key userAccountPublicKey to get User for - * @returns User - */ - mustGet( - key: string, - accountSubscription?: UserSubscriptionConfig - ): Promise; - - mustGetWithSlot( - key: string, - accountSubscription?: UserSubscriptionConfig - ): Promise>; - - mustGetUserAccount(key: string): Promise; - - /** - * gets the Authority for a particular userAccountPublicKey, if no User exists, undefined is returned - * @param key userAccountPublicKey to get User for - * @returns authority PublicKey | undefined - */ - getUserAuthority(key: string): PublicKey | undefined; - - /** - * implements the {@link DLOBSource} interface - * create a DLOB from all the subscribed users - * @param slot - */ - getDLOB( - slot: number, - protectedMakerParamsMap?: ProtectMakerParamsMap - ): Promise; - - updateWithOrderRecord(record: OrderRecord): Promise; - - updateWithEventRecord(record: WrappedEvent): Promise; - - values(): IterableIterator; - - valuesWithSlot(): IterableIterator>; - - entries(): IterableIterator<[string, IUser]>; - - entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; - - size(): number; - - /** - * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria - * @param filterCriteria: Users must meet these criteria to be included - * @returns - */ - getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[]; - - sync(): Promise; - - unsubscribe(): Promise; - - updateUserAccount( - key: string, - userAccount: UserAccount, - slot: number - ): Promise; - - updateLatestSlot(slot: number): void; - - getSlot(): number; -} diff --git a/sdk/src/userMap/userMap.ts b/sdk/src/userMap/userMap.ts index bdbb79cf3f..f022be058a 100644 --- a/sdk/src/userMap/userMap.ts +++ b/sdk/src/userMap/userMap.ts @@ -1,5 +1,6 @@ import { BN } from '@coral-xyz/anchor'; import { User } from '../user'; +import { DriftClient } from '../driftClient'; import { UserAccount, OrderRecord, @@ -14,9 +15,9 @@ import { } from '../types'; import { WrappedEvent } from '../events/types'; import { DLOB } from '../dlob/DLOB'; -import { UserSubscriptionConfig } from '../user/types'; -import { DataAndSlot } from '../accounts/types'; -import { OneShotUserAccountSubscriber } from '../accounts/userAccount/oneShotUserAccountSubscriber'; +import { UserSubscriptionConfig } from '../userConfig'; +import { DataAndSlot, UserEvents } from '../accounts/types'; +import { OneShotUserAccountSubscriber } from '../accounts/oneShotUserAccountSubscriber'; import { ProtectMakerParamsMap } from '../dlob/types'; import { @@ -44,15 +45,41 @@ import { decodeUser } from '../decode/user'; import { grpcSubscription } from './grpcSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { UserEvents } from './events'; -import { IUserMap } from './types'; -import { IDriftClient } from '../driftClient/types'; const MAX_USER_ACCOUNT_SIZE_BYTES = 4376; -export class UserMap implements IUserMap { +export interface UserMapInterface { + eventEmitter: StrictEventEmitter; + subscribe(): Promise; + unsubscribe(): Promise; + addPubkey( + userAccountPublicKey: PublicKey, + userAccount?: UserAccount, + slot?: number, + accountSubscription?: UserSubscriptionConfig + ): Promise; + has(key: string): boolean; + get(key: string): User | undefined; + getWithSlot(key: string): DataAndSlot | undefined; + mustGet( + key: string, + accountSubscription?: UserSubscriptionConfig + ): Promise; + mustGetWithSlot( + key: string, + accountSubscription?: UserSubscriptionConfig + ): Promise>; + getUserAuthority(key: string): PublicKey | undefined; + updateWithOrderRecord(record: OrderRecord): Promise; + values(): IterableIterator; + valuesWithSlot(): IterableIterator>; + entries(): IterableIterator<[string, User]>; + entriesWithSlot(): IterableIterator<[string, DataAndSlot]>; +} + +export class UserMap implements UserMapInterface { private userMap = new Map>(); - driftClient: IDriftClient; + driftClient: DriftClient; eventEmitter: StrictEventEmitter; private connection: Connection; private commitment: Commitment; diff --git a/sdk/src/userMap/userMapConfig.ts b/sdk/src/userMap/userMapConfig.ts index 31bd29fc9c..afb980c3e7 100644 --- a/sdk/src/userMap/userMapConfig.ts +++ b/sdk/src/userMap/userMapConfig.ts @@ -1,5 +1,5 @@ import { Commitment, Connection, MemcmpFilter } from '@solana/web3.js'; -import { IDriftClient } from '../driftClient/types'; +import { DriftClient } from '../driftClient'; import { GrpcConfigs } from '../accounts/types'; // passed into UserMap.getUniqueAuthorities to filter users @@ -19,7 +19,7 @@ export type SyncConfig = }; export type UserMapConfig = { - driftClient: IDriftClient; + driftClient: DriftClient; // connection object to use specifically for the UserMap. If undefined, will use the driftClient's connection connection?: Connection; subscriptionConfig: diff --git a/sdk/src/userMap/userStatsMap.ts b/sdk/src/userMap/userStatsMap.ts index 074ce3eedc..8e9abee863 100644 --- a/sdk/src/userMap/userStatsMap.ts +++ b/sdk/src/userMap/userStatsMap.ts @@ -14,8 +14,8 @@ import { } from '../types'; import { UserStats } from '../userStats'; import { WrappedEvent } from '../events/types'; -import { BulkAccountLoader } from '../accounts/bulkAccountLoader/bulkAccountLoader'; -import { PollingUserStatsAccountSubscriber } from '../accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; +import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; +import { PollingUserStatsAccountSubscriber } from '../accounts/pollingUserStatsAccountSubscriber'; import { SyncConfig } from './userMapConfig'; import { getUserStatsFilter } from '../memcmp'; import { PublicKey } from '@solana/web3.js'; diff --git a/sdk/src/userStats/index.ts b/sdk/src/userStats.ts similarity index 80% rename from sdk/src/userStats/index.ts rename to sdk/src/userStats.ts index e1b2fdc7c4..8fdb30d2bf 100644 --- a/sdk/src/userStats/index.ts +++ b/sdk/src/userStats.ts @@ -1,33 +1,26 @@ +import { DriftClient } from './driftClient'; import { PublicKey } from '@solana/web3.js'; -import { DataAndSlot, UserStatsAccountSubscriber } from '../accounts/types'; -import { UserStatsSubscriptionConfig } from '../userStatsConfig'; -import { PollingUserStatsAccountSubscriber } from '../accounts/userStatsAccount/pollingUserStatsAccountSubscriber'; -import { WebSocketUserStatsAccountSubscriber } from '../accounts/userStatsAccount/webSocketUserStatsAccountSubsriber'; -import { ReferrerInfo, SpotMarketAccount, UserStatsAccount } from '../types'; +import { DataAndSlot, UserStatsAccountSubscriber } from './accounts/types'; +import { UserStatsConfig } from './userStatsConfig'; +import { PollingUserStatsAccountSubscriber } from './accounts/pollingUserStatsAccountSubscriber'; +import { WebSocketUserStatsAccountSubscriber } from './accounts/webSocketUserStatsAccountSubsriber'; +import { ReferrerInfo, SpotMarketAccount, UserStatsAccount } from './types'; import { getUserAccountPublicKeySync, getUserStatsAccountPublicKey, -} from '../addresses/pda'; -import { grpcUserStatsAccountSubscriber } from '../accounts/userStatsAccount/grpcUserStatsAccountSubscriber'; -import { FUEL_START_TS } from '../constants/numericConstants'; -import { ZERO } from '../constants/numericConstants'; +} from './addresses/pda'; +import { grpcUserStatsAccountSubscriber } from './accounts/grpcUserStatsAccountSubscriber'; +import { FUEL_START_TS } from './constants/numericConstants'; +import { ZERO } from './constants/numericConstants'; import { GOV_SPOT_MARKET_INDEX, QUOTE_SPOT_MARKET_INDEX, -} from '../constants/numericConstants'; +} from './constants/numericConstants'; import { BN } from '@coral-xyz/anchor'; -import { calculateInsuranceFuelBonus } from '../math/fuel'; -import { IDriftClient } from '../driftClient/types'; -import { IUserStats } from './types'; +import { calculateInsuranceFuelBonus } from './math/fuel'; -export type UserStatsConfig = { - accountSubscription?: UserStatsSubscriptionConfig; - driftClient: IDriftClient; - userStatsAccountPublicKey: PublicKey; -}; - -export class UserStats implements IUserStats { - driftClient: IDriftClient; +export class UserStats { + driftClient: DriftClient; userStatsAccountPublicKey: PublicKey; accountSubscriber: UserStatsAccountSubscriber; isSubscribed: boolean; diff --git a/sdk/src/userStats/types.ts b/sdk/src/userStats/types.ts deleted file mode 100644 index 3b8e9b0319..0000000000 --- a/sdk/src/userStats/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BN } from '@coral-xyz/anchor'; -import { DataAndSlot, UserStatsAccountSubscriber } from '../accounts/types'; -import { UserStatsAccount } from '../types'; -import { ReferrerInfo } from '../types'; -import { PublicKey } from '@solana/web3.js'; - -export interface IUserStats { - userStatsAccountPublicKey: PublicKey; - accountSubscriber: UserStatsAccountSubscriber; - isSubscribed: boolean; - - subscribe(userStatsAccount?: UserStatsAccount): Promise; - fetchAccounts(): Promise; - unsubscribe(): Promise; - getAccountAndSlot(): DataAndSlot; - getAccount(): UserStatsAccount; - getInsuranceFuelBonus( - now: BN, - includeSettled?: boolean, - includeUnsettled?: boolean - ): BN; - getReferrerInfo(): ReferrerInfo | undefined; -} diff --git a/sdk/src/userStatsConfig.ts b/sdk/src/userStatsConfig.ts index a1866a1182..693de80e85 100644 --- a/sdk/src/userStatsConfig.ts +++ b/sdk/src/userStatsConfig.ts @@ -1,7 +1,14 @@ -import { Commitment } from '@solana/web3.js'; -import { BulkAccountLoader } from './accounts/bulkAccountLoader/bulkAccountLoader'; +import { DriftClient } from './driftClient'; +import { Commitment, PublicKey } from '@solana/web3.js'; +import { BulkAccountLoader } from './accounts/bulkAccountLoader'; import { GrpcConfigs } from './accounts/types'; +export type UserStatsConfig = { + accountSubscription?: UserStatsSubscriptionConfig; + driftClient: DriftClient; + userStatsAccountPublicKey: PublicKey; +}; + export type UserStatsSubscriptionConfig = | { type: 'websocket'; diff --git a/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts b/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts index e032259e14..9058ead1de 100644 --- a/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts +++ b/sdk/tests/accounts/customizedCadenceBulkAccountLoader.test.ts @@ -1,5 +1,5 @@ import { Connection, Keypair, PublicKey } from '@solana/web3.js'; -import { CustomizedCadenceBulkAccountLoader } from '../../src/accounts/bulkAccountLoader/customizedCadenceBulkAccountLoader'; +import { CustomizedCadenceBulkAccountLoader } from '../../src/accounts/customizedCadenceBulkAccountLoader'; import { expect } from 'chai'; describe('CustomizedCadenceBulkAccountLoader', () => { diff --git a/tests/admin.ts b/tests/admin.ts index d6d754d953..b92fbd496e 100644 --- a/tests/admin.ts +++ b/tests/admin.ts @@ -25,7 +25,7 @@ import { BankrunContextWrapper, Connection, } from '../sdk/src/bankrun/bankrunConnection'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; describe('admin', () => { const chProgram = anchor.workspace.Drift as Program; diff --git a/tests/adminDeposit.ts b/tests/adminDeposit.ts index 004a2669e4..2eee301d8c 100644 --- a/tests/adminDeposit.ts +++ b/tests/adminDeposit.ts @@ -19,7 +19,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; dotenv.config(); diff --git a/tests/assetTier.ts b/tests/assetTier.ts index b5f6ae4293..0c1797b4ba 100644 --- a/tests/assetTier.ts +++ b/tests/assetTier.ts @@ -37,7 +37,7 @@ import { // sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { ContractTier } from '../sdk'; diff --git a/tests/cancelAllOrders.ts b/tests/cancelAllOrders.ts index 4a10f61255..d3cd08fcdb 100644 --- a/tests/cancelAllOrders.ts +++ b/tests/cancelAllOrders.ts @@ -19,7 +19,7 @@ import { mockOracleNoProgram, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { isVariant } from '../sdk'; diff --git a/tests/cappedSymFunding.ts b/tests/cappedSymFunding.ts index a1c1350e2f..a95fa58e1e 100644 --- a/tests/cappedSymFunding.ts +++ b/tests/cappedSymFunding.ts @@ -36,7 +36,7 @@ import { Program } from '@coral-xyz/anchor'; import { Keypair, PublicKey } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function updateFundingRateHelper( diff --git a/tests/curve.ts b/tests/curve.ts index 97f3e4a641..ff47909549 100644 --- a/tests/curve.ts +++ b/tests/curve.ts @@ -28,7 +28,7 @@ import { setFeedPriceNoProgram, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('AMM Curve', () => { diff --git a/tests/deleteInitializedSpotMarket.ts b/tests/deleteInitializedSpotMarket.ts index 60cee45fb8..f66267e3e8 100644 --- a/tests/deleteInitializedSpotMarket.ts +++ b/tests/deleteInitializedSpotMarket.ts @@ -23,7 +23,7 @@ import { } from '../sdk'; import { PublicKey } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max deposit', () => { diff --git a/tests/depositIntoSpotMarketVault.ts b/tests/depositIntoSpotMarketVault.ts index 5ef2b4cea1..476d984613 100644 --- a/tests/depositIntoSpotMarketVault.ts +++ b/tests/depositIntoSpotMarketVault.ts @@ -24,7 +24,7 @@ import { } from './testHelpers'; import { SPOT_MARKET_BALANCE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/driftClient.ts b/tests/driftClient.ts index 1f29bc9bc9..6271269e39 100644 --- a/tests/driftClient.ts +++ b/tests/driftClient.ts @@ -32,7 +32,7 @@ import { sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('drift client', () => { diff --git a/tests/fillSpot.ts b/tests/fillSpot.ts index cbdef680d8..2b141d1254 100644 --- a/tests/fillSpot.ts +++ b/tests/fillSpot.ts @@ -27,7 +27,7 @@ import { } from './testHelpers'; import { MARGIN_PRECISION, PostOnlyParams, ReferrerInfo, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('place and fill spot order', () => { diff --git a/tests/forceUserDelete.ts b/tests/forceUserDelete.ts index e13b2efa84..d224e6c7ec 100644 --- a/tests/forceUserDelete.ts +++ b/tests/forceUserDelete.ts @@ -26,7 +26,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { QUOTE_PRECISION, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/fuel.ts b/tests/fuel.ts index c15ce110ac..4514836014 100644 --- a/tests/fuel.ts +++ b/tests/fuel.ts @@ -32,7 +32,7 @@ import { import { QUOTE_PRECISION, calculatePerpFuelBonus } from '../sdk/src'; import { MARGIN_PRECISION, PostOnlyParams, ReferrerInfo, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe("fuelin'", () => { diff --git a/tests/fuelSweep.ts b/tests/fuelSweep.ts index b38062c8ea..a6890c45d8 100644 --- a/tests/fuelSweep.ts +++ b/tests/fuelSweep.ts @@ -21,7 +21,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; dotenv.config(); diff --git a/tests/govStakeDevnet.ts b/tests/govStakeDevnet.ts index 6eb41ee14f..bd7de44653 100644 --- a/tests/govStakeDevnet.ts +++ b/tests/govStakeDevnet.ts @@ -12,7 +12,7 @@ import { } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { diff --git a/tests/highLeverageMode.ts b/tests/highLeverageMode.ts index 70b5b48269..148c41ce92 100644 --- a/tests/highLeverageMode.ts +++ b/tests/highLeverageMode.ts @@ -33,7 +33,7 @@ import { PERCENTAGE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max leverage order params', () => { diff --git a/tests/ifRebalance.ts b/tests/ifRebalance.ts index 14d6135d2b..dcf4373dbf 100644 --- a/tests/ifRebalance.ts +++ b/tests/ifRebalance.ts @@ -39,7 +39,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { DexInstructions, Market, OpenOrders } from '@project-serum/serum'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot swap', () => { diff --git a/tests/imbalancePerpPnl.ts b/tests/imbalancePerpPnl.ts index 7486effc5a..84fbf8dd4b 100644 --- a/tests/imbalancePerpPnl.ts +++ b/tests/imbalancePerpPnl.ts @@ -47,7 +47,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function depositToFeePoolFromIF( diff --git a/tests/insuranceFundStake.ts b/tests/insuranceFundStake.ts index 1ee2b8593d..c90a1795ce 100644 --- a/tests/insuranceFundStake.ts +++ b/tests/insuranceFundStake.ts @@ -43,7 +43,7 @@ import { } from './testHelpers'; import { ContractTier, PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper, asBN, diff --git a/tests/ksolver.ts b/tests/ksolver.ts index 5db4c24709..d3c1d338fc 100644 --- a/tests/ksolver.ts +++ b/tests/ksolver.ts @@ -24,7 +24,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('AMM Curve', () => { diff --git a/tests/liquidateBorrowForPerpPnl.ts b/tests/liquidateBorrowForPerpPnl.ts index ad97045443..f04c9b1310 100644 --- a/tests/liquidateBorrowForPerpPnl.ts +++ b/tests/liquidateBorrowForPerpPnl.ts @@ -31,7 +31,7 @@ import { } from './testHelpers'; import { isVariant, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate borrow for perp pnl', () => { diff --git a/tests/liquidateMaxLps.ts b/tests/liquidateMaxLps.ts index 30ee5b985c..90d4c72190 100644 --- a/tests/liquidateMaxLps.ts +++ b/tests/liquidateMaxLps.ts @@ -24,7 +24,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max lp liq', () => { diff --git a/tests/liquidatePerp.ts b/tests/liquidatePerp.ts index 2df9deb212..ed4156bdbc 100644 --- a/tests/liquidatePerp.ts +++ b/tests/liquidatePerp.ts @@ -31,7 +31,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp (no open orders)', () => { diff --git a/tests/liquidatePerpAndLp.ts b/tests/liquidatePerpAndLp.ts index c09de10331..26edfcf242 100644 --- a/tests/liquidatePerpAndLp.ts +++ b/tests/liquidatePerpAndLp.ts @@ -33,7 +33,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp and lp', () => { diff --git a/tests/liquidatePerpPnlForDeposit.ts b/tests/liquidatePerpPnlForDeposit.ts index 65c38af0a1..cb42c23adb 100644 --- a/tests/liquidatePerpPnlForDeposit.ts +++ b/tests/liquidatePerpPnlForDeposit.ts @@ -36,7 +36,7 @@ import { UserStatus, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp pnl for deposit', () => { diff --git a/tests/liquidatePerpWithFill.ts b/tests/liquidatePerpWithFill.ts index ed19633acb..f4d0b61cd3 100644 --- a/tests/liquidatePerpWithFill.ts +++ b/tests/liquidatePerpWithFill.ts @@ -28,7 +28,7 @@ import { } from './testHelpers'; import { OrderType, PERCENTAGE_PRECISION, PerpOperation } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate perp (no open orders)', () => { diff --git a/tests/liquidateSpot.ts b/tests/liquidateSpot.ts index e56dc6b736..9b30c4bb27 100644 --- a/tests/liquidateSpot.ts +++ b/tests/liquidateSpot.ts @@ -34,7 +34,7 @@ import { } from './testHelpers'; import { PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate spot', () => { diff --git a/tests/liquidateSpotSocialLoss.ts b/tests/liquidateSpotSocialLoss.ts index ca49933deb..0695db6b18 100644 --- a/tests/liquidateSpotSocialLoss.ts +++ b/tests/liquidateSpotSocialLoss.ts @@ -29,7 +29,7 @@ import { } from './testHelpers'; import { isVariant, UserStatus, PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('liquidate spot w/ social loss', () => { diff --git a/tests/liquidateSpotWithSwap.ts b/tests/liquidateSpotWithSwap.ts index 47abd36266..f6966324ee 100644 --- a/tests/liquidateSpotWithSwap.ts +++ b/tests/liquidateSpotWithSwap.ts @@ -34,7 +34,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { DexInstructions, Market, OpenOrders } from '@project-serum/serum'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot swap', () => { diff --git a/tests/liquidityProvider.ts b/tests/liquidityProvider.ts index 6425b008d2..c865579d36 100644 --- a/tests/liquidityProvider.ts +++ b/tests/liquidityProvider.ts @@ -37,7 +37,7 @@ import { } from './testHelpers'; import { PerpPosition } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function adjustOraclePostSwap(baa, swapDirection, market, context) { diff --git a/tests/marketOrder.ts b/tests/marketOrder.ts index 5b9d6a2d95..f7303296f2 100644 --- a/tests/marketOrder.ts +++ b/tests/marketOrder.ts @@ -35,7 +35,7 @@ import { getAssociatedTokenAddressSync, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('market order', () => { diff --git a/tests/marketOrderBaseAssetAmount.ts b/tests/marketOrderBaseAssetAmount.ts index f4494cbfa9..e541222f97 100644 --- a/tests/marketOrderBaseAssetAmount.ts +++ b/tests/marketOrderBaseAssetAmount.ts @@ -15,7 +15,7 @@ import { mockOracleNoProgram, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('market orders', () => { diff --git a/tests/maxDeposit.ts b/tests/maxDeposit.ts index 57ed8d5559..626ff2fdec 100644 --- a/tests/maxDeposit.ts +++ b/tests/maxDeposit.ts @@ -12,7 +12,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max deposit', () => { diff --git a/tests/maxLeverageOrderParams.ts b/tests/maxLeverageOrderParams.ts index be8e08e954..4695105240 100644 --- a/tests/maxLeverageOrderParams.ts +++ b/tests/maxLeverageOrderParams.ts @@ -31,7 +31,7 @@ import { PERCENTAGE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('max leverage order params', () => { diff --git a/tests/modifyOrder.ts b/tests/modifyOrder.ts index a1f4ed90fc..9f67eea3f2 100644 --- a/tests/modifyOrder.ts +++ b/tests/modifyOrder.ts @@ -20,7 +20,7 @@ import { } from './testHelpers'; import { OrderType, TWO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('modify orders', () => { diff --git a/tests/multipleMakerOrders.ts b/tests/multipleMakerOrders.ts index 21aca163e5..af5473510a 100644 --- a/tests/multipleMakerOrders.ts +++ b/tests/multipleMakerOrders.ts @@ -26,7 +26,7 @@ import { } from './testHelpers'; import { ContractTier, MARGIN_PRECISION, OrderType } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('multiple maker orders', () => { diff --git a/tests/multipleSpotMakerOrders.ts b/tests/multipleSpotMakerOrders.ts index f4bf461e5f..411e79e474 100644 --- a/tests/multipleSpotMakerOrders.ts +++ b/tests/multipleSpotMakerOrders.ts @@ -29,7 +29,7 @@ import { MARGIN_PRECISION, OrderType, SpotOperation } from '../sdk/src'; import { LAMPORTS_PER_SOL } from '@solana/web3.js'; import { ContractTier } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('multiple maker orders', () => { diff --git a/tests/openbookTest.ts b/tests/openbookTest.ts index 16e49c6c5f..3d57b25d99 100644 --- a/tests/openbookTest.ts +++ b/tests/openbookTest.ts @@ -12,7 +12,7 @@ import { } from '../sdk/src'; import openbookIDL from '../sdk/src/idl/openbook.json'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { createOpenOrdersAccount, diff --git a/tests/oracleDiffSources.ts b/tests/oracleDiffSources.ts index e432fc0851..3971f60370 100644 --- a/tests/oracleDiffSources.ts +++ b/tests/oracleDiffSources.ts @@ -28,7 +28,7 @@ import { } from './testHelpers'; // import { PRICE_PRECISION, PEG_PRECISION, Wallet, DriftClient } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('oracle diff sources', () => { diff --git a/tests/oracleFillPriceGuardrails.ts b/tests/oracleFillPriceGuardrails.ts index c6215322ba..dc0b5758ca 100644 --- a/tests/oracleFillPriceGuardrails.ts +++ b/tests/oracleFillPriceGuardrails.ts @@ -31,7 +31,7 @@ import { PostOnlyParams, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('oracle fill guardrails', () => { diff --git a/tests/oracleOffsetOrders.ts b/tests/oracleOffsetOrders.ts index 0037353532..2f498c2e5d 100644 --- a/tests/oracleOffsetOrders.ts +++ b/tests/oracleOffsetOrders.ts @@ -30,7 +30,7 @@ import { } from './testHelpers'; import { calculateEntryPrice, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('oracle offset', () => { diff --git a/tests/order.ts b/tests/order.ts index d603a268c1..a7e8f1b45d 100644 --- a/tests/order.ts +++ b/tests/order.ts @@ -47,7 +47,7 @@ import { ZERO, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; const enumsAreEqual = ( diff --git a/tests/ordersWithSpread.ts b/tests/ordersWithSpread.ts index e7b589bb27..b3d82dc518 100644 --- a/tests/ordersWithSpread.ts +++ b/tests/ordersWithSpread.ts @@ -34,7 +34,7 @@ import { PEG_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('amm spread: market order', () => { diff --git a/tests/overwritePerpAccounts.ts b/tests/overwritePerpAccounts.ts index fdbf3cac3a..989558f399 100644 --- a/tests/overwritePerpAccounts.ts +++ b/tests/overwritePerpAccounts.ts @@ -18,7 +18,7 @@ import { overWritePerpMarket, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { diff --git a/tests/pauseDepositWithdraw.ts b/tests/pauseDepositWithdraw.ts index 5360f124d0..830cf6d5d3 100644 --- a/tests/pauseDepositWithdraw.ts +++ b/tests/pauseDepositWithdraw.ts @@ -36,7 +36,7 @@ import { SpotOperation, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw 22', () => { diff --git a/tests/pauseExchange.ts b/tests/pauseExchange.ts index 28d40955d2..8ca04cd020 100644 --- a/tests/pauseExchange.ts +++ b/tests/pauseExchange.ts @@ -19,7 +19,7 @@ import { initializeQuoteSpotMarket, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('Pause exchange', () => { diff --git a/tests/perpLpJit.ts b/tests/perpLpJit.ts index 6cd4aa53a8..fce1546a06 100644 --- a/tests/perpLpJit.ts +++ b/tests/perpLpJit.ts @@ -36,7 +36,7 @@ import { // sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; let lastOrderRecordsLength = 0; diff --git a/tests/perpLpRiskMitigation.ts b/tests/perpLpRiskMitigation.ts index 22c8ecd0c3..12b8037f31 100644 --- a/tests/perpLpRiskMitigation.ts +++ b/tests/perpLpRiskMitigation.ts @@ -35,7 +35,7 @@ import { // sleep, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function adjustOraclePostSwap(baa, swapDirection, market, context) { diff --git a/tests/phoenixTest.ts b/tests/phoenixTest.ts index c8a0cbc9f3..5d1bf8edbc 100644 --- a/tests/phoenixTest.ts +++ b/tests/phoenixTest.ts @@ -45,7 +45,7 @@ import { deserializeMarketData, TokenConfig } from '@ellipsis-labs/phoenix-sdk'; import * as Phoenix from '@ellipsis-labs/phoenix-sdk'; import { assert } from 'chai'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { BankrunProvider } from 'anchor-bankrun'; import { diff --git a/tests/placeAndMakePerp.ts b/tests/placeAndMakePerp.ts index 1b25409f71..efdea60451 100644 --- a/tests/placeAndMakePerp.ts +++ b/tests/placeAndMakePerp.ts @@ -27,7 +27,7 @@ import { } from './testHelpers'; import { PEG_PRECISION, PerpOperation, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('place and make perp order', () => { diff --git a/tests/placeAndMakeSignedMsgBankrun.ts b/tests/placeAndMakeSignedMsgBankrun.ts index 5e948e36fb..173e1364c4 100644 --- a/tests/placeAndMakeSignedMsgBankrun.ts +++ b/tests/placeAndMakeSignedMsgBankrun.ts @@ -62,7 +62,7 @@ import { PostOnlyParams, } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { nanoid } from 'nanoid'; diff --git a/tests/placeAndMakeSpotOrder.ts b/tests/placeAndMakeSpotOrder.ts index 22f4da8428..b5ec4cca97 100644 --- a/tests/placeAndMakeSpotOrder.ts +++ b/tests/placeAndMakeSpotOrder.ts @@ -28,7 +28,7 @@ import { } from './testHelpers'; import { MARGIN_PRECISION, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('place and make spot order', () => { diff --git a/tests/postOnly.ts b/tests/postOnly.ts index b9ae5e95b2..f680dedbaa 100644 --- a/tests/postOnly.ts +++ b/tests/postOnly.ts @@ -33,7 +33,7 @@ import { ZERO, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('post only', () => { diff --git a/tests/postOnlyAmmFulfillment.ts b/tests/postOnlyAmmFulfillment.ts index 085177f220..388f0fc595 100644 --- a/tests/postOnlyAmmFulfillment.ts +++ b/tests/postOnlyAmmFulfillment.ts @@ -33,7 +33,7 @@ import { } from './testHelpers'; import { convertToNumber, PostOnlyParams } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('post only maker order w/ amm fulfillments', () => { diff --git a/tests/prelisting.ts b/tests/prelisting.ts index e26889d75b..92f7a510af 100644 --- a/tests/prelisting.ts +++ b/tests/prelisting.ts @@ -28,7 +28,7 @@ import { PostOnlyParams, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('prelisting', () => { diff --git a/tests/pyth.ts b/tests/pyth.ts index c9a066f020..d437bf60e8 100644 --- a/tests/pyth.ts +++ b/tests/pyth.ts @@ -29,7 +29,7 @@ import { QUOTE_SPOT_MARKET_INDEX, } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function updateFundingRateHelper( diff --git a/tests/pythLazerBankrun.ts b/tests/pythLazerBankrun.ts index fd0b976209..7e1721bea9 100644 --- a/tests/pythLazerBankrun.ts +++ b/tests/pythLazerBankrun.ts @@ -12,7 +12,7 @@ import { getPythLazerOraclePublicKey, isVariant, } from '../sdk/src'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { startAnchor } from 'solana-bankrun'; import { AccountInfo, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; diff --git a/tests/pythPull.ts b/tests/pythPull.ts index 14f333ce12..5118651af6 100644 --- a/tests/pythPull.ts +++ b/tests/pythPull.ts @@ -5,7 +5,7 @@ import { TestClient, getPythPullOraclePublicKey, } from '../sdk/src'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { startAnchor } from 'solana-bankrun'; import { AccountInfo, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; diff --git a/tests/referencePriceOffset.ts b/tests/referencePriceOffset.ts index 5e3b910150..5bfa89007e 100644 --- a/tests/referencePriceOffset.ts +++ b/tests/referencePriceOffset.ts @@ -30,7 +30,7 @@ import { placeAndFillVammTrade, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { diff --git a/tests/referrer.ts b/tests/referrer.ts index e712b2c98f..a310bcd24a 100644 --- a/tests/referrer.ts +++ b/tests/referrer.ts @@ -31,7 +31,7 @@ import { } from '../sdk/src'; import { decodeName } from '../sdk/src/userName'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('referrer', () => { diff --git a/tests/resizeSwiftUserOrderIds.ts b/tests/resizeSwiftUserOrderIds.ts index 2aaf0a9e69..e6ffa6d425 100644 --- a/tests/resizeSwiftUserOrderIds.ts +++ b/tests/resizeSwiftUserOrderIds.ts @@ -25,7 +25,7 @@ import { } from './testHelpers'; import { PEG_PRECISION } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; dotenv.config(); diff --git a/tests/roundInFavorBaseAsset.ts b/tests/roundInFavorBaseAsset.ts index 5e57443d66..c1a3b7dec0 100644 --- a/tests/roundInFavorBaseAsset.ts +++ b/tests/roundInFavorBaseAsset.ts @@ -22,7 +22,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('round in favor', () => { diff --git a/tests/serumTest.ts b/tests/serumTest.ts index c683edb2bb..7ede64540d 100644 --- a/tests/serumTest.ts +++ b/tests/serumTest.ts @@ -33,7 +33,7 @@ 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/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('serum spot market', () => { diff --git a/tests/signedMsgWsDelegates.ts b/tests/signedMsgWsDelegates.ts index 90f9227a70..990be3bb75 100644 --- a/tests/signedMsgWsDelegates.ts +++ b/tests/signedMsgWsDelegates.ts @@ -20,7 +20,7 @@ import { import { mockOracleNoProgram } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import dotenv from 'dotenv'; import { PYTH_STORAGE_DATA } from './pythLazerData'; diff --git a/tests/spotDepositWithdraw.ts b/tests/spotDepositWithdraw.ts index f9565bf604..71b412b9b6 100644 --- a/tests/spotDepositWithdraw.ts +++ b/tests/spotDepositWithdraw.ts @@ -41,7 +41,7 @@ import { PRICE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/spotDepositWithdraw22.ts b/tests/spotDepositWithdraw22.ts index 339fff0953..5bcd5cf5f1 100644 --- a/tests/spotDepositWithdraw22.ts +++ b/tests/spotDepositWithdraw22.ts @@ -41,7 +41,7 @@ import { PRICE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw 22', () => { diff --git a/tests/spotDepositWithdraw22TransferHooks.ts b/tests/spotDepositWithdraw22TransferHooks.ts index 6466886fe7..f01043172e 100644 --- a/tests/spotDepositWithdraw22TransferHooks.ts +++ b/tests/spotDepositWithdraw22TransferHooks.ts @@ -51,7 +51,7 @@ import { resolveExtraAccountMeta, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { initializeExtraAccountMetaList } from './splTransferHookClient'; diff --git a/tests/spotMarketPoolIds.ts b/tests/spotMarketPoolIds.ts index 1b2ce5c087..1395856599 100644 --- a/tests/spotMarketPoolIds.ts +++ b/tests/spotMarketPoolIds.ts @@ -25,7 +25,7 @@ import { } from './testHelpers'; import { QUOTE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('spot deposit and withdraw', () => { diff --git a/tests/spotSwap.ts b/tests/spotSwap.ts index 5aaa985c30..f60331cc4f 100644 --- a/tests/spotSwap.ts +++ b/tests/spotSwap.ts @@ -40,7 +40,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { DexInstructions, Market, OpenOrders } from '@project-serum/serum'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { DRIFT_PROGRAM_ID } from '../sdk/src'; diff --git a/tests/spotSwap22.ts b/tests/spotSwap22.ts index de9b72a350..a876a74504 100644 --- a/tests/spotSwap22.ts +++ b/tests/spotSwap22.ts @@ -36,7 +36,7 @@ import { createTransferInstruction, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { DRIFT_PROGRAM_ID } from '../sdk/src'; diff --git a/tests/spotWithdrawUtil100.ts b/tests/spotWithdrawUtil100.ts index 17568581fb..25cf31c10d 100644 --- a/tests/spotWithdrawUtil100.ts +++ b/tests/spotWithdrawUtil100.ts @@ -46,7 +46,7 @@ import { import { NATIVE_MINT } from '@solana/spl-token'; import { ContractTier } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('test function when spot market at >= 100% util', () => { diff --git a/tests/stopLimits.ts b/tests/stopLimits.ts index 2edd036a7b..3bcae9d15a 100644 --- a/tests/stopLimits.ts +++ b/tests/stopLimits.ts @@ -33,7 +33,7 @@ import { getAssociatedTokenAddressSync, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('stop limit', () => { diff --git a/tests/subaccounts.ts b/tests/subaccounts.ts index 79aac79eba..2f6f5c5cd2 100644 --- a/tests/subaccounts.ts +++ b/tests/subaccounts.ts @@ -30,7 +30,7 @@ import { } from '../sdk'; import { PublicKey } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('subaccounts', () => { diff --git a/tests/surgePricing.ts b/tests/surgePricing.ts index 26f19bfaac..4402201060 100644 --- a/tests/surgePricing.ts +++ b/tests/surgePricing.ts @@ -26,7 +26,7 @@ import { import { QUOTE_PRECISION, getUserAccountPublicKey } from '../sdk/src'; import { calculateInitUserFee } from '../sdk/src/math/state'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('surge pricing', () => { diff --git a/tests/switchOracle.ts b/tests/switchOracle.ts index 6980a712ec..7cd425e8c4 100644 --- a/tests/switchOracle.ts +++ b/tests/switchOracle.ts @@ -25,7 +25,7 @@ import { } from './testHelpers'; import { PRICE_PRECISION, PEG_PRECISION, Wallet, DriftClient } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('switch oracles', () => { diff --git a/tests/switchboardOnDemand.ts b/tests/switchboardOnDemand.ts index 278294639e..81e04d65d2 100644 --- a/tests/switchboardOnDemand.ts +++ b/tests/switchboardOnDemand.ts @@ -1,7 +1,7 @@ import * as anchor from '@coral-xyz/anchor'; import { Program } from '@coral-xyz/anchor'; import { AccountInfo, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { initializeQuoteSpotMarket, mockUSDCMint } from './testHelpers'; import { OracleSource, TestClient } from '../sdk/src'; diff --git a/tests/switchboardTxCus.ts b/tests/switchboardTxCus.ts index abb4ffb23c..fab886314d 100644 --- a/tests/switchboardTxCus.ts +++ b/tests/switchboardTxCus.ts @@ -30,7 +30,7 @@ import { PostOnlyParams, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('switchboard place orders cus', () => { diff --git a/tests/testHelpers.ts b/tests/testHelpers.ts index 45d777eb99..df4e742abe 100644 --- a/tests/testHelpers.ts +++ b/tests/testHelpers.ts @@ -58,7 +58,7 @@ import { BankrunConnection, } from '../sdk/src/bankrun/bankrunConnection'; import pythIDL from '../sdk/src/idl/pyth.json'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; export async function mockOracle( price: number = 50 * 10e7, diff --git a/tests/tokenFaucet.ts b/tests/tokenFaucet.ts index a9afed75d4..c18cf15699 100644 --- a/tests/tokenFaucet.ts +++ b/tests/tokenFaucet.ts @@ -12,7 +12,7 @@ import { unpackMint, } from '@solana/spl-token'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('token faucet', () => { diff --git a/tests/tradingLP.ts b/tests/tradingLP.ts index 8755c2095b..565178183e 100644 --- a/tests/tradingLP.ts +++ b/tests/tradingLP.ts @@ -21,7 +21,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; async function createNewUser( diff --git a/tests/transferPerpPosition.ts b/tests/transferPerpPosition.ts index 0171ce29e0..d2471a94d9 100644 --- a/tests/transferPerpPosition.ts +++ b/tests/transferPerpPosition.ts @@ -21,7 +21,7 @@ import { } from './testHelpers'; import { BASE_PRECISION, OracleSource, PERCENTAGE_PRECISION } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; function getOpenInterest(driftClient: TestClient, marketIndex: number) { diff --git a/tests/transferPools.ts b/tests/transferPools.ts index 08632c7e7c..a79d863601 100644 --- a/tests/transferPools.ts +++ b/tests/transferPools.ts @@ -26,7 +26,7 @@ import { } from './testHelpers'; import { QUOTE_PRECISION, ZERO } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; import { NATIVE_MINT } from '@solana/spl-token'; diff --git a/tests/triggerOrders.ts b/tests/triggerOrders.ts index e40473653a..99bf5f71ab 100644 --- a/tests/triggerOrders.ts +++ b/tests/triggerOrders.ts @@ -35,7 +35,7 @@ import { ZERO, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('trigger orders', () => { diff --git a/tests/triggerSpotOrder.ts b/tests/triggerSpotOrder.ts index a8808c2106..0f9277e2c3 100644 --- a/tests/triggerSpotOrder.ts +++ b/tests/triggerSpotOrder.ts @@ -32,7 +32,7 @@ import { PERCENTAGE_PRECISION, } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('trigger orders', () => { diff --git a/tests/userAccount.ts b/tests/userAccount.ts index 6f2f00e26b..96384ba98a 100644 --- a/tests/userAccount.ts +++ b/tests/userAccount.ts @@ -30,7 +30,7 @@ import { AMM_RESERVE_PRECISION, } from '../sdk/src'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('User Account', () => { diff --git a/tests/userDelegate.ts b/tests/userDelegate.ts index 4ed2660898..80dafcb539 100644 --- a/tests/userDelegate.ts +++ b/tests/userDelegate.ts @@ -24,7 +24,7 @@ import { import { assert } from 'chai'; import { Keypair } from '@solana/web3.js'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('user delegate', () => { diff --git a/tests/userOrderId.ts b/tests/userOrderId.ts index 7840e056d6..9ed86fd4f4 100644 --- a/tests/userOrderId.ts +++ b/tests/userOrderId.ts @@ -24,7 +24,7 @@ import { } from './testHelpers'; import { ContractTier, ExchangeStatus } from '../sdk'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('user order id', () => { diff --git a/tests/whitelist.ts b/tests/whitelist.ts index f7bb7b3c3e..66c5f15ead 100644 --- a/tests/whitelist.ts +++ b/tests/whitelist.ts @@ -28,7 +28,7 @@ import { mockUserUSDCAccount, } from './testHelpers'; import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/bulkAccountLoader/testBulkAccountLoader'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; describe('whitelist', () => { From 707f50a133a3bf275ffb816f067bc203955837b7 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:55:48 +0000 Subject: [PATCH 029/216] sdk: release v2.130.0-beta.14 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index f1837b1c2c..38da28b8a1 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.13 \ No newline at end of file +2.130.0-beta.14 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 1e754816ed..2a1c1a3d59 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.13", + "version": "2.130.0-beta.14", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 060f4b401fc66765fe0203d7c0535489848395dc Mon Sep 17 00:00:00 2001 From: wphan Date: Tue, 29 Jul 2025 16:22:34 -0700 Subject: [PATCH 030/216] sdk: restore grpc disconnect default behavior (#1782) --- sdk/src/accounts/grpcAccountSubscriber.ts | 35 ++++++++++++------ .../accounts/grpcProgramAccountSubscriber.ts | 37 +++++++++++++------ sdk/src/accounts/types.ts | 5 +++ 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/sdk/src/accounts/grpcAccountSubscriber.ts b/sdk/src/accounts/grpcAccountSubscriber.ts index 0ad6ba43cb..1ef65cd17c 100644 --- a/sdk/src/accounts/grpcAccountSubscriber.ts +++ b/sdk/src/accounts/grpcAccountSubscriber.ts @@ -18,6 +18,7 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { private stream: ClientDuplexStream; private commitmentLevel: CommitmentLevel; public listenerId?: number; + private enableReconnect: boolean; private constructor( client: Client, @@ -26,11 +27,13 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { program: Program, accountPublicKey: PublicKey, decodeBuffer?: (buffer: Buffer) => T, - resubOpts?: ResubOpts + resubOpts?: ResubOpts, + enableReconnect = false ) { super(accountName, program, accountPublicKey, decodeBuffer, resubOpts); this.client = client; this.commitmentLevel = commitmentLevel; + this.enableReconnect = enableReconnect; } public static async create( @@ -57,7 +60,8 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { program, accountPublicKey, decodeBuffer, - resubOpts + resubOpts, + grpcConfigs.enableReconnect ); } @@ -92,15 +96,24 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { transactionsStatus: {}, }; - this.stream.on('error', (error) => { - // @ts-ignore - if (error.code === 1) { - // expected: 1 CANCELLED: Cancelled on client - return; - } else { - console.error('GRPC unexpected error caught:', error); - } - }); + if (this.enableReconnect) { + this.stream.on('error', (error) => { + // @ts-ignore + if (error.code === 1) { + // expected: 1 CANCELLED: Cancelled on client + console.error( + 'GRPC (grpcAccountSubscriber) Cancelled on client caught:', + error + ); + return; + } else { + console.error( + 'GRPC (grpcAccountSubscriber) unexpected error caught:', + error + ); + } + }); + } this.stream.on('data', (chunk: SubscribeUpdate) => { if (!chunk.account) { diff --git a/sdk/src/accounts/grpcProgramAccountSubscriber.ts b/sdk/src/accounts/grpcProgramAccountSubscriber.ts index 73b6b1a077..d669b508e9 100644 --- a/sdk/src/accounts/grpcProgramAccountSubscriber.ts +++ b/sdk/src/accounts/grpcProgramAccountSubscriber.ts @@ -20,6 +20,7 @@ export class grpcProgramAccountSubscriber< private stream: ClientDuplexStream; private commitmentLevel: CommitmentLevel; public listenerId?: number; + private enableReconnect: boolean; private constructor( client: Client, @@ -31,7 +32,8 @@ export class grpcProgramAccountSubscriber< options: { filters: MemcmpFilter[] } = { filters: [], }, - resubOpts?: ResubOpts + resubOpts?: ResubOpts, + enableReconnect = false ) { super( subscriptionName, @@ -43,6 +45,7 @@ export class grpcProgramAccountSubscriber< ); this.client = client; this.commitmentLevel = commitmentLevel; + this.enableReconnect = enableReconnect; } public static async create( @@ -73,7 +76,8 @@ export class grpcProgramAccountSubscriber< program, decodeBufferFn, options, - resubOpts + resubOpts, + grpcConfigs.enableReconnect ); } @@ -119,15 +123,26 @@ export class grpcProgramAccountSubscriber< entry: {}, transactionsStatus: {}, }; - this.stream.on('error', (error) => { - // @ts-ignore - if (error.code === 1) { - // expected: 1 CANCELLED: Cancelled on client - return; - } else { - console.error('GRPC unexpected error caught:', error); - } - }); + + if (this.enableReconnect) { + this.stream.on('error', (error) => { + // @ts-ignore + if (error.code === 1) { + // expected: 1 CANCELLED: Cancelled on client + console.error( + 'GRPC (grpcProgramAccountSubscriber) Cancelled on client caught:', + error + ); + return; + } else { + console.error( + 'GRPC (grpcProgramAccountSubscriber) unexpected error caught:', + error + ); + } + }); + } + this.stream.on('data', (chunk: SubscribeUpdate) => { if (!chunk.account) { return; diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index a4b4425253..26bb9cfb49 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -226,6 +226,11 @@ export type GrpcConfigs = { token: string; commitmentLevel?: CommitmentLevel; channelOptions?: ChannelOptions; + /** + * Whether to enable automatic reconnection on connection loss . + * Defaults to false, will throw on connection loss. + */ + enableReconnect?: boolean; }; export interface HighLeverageModeConfigAccountSubscriber { From e0bf4ad6ce3022df04842180631b1d6016aa4e81 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 23:27:25 +0000 Subject: [PATCH 031/216] sdk: release v2.130.0-beta.15 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 38da28b8a1..ffea015a4b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.14 \ No newline at end of file +2.130.0-beta.15 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 2a1c1a3d59..09ea2b17ff 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.14", + "version": "2.130.0-beta.15", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From cb8da2c42f343aeea91fc0eea1dc738a8e0e42ad Mon Sep 17 00:00:00 2001 From: lil perp Date: Tue, 29 Jul 2025 20:21:16 -0400 Subject: [PATCH 032/216] program: trigger price based on oracle and basis (#1716) * init * shrink for time since last funding * add max diff * use trigger price isntead of oracle * add event * more tests * update for contract tier C * add ts code * return early instead of throw error * sdk: rename oraclePrice to triggerPrice * feature flag * styling * address comments * parentheses * casts * CHANGELOG --- CHANGELOG.md | 2 + programs/drift/src/controller/funding.rs | 1 + programs/drift/src/controller/liquidation.rs | 1 + programs/drift/src/controller/orders.rs | 29 ++- programs/drift/src/instructions/admin.rs | 52 +++-- programs/drift/src/instructions/user.rs | 1 + programs/drift/src/lib.rs | 13 +- programs/drift/src/math/constants.rs | 2 + programs/drift/src/state/events.rs | 6 +- programs/drift/src/state/perp_market.rs | 131 +++++++++++-- programs/drift/src/state/perp_market/tests.rs | 180 ++++++++++++++++++ programs/drift/src/state/state.rs | 12 +- sdk/src/adminClient.ts | 56 +++--- sdk/src/dlob/DLOB.ts | 6 +- sdk/src/idl/drift.json | 47 ++++- sdk/src/math/market.ts | 105 ++++++++++ sdk/src/math/state.ts | 8 +- sdk/src/types.ts | 9 + tests/admin.ts | 5 +- 19 files changed, 588 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59aaf6f7f3..647319efb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: new median price for trigger orders ([#1716](https://github.com/drift-labs/protocol-v2/pull/1716)) + ### Fixes ### Breaking diff --git a/programs/drift/src/controller/funding.rs b/programs/drift/src/controller/funding.rs index e249f12540..eaa2dd9833 100644 --- a/programs/drift/src/controller/funding.rs +++ b/programs/drift/src/controller/funding.rs @@ -271,6 +271,7 @@ pub fn update_funding_rate( .safe_add(funding_rate_short)?; market.amm.last_funding_rate = funding_rate; + market.amm.last_funding_oracle_twap = oracle_price_twap; market.amm.last_funding_rate_long = funding_rate_long.cast()?; market.amm.last_funding_rate_short = funding_rate_short.cast()?; market.amm.last_24h_avg_funding_rate = calculate_new_twap( diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 1e2e8eb82e..057f4b8168 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -698,6 +698,7 @@ pub fn liquidate_perp( taker_existing_base_asset_amount: taker_existing_base_asset_amount, maker_existing_quote_entry_amount: maker_existing_quote_entry_amount, maker_existing_base_asset_amount: maker_existing_base_asset_amount, + trigger_price: None, }; emit!(fill_record); diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 437809e50b..2ec096ce77 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -442,6 +442,7 @@ pub fn place_perp_order( None, None, None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -721,6 +722,7 @@ pub fn cancel_order( None, None, None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; } @@ -1324,7 +1326,7 @@ pub fn fill_perp_order( let fill_price = calculate_fill_price(quote_asset_amount, base_asset_amount, BASE_PRECISION_U64)?; - let perp_market = perp_market_map.get_ref(&market_index)?; + let mut perp_market = perp_market_map.get_ref_mut(&market_index)?; validate_fill_price_within_price_bands( fill_price, oracle_price, @@ -1335,6 +1337,8 @@ pub fn fill_perp_order( .max_oracle_twap_5min_percent_divergence(), perp_market.is_prediction_market(), )?; + + perp_market.last_fill_price = fill_price; } let base_asset_amount_after = user.perp_positions[position_index].base_asset_amount; @@ -2449,6 +2453,7 @@ pub fn fulfill_perp_order_with_amm( taker_existing_base_asset_amount, maker_existing_quote_entry_amount, maker_existing_base_asset_amount, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -2897,6 +2902,7 @@ pub fn fulfill_perp_order_with_match( taker_existing_base_asset_amount, maker_existing_quote_entry_amount, maker_existing_base_asset_amount, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -2989,11 +2995,10 @@ pub fn trigger_order( "Order is not triggerable" )?; - validate!( - !user.orders[order_index].triggered(), - ErrorCode::OrderNotTriggerable, - "Order is already triggered" - )?; + if user.orders[order_index].triggered() { + msg!("Order is already triggered"); + return Ok(()); + } validate!( market_type == MarketType::Perp, @@ -3049,10 +3054,9 @@ pub fn trigger_order( "oracle price vs twap too divergent" )?; - let can_trigger = order_satisfies_trigger_condition( - &user.orders[order_index], - oracle_price.unsigned_abs().cast()?, - )?; + let trigger_price = + perp_market.get_trigger_price(oracle_price, now, state.use_median_trigger_price())?; + let can_trigger = order_satisfies_trigger_condition(&user.orders[order_index], trigger_price)?; validate!(can_trigger, ErrorCode::OrderDidNotSatisfyTriggerCondition)?; let (_, worst_case_liability_value_before) = user @@ -3125,6 +3129,7 @@ pub fn trigger_order( None, None, None, + Some(trigger_price), )?; emit!(order_action_record); @@ -3810,6 +3815,7 @@ pub fn place_spot_order( None, None, None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5046,6 +5052,7 @@ pub fn fulfill_spot_order_with_match( None, None, None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5319,6 +5326,7 @@ pub fn fulfill_spot_order_with_external_market( None, None, None, + None, )?; emit_stack::<_, { OrderActionRecord::SIZE }>(order_action_record)?; @@ -5522,6 +5530,7 @@ pub fn trigger_spot_order( None, None, None, + Some(oracle_price.unsigned_abs()), )?; emit!(order_action_record); diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 0cff63f410..3b17025310 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -1,7 +1,7 @@ use std::convert::{identity, TryInto}; use std::mem::size_of; -use crate::msg; +use crate::{msg, FeatureBitFlags}; use anchor_lang::prelude::*; use anchor_spl::token_2022::Token2022; use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; @@ -114,7 +114,7 @@ pub fn handle_initialize(ctx: Context) -> Result<()> { initial_pct_to_liquidate: 0, max_number_of_sub_accounts: 0, max_initialize_user_fee: 0, - disable_bit_flags: 0, + feature_bit_flags: 0, padding: [0; 9], }; @@ -965,7 +965,9 @@ pub fn handle_initialize_perp_market( high_leverage_margin_ratio_maintenance: 0, protected_maker_limit_price_divisor: 0, protected_maker_dynamic_divisor: 0, - padding: [0; 36], + padding1: 0, + last_fill_price: 0, + padding: [0; 24], amm: AMM { oracle: *ctx.accounts.oracle.key, oracle_source, @@ -1061,7 +1063,8 @@ pub fn handle_initialize_perp_market( quote_asset_amount_with_unsettled_lp: 0, reference_price_offset: 0, amm_inventory_spread_adjustment: 0, - padding: [0; 11], + padding: [0; 3], + last_funding_oracle_twap: 0, }, }; @@ -4853,7 +4856,7 @@ pub fn handle_zero_mm_oracle_fields(ctx: Context) -> R pub fn handle_update_mm_oracle_native(accounts: &[AccountInfo], data: &[u8]) -> Result<()> { // Verify this ix is allowed let state = &accounts[3].data.borrow(); - assert!(state[982] & 1 == 0, "ix disabled by admin state"); + assert!(state[982] & 1 > 0, "ix disabled by admin state"); let signer_account = &accounts[1]; #[cfg(not(feature = "anchor-test"))] @@ -4898,22 +4901,47 @@ pub fn handle_update_amm_spread_adjustment_native( Ok(()) } -pub fn handle_update_disable_bitflags_mm_oracle( +pub fn handle_update_feature_bit_flags_mm_oracle( ctx: Context, - disable: bool, + enable: bool, ) -> Result<()> { let state = &mut ctx.accounts.state; - if disable { - msg!("Setting first bit to 1, disabling mm oracle update"); - state.disable_bit_flags = state.disable_bit_flags | 1; + if enable { + validate!( + ctx.accounts.admin.key().eq(&state.admin), + ErrorCode::DefaultError, + "Only state admin can re-enable after kill switch" + )?; + + msg!("Setting first bit to 1, enabling mm oracle update"); + state.feature_bit_flags = state.feature_bit_flags | (FeatureBitFlags::MmOracleUpdate as u8); } else { + msg!("Setting first bit to 0, disabling mm oracle update"); + state.feature_bit_flags = + state.feature_bit_flags & !(FeatureBitFlags::MmOracleUpdate as u8); + } + Ok(()) +} + +pub fn handle_update_feature_bit_flags_median_trigger_price( + ctx: Context, + enable: bool, +) -> Result<()> { + let state = &mut ctx.accounts.state; + if enable { validate!( ctx.accounts.admin.key().eq(&state.admin), ErrorCode::DefaultError, "Only state admin can re-enable after kill switch" )?; - msg!("Setting first bit to 0, enabling mm oracle update"); - state.disable_bit_flags = state.disable_bit_flags & !1; + + msg!("Setting second bit to 1, enabling median trigger price"); + state.feature_bit_flags = + state.feature_bit_flags | (FeatureBitFlags::MedianTriggerPrice as u8); + } else { + msg!("Setting second bit to 0, disabling median trigger price"); + state.feature_bit_flags = + state.feature_bit_flags & !(FeatureBitFlags::MedianTriggerPrice as u8); } Ok(()) } diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index abeb747d90..3f11a30c10 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -1888,6 +1888,7 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( taker_existing_base_asset_amount: to_existing_base_asset_amount, maker_existing_quote_entry_amount: from_existing_quote_entry_amount, maker_existing_base_asset_amount: from_existing_base_asset_amount, + trigger_price: None, }; emit_stack::<_, { OrderActionRecord::SIZE }>(fill_record)?; diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 49b75fe87d..debfa97229 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -1810,16 +1810,23 @@ pub mod drift { handle_update_if_rebalance_config(ctx, params) } - pub fn update_disable_bitflags_mm_oracle( + pub fn update_feature_bit_flags_mm_oracle( ctx: Context, - disable: bool, + enable: bool, ) -> Result<()> { - handle_update_disable_bitflags_mm_oracle(ctx, disable) + handle_update_feature_bit_flags_mm_oracle(ctx, enable) } pub fn zero_mm_oracle_fields(ctx: Context) -> Result<()> { handle_zero_mm_oracle_fields(ctx) } + + pub fn update_feature_bit_flags_median_trigger_price( + ctx: Context, + enable: bool, + ) -> Result<()> { + handle_update_feature_bit_flags_median_trigger_price(ctx, enable) + } } #[cfg(not(feature = "no-entrypoint"))] diff --git a/programs/drift/src/math/constants.rs b/programs/drift/src/math/constants.rs index 6ff4be7526..9ae6c0ca24 100644 --- a/programs/drift/src/math/constants.rs +++ b/programs/drift/src/math/constants.rs @@ -204,6 +204,8 @@ pub const DEFAULT_QUOTE_ASSET_AMOUNT_TICK_SIZE: u64 = // FUNDING pub const FUNDING_RATE_OFFSET_DENOMINATOR: i64 = 5000; // 5000 => 7.3% annualized rate for hourly funding +pub const FUNDING_RATE_OFFSET_PERCENTAGE: i64 = + FUNDING_RATE_PRECISION_I64 / FUNDING_RATE_OFFSET_DENOMINATOR; // ORDERS pub const AUCTION_DERIVE_PRICE_FRACTION: i64 = 200; diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index 8f93fae37e..f4806fa5be 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -254,10 +254,12 @@ pub struct OrderActionRecord { /// precision: BASE_PRECISION /// Only Some if the maker flipped position direction pub maker_existing_base_asset_amount: Option, + /// precision: PRICE_PRECISION + pub trigger_price: Option, } impl Size for OrderActionRecord { - const SIZE: usize = 448; + const SIZE: usize = 464; } pub fn get_order_action_record( @@ -285,6 +287,7 @@ pub fn get_order_action_record( taker_existing_base_asset_amount: Option, maker_existing_quote_entry_amount: Option, maker_existing_base_asset_amount: Option, + trigger_price: Option, ) -> DriftResult { Ok(OrderActionRecord { ts, @@ -337,6 +340,7 @@ pub fn get_order_action_record( taker_existing_base_asset_amount, maker_existing_quote_entry_amount, maker_existing_base_asset_amount, + trigger_price, }) } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 249f986541..32559f30ad 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -10,17 +10,17 @@ use crate::error::{DriftResult, ErrorCode}; use crate::math::amm; use crate::math::casting::Cast; #[cfg(test)] -use crate::math::constants::{ - AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT, PRICE_PRECISION_I64, -}; +use crate::math::constants::{AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT}; use crate::math::constants::{ AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128, - DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, LIQUIDATION_FEE_PRECISION, + DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, FUNDING_RATE_BUFFER_I128, + FUNDING_RATE_OFFSET_PERCENTAGE, LIQUIDATION_FEE_PRECISION, LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, LP_FEE_SLICE_DENOMINATOR, LP_FEE_SLICE_NUMERATOR, MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, - PERCENTAGE_PRECISION_U64, PRICE_PRECISION, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, + PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, PRICE_PRECISION_I64, + SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, }; use crate::math::helpers::get_proportion_i128; use crate::math::margin::{ @@ -253,7 +253,9 @@ pub struct PerpMarket { pub high_leverage_margin_ratio_maintenance: u16, pub protected_maker_limit_price_divisor: u8, pub protected_maker_dynamic_divisor: u8, - pub padding: [u8; 36], + pub padding1: u32, + pub last_fill_price: u64, + pub padding: [u8; 24], } impl Default for PerpMarket { @@ -295,7 +297,9 @@ impl Default for PerpMarket { high_leverage_margin_ratio_maintenance: 0, protected_maker_limit_price_divisor: 0, protected_maker_dynamic_divisor: 0, - padding: [0; 36], + padding1: 0, + last_fill_price: 0, + padding: [0; 24], } } } @@ -750,6 +754,109 @@ impl PerpMarket { } } + pub fn get_trigger_price( + &self, + oracle_price: i64, + now: i64, + use_median_price: bool, + ) -> DriftResult { + if !use_median_price { + return oracle_price.cast::(); + } + + let last_fill_price = self.last_fill_price; + + let mark_price_5min_twap = self.amm.last_mark_price_twap; + let last_oracle_price_twap_5min = + self.amm.historical_oracle_data.last_oracle_price_twap_5min; + + let basis_5min = mark_price_5min_twap + .cast::()? + .safe_sub(last_oracle_price_twap_5min)?; + + let oracle_plus_basis_5min = oracle_price.safe_add(basis_5min)?.cast::()?; + + let last_funding_basis = self.get_last_funding_basis(oracle_price, now)?; + + let oracle_plus_funding_basis = oracle_price.safe_add(last_funding_basis)?.cast::()?; + + let median_price = if last_fill_price > 0 { + let mut prices = [ + last_fill_price, + oracle_plus_funding_basis, + oracle_plus_basis_5min, + ]; + prices.sort_unstable(); + + prices[1] + } else { + let mut prices = [ + oracle_price.unsigned_abs(), + oracle_plus_funding_basis, + oracle_plus_basis_5min, + ]; + prices.sort_unstable(); + + prices[1] + }; + + self.clamp_trigger_price(oracle_price.unsigned_abs(), median_price) + } + + #[inline(always)] + fn get_last_funding_basis(&self, oracle_price: i64, now: i64) -> DriftResult { + if self.amm.last_funding_oracle_twap > 0 { + let last_funding_rate = self + .amm + .last_funding_rate + .cast::()? + .safe_mul(PRICE_PRECISION_I128)? + .safe_div(self.amm.last_funding_oracle_twap.cast::()?)? + .safe_mul(24)?; + let last_funding_rate_pre_adj = + last_funding_rate.safe_sub(FUNDING_RATE_OFFSET_PERCENTAGE as i128)?; + + let time_left_until_funding_update = now + .safe_sub(self.amm.last_funding_rate_ts)? + .min(self.amm.funding_period); + + let last_funding_basis = oracle_price + .cast::()? + .safe_mul(last_funding_rate_pre_adj)? + .safe_div(PERCENTAGE_PRECISION_I128)? + .safe_mul( + self.amm + .funding_period + .safe_sub(time_left_until_funding_update)? + .cast::()?, + )? + .safe_div(self.amm.funding_period.cast::()?)? + / FUNDING_RATE_BUFFER_I128; + + last_funding_basis.cast::() + } else { + Ok(0) + } + } + + #[inline(always)] + fn clamp_trigger_price(&self, oracle_price: u64, median_price: u64) -> DriftResult { + let max_bps_diff = if matches!(self.contract_tier, ContractTier::A | ContractTier::B) { + 500 // 20 BPS + } else if matches!(self.contract_tier, ContractTier::C) { + 100 // 100 BPS + } else { + 40 // 250 BPS + }; + let max_oracle_diff = oracle_price / max_bps_diff; + + Ok(median_price.clamp( + oracle_price.safe_sub(max_oracle_diff)?, + oracle_price.safe_add(max_oracle_diff)?, + )) + } + + #[inline(always)] pub fn get_mm_oracle_price_data( &self, oracle_price_data: OraclePriceData, @@ -952,10 +1059,10 @@ pub struct AMM { /// precision: AMM_RESERVE_PRECISION pub user_lp_shares: u128, /// last funding rate in this perp market (unit is quote per base) - /// precision: QUOTE_PRECISION + /// precision: FUNDING_RATE_PRECISION pub last_funding_rate: i64, /// last funding rate for longs in this perp market (unit is quote per base) - /// precision: QUOTE_PRECISION + /// precision: FUNDING_RATE_PRECISION pub last_funding_rate_long: i64, /// last funding rate for shorts in this perp market (unit is quote per base) /// precision: QUOTE_PRECISION @@ -1096,7 +1203,8 @@ pub struct AMM { pub reference_price_offset: i32, /// signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double) pub amm_inventory_spread_adjustment: i8, - pub padding: [u8; 11], + pub padding: [u8; 3], + pub last_funding_oracle_twap: i64, } impl Default for AMM { @@ -1186,7 +1294,8 @@ impl Default for AMM { quote_asset_amount_with_unsettled_lp: 0, reference_price_offset: 0, amm_inventory_spread_adjustment: 0, - padding: [0; 11], + padding: [0; 3], + last_funding_oracle_twap: 0, } } } diff --git a/programs/drift/src/state/perp_market/tests.rs b/programs/drift/src/state/perp_market/tests.rs index 3d3a9bb4cb..89d1974d08 100644 --- a/programs/drift/src/state/perp_market/tests.rs +++ b/programs/drift/src/state/perp_market/tests.rs @@ -156,3 +156,183 @@ mod get_min_perp_auction_duration { ); } } + +mod get_trigger_price { + use crate::state::perp_market::HistoricalOracleData; + use crate::state::perp_market::{PerpMarket, AMM}; + + #[test] + fn test_get_last_funding_basis() { + let oracle_price = 109144736794; + let last_funding_rate_ts = 1752080410; + let now = last_funding_rate_ts + 0; + let perp_market = PerpMarket { + amm: AMM { + last_funding_rate: 1410520875, + last_funding_rate_ts: 1752080410, + last_mark_price_twap: 109146153042, + last_funding_oracle_twap: 109198342833, + funding_period: 3600, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap_5min: 109143803911, + ..HistoricalOracleData::default() + }, + ..AMM::default() + }, + ..PerpMarket::default() + }; + + let last_funding_basis = perp_market + .get_last_funding_basis(oracle_price, now) + .unwrap(); + + assert_eq!(last_funding_basis, 12006794); // $12 basis + + let now = last_funding_rate_ts + 1800; + let last_funding_basis = perp_market + .get_last_funding_basis(oracle_price, now) + .unwrap(); + + assert_eq!(last_funding_basis, 6003397); // $6 basis + + let now = last_funding_rate_ts + 3600; + let last_funding_basis = perp_market + .get_last_funding_basis(oracle_price, now) + .unwrap(); + + assert_eq!(last_funding_basis, 0); + + let now = last_funding_rate_ts + 5400; + let last_funding_basis = perp_market + .get_last_funding_basis(oracle_price, now) + .unwrap(); + + assert_eq!(last_funding_basis, 0); + } + + #[test] + fn test_get_trigger_price() { + let oracle_price = 109144736794; + let now = 1752082210; + let perp_market = PerpMarket { + amm: AMM { + last_funding_rate: 1410520875, + last_funding_rate_ts: 1752080410, + last_mark_price_twap: 109146153042, + last_funding_oracle_twap: 109198342833, + funding_period: 3600, + historical_oracle_data: HistoricalOracleData { + last_oracle_price_twap_5min: 109143803911, + ..HistoricalOracleData::default() + }, + ..AMM::default() + }, + ..PerpMarket::default() + }; + + let trigger_price = perp_market + .get_trigger_price(oracle_price, now, true) + .unwrap(); + + assert_eq!(trigger_price, 109147085925); + } + + #[test] + fn test_clamp_trigger_price() { + use crate::state::perp_market::{ContractTier, PerpMarket}; + + // Test Contract Tier A (20 BPS = 500 divisor) + let perp_market_a = PerpMarket { + contract_tier: ContractTier::A, + ..PerpMarket::default() + }; + + let oracle_price = 100_000_000_000; // $100,000 + let max_bps_diff = 500; // 20 BPS + let max_oracle_diff = oracle_price / max_bps_diff; // 200,000,000 + + // Test median price below lower bound + let median_price_below = oracle_price - max_oracle_diff - 1_000_000; + let clamped_price = perp_market_a + .clamp_trigger_price(oracle_price, median_price_below) + .unwrap(); + assert_eq!(clamped_price, oracle_price - max_oracle_diff); + + // Test median price above upper bound + let median_price_above = oracle_price + max_oracle_diff + 1_000_000; + let clamped_price = perp_market_a + .clamp_trigger_price(oracle_price, median_price_above) + .unwrap(); + assert_eq!(clamped_price, oracle_price + max_oracle_diff); + + // Test median price within bounds (should not be clamped) + let median_price_within = oracle_price + max_oracle_diff / 2; + let clamped_price = perp_market_a + .clamp_trigger_price(oracle_price, median_price_within) + .unwrap(); + assert_eq!(clamped_price, median_price_within); + + // Test median price at exact bounds + let median_price_at_lower = oracle_price - max_oracle_diff; + let clamped_price = perp_market_a + .clamp_trigger_price(oracle_price, median_price_at_lower) + .unwrap(); + assert_eq!(clamped_price, median_price_at_lower); + + let median_price_at_upper = oracle_price + max_oracle_diff; + let clamped_price = perp_market_a + .clamp_trigger_price(oracle_price, median_price_at_upper) + .unwrap(); + assert_eq!(clamped_price, median_price_at_upper); + + // Test Contract Tier C (100 BPS = 100 divisor) + let perp_market_c = PerpMarket { + contract_tier: ContractTier::C, + ..PerpMarket::default() + }; + + let max_bps_diff_c = 100; // 100 BPS + let max_oracle_diff_c = oracle_price / max_bps_diff_c; // 1,000,000,000 + + // Test median price below lower bound for Tier C + let median_price_below_c = oracle_price - max_oracle_diff_c - 1_000_000; + let clamped_price = perp_market_c + .clamp_trigger_price(oracle_price, median_price_below_c) + .unwrap(); + assert_eq!(clamped_price, oracle_price - max_oracle_diff_c); + + // Test median price above upper bound for Tier C + let median_price_above_c = oracle_price + max_oracle_diff_c + 1_000_000; + let clamped_price = perp_market_c + .clamp_trigger_price(oracle_price, median_price_above_c) + .unwrap(); + assert_eq!(clamped_price, oracle_price + max_oracle_diff_c); + + // Test median price within bounds for Tier C + let median_price_within_c = oracle_price + max_oracle_diff_c / 2; + let clamped_price = perp_market_c + .clamp_trigger_price(oracle_price, median_price_within_c) + .unwrap(); + assert_eq!(clamped_price, median_price_within_c); + + // Test edge cases with very small oracle price + let small_oracle_price = 1_000_000; // $1 + let max_oracle_diff_small = small_oracle_price / max_bps_diff; // 2,000 + + let median_price_small = small_oracle_price - max_oracle_diff_small - 100; + let clamped_price = perp_market_a + .clamp_trigger_price(small_oracle_price, median_price_small) + .unwrap(); + assert_eq!(clamped_price, small_oracle_price - max_oracle_diff_small); + + // Test edge cases with very large oracle price + let large_oracle_price = 1_000_000_000_000_000; // $1M + let max_oracle_diff_large = large_oracle_price / max_bps_diff; // 2,000,000,000,000 + + let median_price_large = large_oracle_price + max_oracle_diff_large + 1_000_000_000; + let clamped_price = perp_market_a + .clamp_trigger_price(large_oracle_price, median_price_large) + .unwrap(); + assert_eq!(clamped_price, large_oracle_price + max_oracle_diff_large); + } +} diff --git a/programs/drift/src/state/state.rs b/programs/drift/src/state/state.rs index de763c2a7f..9e8b0961df 100644 --- a/programs/drift/src/state/state.rs +++ b/programs/drift/src/state/state.rs @@ -41,7 +41,7 @@ pub struct State { pub initial_pct_to_liquidate: u16, pub max_number_of_sub_accounts: u16, pub max_initialize_user_fee: u16, - pub disable_bit_flags: u8, + pub feature_bit_flags: u8, pub padding: [u8; 9], } @@ -116,6 +116,16 @@ impl State { Ok(init_fee) } + + pub fn use_median_trigger_price(&self) -> bool { + (self.feature_bit_flags & (FeatureBitFlags::MedianTriggerPrice as u8)) > 0 + } +} + +#[derive(Clone, Copy, PartialEq, Debug, Eq)] +pub enum FeatureBitFlags { + MmOracleUpdate = 0b00000001, + MedianTriggerPrice = 0b00000010, } impl Size for State { diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index 5e99f564bc..7c59de7afb 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -4552,33 +4552,6 @@ export class AdminClient extends DriftClient { }); } - public async updateDisableBitFlagsMMOracle( - disable: boolean - ): Promise { - const updateDisableBitFlagsMMOracleIx = - await this.getUpdateDisableBitFlagsMMOracleIx(disable); - - const tx = await this.buildTransaction(updateDisableBitFlagsMMOracleIx); - const { txSig } = await this.sendTransaction(tx, [], this.opts); - - return txSig; - } - public async getUpdateDisableBitFlagsMMOracleIx( - disable: boolean - ): Promise { - return await this.program.instruction.updateDisableBitflagsMmOracle( - disable, - { - accounts: { - admin: this.isSubscribed - ? this.getStateAccount().admin - : this.wallet.publicKey, - state: await this.getStatePublicKey(), - }, - } - ); - } - public async zeroMMOracleFields( marketIndex: number ): Promise { @@ -4591,6 +4564,7 @@ export class AdminClient extends DriftClient { return txSig; } + public async getZeroMMOracleFieldsIx( marketIndex: number ): Promise { @@ -4607,4 +4581,32 @@ export class AdminClient extends DriftClient { }, }); } + + public async updateFeatureBitFlagsMMOracle( + enable: boolean + ): Promise { + const updateFeatureBitFlagsMMOracleIx = + await this.getUpdateFeatureBitFlagsMMOracleIx(enable); + + const tx = await this.buildTransaction(updateFeatureBitFlagsMMOracleIx); + const { txSig } = await this.sendTransaction(tx, [], this.opts); + + return txSig; + } + + public async getUpdateFeatureBitFlagsMMOracleIx( + enable: boolean + ): Promise { + return await this.program.instruction.updateFeatureBitFlagsMmOracle( + enable, + { + accounts: { + admin: this.useHotWalletAdmin + ? this.wallet.publicKey + : this.getStateAccount().admin, + state: await this.getStatePublicKey(), + }, + } + ); + } } diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index d679a89ce1..92fb572c22 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -1582,7 +1582,7 @@ export class DLOB { public findNodesToTrigger( marketIndex: number, slot: number, - oraclePrice: BN, + triggerPrice: BN, marketType: MarketType, stateAccount: StateAccount ): NodeToTrigger[] { @@ -1599,7 +1599,7 @@ export class DLOB { : undefined; if (triggerAboveList) { for (const node of triggerAboveList.getGenerator()) { - if (oraclePrice.gt(node.order.triggerPrice)) { + if (triggerPrice.gt(node.order.triggerPrice)) { nodesToTrigger.push({ node: node, }); @@ -1614,7 +1614,7 @@ export class DLOB { : undefined; if (triggerBelowList) { for (const node of triggerBelowList.getGenerator()) { - if (oraclePrice.lt(node.order.triggerPrice)) { + if (triggerPrice.lt(node.order.triggerPrice)) { nodesToTrigger.push({ node: node, }); diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index ee618052a0..c8ec7b4c3c 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -7536,7 +7536,7 @@ ] }, { - "name": "updateDisableBitflagsMmOracle", + "name": "updateFeatureBitFlagsMmOracle", "accounts": [ { "name": "admin", @@ -7551,7 +7551,7 @@ ], "args": [ { - "name": "disable", + "name": "enable", "type": "bool" } ] @@ -8342,12 +8342,20 @@ "name": "protectedMakerDynamicDivisor", "type": "u8" }, + { + "name": "padding1", + "type": "u32" + }, + { + "name": "lastFillPrice", + "type": "u64" + }, { "name": "padding", "type": { "array": [ "u8", - 36 + 24 ] } } @@ -9078,7 +9086,7 @@ "type": "u16" }, { - "name": "disableBitFlags", + "name": "featureBitFlags", "type": "u8" }, { @@ -10658,7 +10666,7 @@ "name": "lastFundingRate", "docs": [ "last funding rate in this perp market (unit is quote per base)", - "precision: QUOTE_PRECISION" + "precision: FUNDING_RATE_PRECISION" ], "type": "i64" }, @@ -10666,7 +10674,7 @@ "name": "lastFundingRateLong", "docs": [ "last funding rate for longs in this perp market (unit is quote per base)", - "precision: QUOTE_PRECISION" + "precision: FUNDING_RATE_PRECISION" ], "type": "i64" }, @@ -11096,9 +11104,13 @@ "type": { "array": [ "u8", - 11 + 3 ] } + }, + { + "name": "lastFundingOracleTwap", + "type": "i64" } ] } @@ -12789,6 +12801,20 @@ ] } }, + { + "name": "FeatureBitFlags", + "type": { + "kind": "enum", + "variants": [ + { + "name": "MmOracleUpdate" + }, + { + "name": "EnableMedianTriggerPrice" + } + ] + } + }, { "name": "UserStatus", "type": { @@ -13712,6 +13738,13 @@ "option": "u64" }, "index": false + }, + { + "name": "triggerPrice", + "type": { + "option": "u64" + }, + "index": false } ] }, diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index c7c9b30d4e..26857777c6 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -6,6 +6,7 @@ import { SpotMarketAccount, SpotBalanceType, MarketType, + isVariant, } from '../types'; import { calculateAmmReservesAfterSwap, @@ -26,6 +27,9 @@ import { PRICE_TO_QUOTE_PRECISION, ZERO, QUOTE_SPOT_MARKET_INDEX, + PRICE_PRECISION, + PERCENTAGE_PRECISION, + FUNDING_RATE_PRECISION, } from '../constants/numericConstants'; import { getTokenAmount } from './spotBalance'; import { DLOB } from '../dlob/DLOB'; @@ -350,3 +354,104 @@ export function calculatePerpMarketBaseLiquidatorFee( return market.liquidatorFee; } } + +/** + * Calculates trigger price for a perp market based on oracle price and current time + * Implements the same logic as the Rust get_trigger_price function + * + * @param market - The perp market account + * @param oraclePrice - Current oracle price (precision: PRICE_PRECISION) + * @param now - Current timestamp in seconds + * @returns trigger price (precision: PRICE_PRECISION) + */ +export function getTriggerPrice( + market: PerpMarketAccount, + oraclePrice: BN, + now: BN, + useMedianPrice: boolean +): BN { + if (!useMedianPrice) { + return oraclePrice.abs(); + } + + const lastFillPrice = market.lastFillPrice; + + // Calculate 5-minute basis + const markPrice5minTwap = market.amm.lastMarkPriceTwap5Min; + const lastOraclePriceTwap5min = + market.amm.historicalOracleData.lastOraclePriceTwap5Min; + const basis5min = markPrice5minTwap.sub(lastOraclePriceTwap5min); + + const oraclePlusBasis5min = oraclePrice.add(basis5min); + + // Calculate funding basis + const lastFundingBasis = getLastFundingBasis(market, oraclePrice, now); + const oraclePlusFundingBasis = oraclePrice.add(lastFundingBasis); + + const prices = [ + lastFillPrice.gt(ZERO) ? lastFillPrice : oraclePrice, + oraclePlusFundingBasis, + oraclePlusBasis5min, + ].sort((a, b) => a.cmp(b)); + const medianPrice = prices[1]; + + return clampTriggerPrice(market, oraclePrice.abs(), medianPrice); +} + +/** + * Calculates the last funding basis for trigger price calculation + * Implements the same logic as the Rust get_last_funding_basis function + */ +function getLastFundingBasis( + market: PerpMarketAccount, + oraclePrice: BN, + now: BN +): BN { + if (market.amm.lastFundingOracleTwap.gt(ZERO)) { + const lastFundingRate = market.amm.lastFundingRate + .mul(PRICE_PRECISION) + .div(market.amm.lastFundingOracleTwap) + .muln(24); + const lastFundingRatePreAdj = lastFundingRate.sub( + FUNDING_RATE_PRECISION.div(new BN(5000)) // FUNDING_RATE_OFFSET_PERCENTAGE + ); + const timeLeftUntilFundingUpdate = BN.min( + BN.max(now.sub(market.amm.lastFundingRateTs), ZERO), + market.amm.fundingPeriod + ); + const lastFundingBasis = oraclePrice + .mul(lastFundingRatePreAdj) + .div(PERCENTAGE_PRECISION) + .mul(market.amm.fundingPeriod.sub(timeLeftUntilFundingUpdate)) + .div(market.amm.fundingPeriod) + .div(new BN(1000)); // FUNDING_RATE_BUFFER + return lastFundingBasis; + } else { + return ZERO; + } +} + +/** + * Clamps trigger price based on contract tier + * Implements the same logic as the Rust clamp_trigger_price function + */ +function clampTriggerPrice( + market: PerpMarketAccount, + oraclePrice: BN, + medianPrice: BN +): BN { + let maxBpsDiff: BN; + const tier = market.contractTier; + if (isVariant(tier, 'a') || isVariant(tier, 'b')) { + maxBpsDiff = new BN(500); // 20 BPS + } else if (isVariant(tier, 'c')) { + maxBpsDiff = new BN(100); // 100 BPS + } else { + maxBpsDiff = new BN(40); // 250 BPS + } + const maxOracleDiff = oraclePrice.div(maxBpsDiff); + return BN.min( + BN.max(medianPrice, oraclePrice.sub(maxOracleDiff)), + oraclePrice.add(maxOracleDiff) + ); +} diff --git a/sdk/src/math/state.ts b/sdk/src/math/state.ts index f4c52cac1c..f4414214a9 100644 --- a/sdk/src/math/state.ts +++ b/sdk/src/math/state.ts @@ -4,7 +4,7 @@ import { PERCENTAGE_PRECISION, ZERO, } from '../constants/numericConstants'; -import { StateAccount } from '../types'; +import { FeatureBitFlags, StateAccount } from '../types'; export function calculateInitUserFee(stateAccount: StateAccount): BN { const maxInitFee = new BN(stateAccount.maxInitializeUserFee) @@ -32,3 +32,9 @@ export function getMaxNumberOfSubAccounts(stateAccount: StateAccount): BN { } return new BN(stateAccount.maxNumberOfSubAccounts).muln(100); } + +export function useMedianTriggerPrice(stateAccount: StateAccount): boolean { + return ( + (stateAccount.featureBitFlags & FeatureBitFlags.MEDIAN_TRIGGER_PRICE) > 0 + ); +} diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 74177626d8..06a174f426 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -28,6 +28,11 @@ export enum ExchangeStatus { PAUSED = 255, } +export enum FeatureBitFlags { + MM_ORACLE_UPDATE = 1, + MEDIAN_TRIGGER_PRICE = 2, +} + export class MarketStatus { static readonly INITIALIZED = { initialized: {} }; static readonly ACTIVE = { active: {} }; @@ -760,6 +765,7 @@ export type StateAccount = { initialPctToLiquidate: number; liquidationDuration: number; maxInitializeUserFee: number; + featureBitFlags: number; }; export type PerpMarketAccount = { @@ -806,6 +812,7 @@ export type PerpMarketAccount = { highLeverageMarginRatioMaintenance: number; protectedMakerLimitPriceDivisor: number; protectedMakerDynamicDivisor: number; + lastFillPrice: BN; }; export type HistoricalOracleData = { @@ -1017,6 +1024,8 @@ export type AMM = { takerSpeedBumpOverride: number; ammSpreadAdjustment: number; ammInventorySpreadAdjustment: number; + + lastFundingOracleTwap: BN; }; // # User Account Types diff --git a/tests/admin.ts b/tests/admin.ts index b92fbd496e..6faef7d2d6 100644 --- a/tests/admin.ts +++ b/tests/admin.ts @@ -412,6 +412,7 @@ describe('admin', () => { it('update MM oracle native', async () => { const oraclePrice = new BN(100); const oracleTS = new BN(Date.now()); + await driftClient.updateFeatureBitFlagsMMOracle(true); await driftClient.updateMmOracleNative(0, oraclePrice, oracleTS); let perpMarket = driftClient.getPerpMarketAccount(0); @@ -428,7 +429,7 @@ describe('admin', () => { assert(perpMarket.amm.mmOraclePrice.eq(oraclePrice)); // Doesnt update if we flip the admin switch - await driftClient.updateDisableBitFlagsMMOracle(true); + await driftClient.updateFeatureBitFlagsMMOracle(false); try { await driftClient.updateMmOracleNative(0, oraclePrice, oracleTS); assert.fail('Should have thrown'); @@ -438,7 +439,7 @@ describe('admin', () => { } // Re-enable and update - await driftClient.updateDisableBitFlagsMMOracle(false); + await driftClient.updateFeatureBitFlagsMMOracle(true); await driftClient.updateMmOracleNative( 0, oraclePrice.addn(2), From 4c539debf572ed71f369ba1fbcc18e373106eb0c Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 00:26:32 +0000 Subject: [PATCH 033/216] sdk: release v2.130.0-beta.16 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index ffea015a4b..8cd0a7e70c 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.15 \ No newline at end of file +2.130.0-beta.16 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 09ea2b17ff..2487f9294c 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.15", + "version": "2.130.0-beta.16", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From fab10293d330f44dafa9f102b6ef35cefb9d8a1a Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 29 Jul 2025 20:26:42 -0400 Subject: [PATCH 034/216] add missing CHANGELOGs --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 647319efb7..328e7948a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features - program: new median price for trigger orders ([#1716](https://github.com/drift-labs/protocol-v2/pull/1716)) +- program: mm oracle ([#1767](https://github.com/drift-labs/protocol-v2/pull/1767)) +- program: add high leverage maintenance margin mode ([#1759](https://github.com/drift-labs/protocol-v2/pull/1759)) ### Fixes +- program: update validate fill price to work both directions ([#1772](https://github.com/drift-labs/protocol-v2/pull/1772)) + ### Breaking ## [2.129.0] - 2025-07-23 From ee8172f049b6308fd609d9f774462313de3b5e40 Mon Sep 17 00:00:00 2001 From: wphan Date: Tue, 29 Jul 2025 18:08:25 -0700 Subject: [PATCH 035/216] v2.130.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 4 ++-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 328e7948a0..9aedc573dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +### Fixes + +### Breaking + +## [2.130.0] - 2025-07-29 + +### Features + - program: new median price for trigger orders ([#1716](https://github.com/drift-labs/protocol-v2/pull/1716)) - program: mm oracle ([#1767](https://github.com/drift-labs/protocol-v2/pull/1767)) - program: add high leverage maintenance margin mode ([#1759](https://github.com/drift-labs/protocol-v2/pull/1759)) diff --git a/Cargo.lock b/Cargo.lock index c737bbef32..bacc94f31c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.129.0" +version = "2.130.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 04320aa616..92ad4d488d 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.129.0" +version = "2.130.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index 2487f9294c..4059ecdad1 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0-beta.16", + "version": "2.130.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index c8ec7b4c3c..def1f3eca3 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.129.0", + "version": "2.130.0", "name": "drift", "instructions": [ { @@ -16053,4 +16053,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} \ No newline at end of file +} From c26a214e3d7f90112c4e8efa6e1d4477121d42f8 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 01:16:15 +0000 Subject: [PATCH 036/216] sdk: release v2.131.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8cd0a7e70c..29cde8a5d5 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.130.0-beta.16 \ No newline at end of file +2.131.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 4059ecdad1..3e4378337f 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.130.0", + "version": "2.131.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 0751acfa62fc155d393f5f47a63e4601257668d5 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Wed, 30 Jul 2025 12:19:23 +0800 Subject: [PATCH 037/216] Add lint for index imports (#1787) --- .eslintrc.js | 11 ++++++++++- sdk/src/events/types.ts | 2 +- sdk/src/math/insurance.ts | 3 ++- sdk/src/math/oracles.ts | 16 +++++++++------- sdk/src/orderSubscriber/OrderSubscriber.ts | 3 ++- sdk/tests/bn/test.ts | 3 ++- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 60261693ed..f09b072302 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -37,7 +37,16 @@ module.exports = { // Restrict importing BN from bn.js "group": ["bn.js"], "message": "Import BN from @drift-labs/sdk instead", - } + }, + { + // Prevent imports from index files within the same package + group: [ + '**/index', + '**/index.ts', + ], + message: + 'Do not import from index file within the same package. Import directly from source files instead. This prevents barrel imports.', + }, ], }, ], diff --git a/sdk/src/events/types.ts b/sdk/src/events/types.ts index 3c4c92548c..2671344ceb 100644 --- a/sdk/src/events/types.ts +++ b/sdk/src/events/types.ts @@ -21,7 +21,7 @@ import { FuelSeasonRecord, InsuranceFundSwapRecord, TransferProtocolIfSharesToRevenuePoolRecord, -} from '../index'; +} from '../types'; import { EventEmitter } from 'events'; export type EventSubscriptionOptions = { diff --git a/sdk/src/math/insurance.ts b/sdk/src/math/insurance.ts index 093a780073..6524b5d86c 100644 --- a/sdk/src/math/insurance.ts +++ b/sdk/src/math/insurance.ts @@ -1,6 +1,7 @@ import { PERCENTAGE_PRECISION, ZERO } from '../constants/numericConstants'; -import { BN, SpotMarketAccount, SpotBalanceType } from '../index'; import { getTokenAmount } from '../math/spotBalance'; +import { BN } from '@coral-xyz/anchor'; +import { SpotBalanceType, SpotMarketAccount } from '../types'; export function nextRevenuePoolSettleApr( spotMarket: SpotMarketAccount, diff --git a/sdk/src/math/oracles.ts b/sdk/src/math/oracles.ts index bfbd88702c..8873c66150 100644 --- a/sdk/src/math/oracles.ts +++ b/sdk/src/math/oracles.ts @@ -1,4 +1,11 @@ -import { AMM, OracleGuardRails, isVariant } from '../types'; +import { + AMM, + HistoricalOracleData, + OracleGuardRails, + OracleSource, + PerpMarketAccount, + isVariant, +} from '../types'; import { OraclePriceData } from '../oracles/types'; import { BID_ASK_SPREAD_PRECISION, @@ -9,13 +16,8 @@ import { FIVE_MINUTE, PERCENTAGE_PRECISION, } from '../constants/numericConstants'; -import { - BN, - HistoricalOracleData, - OracleSource, - PerpMarketAccount, -} from '../index'; import { assert } from '../assert/assert'; +import { BN } from '@coral-xyz/anchor'; export function oraclePriceBands( market: PerpMarketAccount, diff --git a/sdk/src/orderSubscriber/OrderSubscriber.ts b/sdk/src/orderSubscriber/OrderSubscriber.ts index 2c0b95bb69..6ecb37234e 100644 --- a/sdk/src/orderSubscriber/OrderSubscriber.ts +++ b/sdk/src/orderSubscriber/OrderSubscriber.ts @@ -13,12 +13,13 @@ import { PollingSubscription } from './PollingSubscription'; import { WebsocketSubscription } from './WebsocketSubscription'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { calculateOrderBaseAssetAmount, ZERO } from '../index'; import { BN } from '@coral-xyz/anchor'; import { ProtectMakerParamsMap } from '../dlob/types'; import { decodeUser } from '../decode/user'; import { grpcSubscription } from './grpcSubscription'; import { isUserProtectedMaker } from '../math/userStatus'; +import { calculateOrderBaseAssetAmount } from '../math/orders'; +import { ZERO } from '../constants/numericConstants'; export class OrderSubscriber { driftClient: DriftClient; diff --git a/sdk/tests/bn/test.ts b/sdk/tests/bn/test.ts index e4733dee6f..305e14d120 100644 --- a/sdk/tests/bn/test.ts +++ b/sdk/tests/bn/test.ts @@ -1,4 +1,3 @@ -import { BN, numberToSafeBN } from '../../src/index'; import { expect } from 'chai'; import { BigNum } from '../../src/factory/bigNum'; import { @@ -7,6 +6,8 @@ import { BASE_PRECISION_EXP, TEN_THOUSAND, } from '../../src/constants/numericConstants'; +import { BN } from '@coral-xyz/anchor'; +import { numberToSafeBN } from '../../src/math/utils'; // if you used the '@types/mocha' method to install mocha type definitions, uncomment the following line // import 'mocha'; From 6554cca252184bbcebf9d3ce30c612afa9e460dd Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 04:23:44 +0000 Subject: [PATCH 038/216] sdk: release v2.131.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 29cde8a5d5..b3c0f20ad3 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.0 \ No newline at end of file +2.131.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 3e4378337f..302173ee33 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.0", + "version": "2.131.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From d69208ccf79da3cb7d048da88f9ae5abb37d4a32 Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 30 Jul 2025 08:42:58 -0700 Subject: [PATCH 039/216] sdk: revert grpc account subscribers to v2.126.0-beta.0 (#1789) --- sdk/src/accounts/grpcAccountSubscriber.ts | 30 ++----------------- .../accounts/grpcProgramAccountSubscriber.ts | 30 ++----------------- 2 files changed, 4 insertions(+), 56 deletions(-) diff --git a/sdk/src/accounts/grpcAccountSubscriber.ts b/sdk/src/accounts/grpcAccountSubscriber.ts index 1ef65cd17c..be141c3746 100644 --- a/sdk/src/accounts/grpcAccountSubscriber.ts +++ b/sdk/src/accounts/grpcAccountSubscriber.ts @@ -18,7 +18,6 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { private stream: ClientDuplexStream; private commitmentLevel: CommitmentLevel; public listenerId?: number; - private enableReconnect: boolean; private constructor( client: Client, @@ -27,13 +26,11 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { program: Program, accountPublicKey: PublicKey, decodeBuffer?: (buffer: Buffer) => T, - resubOpts?: ResubOpts, - enableReconnect = false + resubOpts?: ResubOpts ) { super(accountName, program, accountPublicKey, decodeBuffer, resubOpts); this.client = client; this.commitmentLevel = commitmentLevel; - this.enableReconnect = enableReconnect; } public static async create( @@ -60,8 +57,7 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { program, accountPublicKey, decodeBuffer, - resubOpts, - grpcConfigs.enableReconnect + resubOpts ); } @@ -95,26 +91,6 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { entry: {}, transactionsStatus: {}, }; - - if (this.enableReconnect) { - this.stream.on('error', (error) => { - // @ts-ignore - if (error.code === 1) { - // expected: 1 CANCELLED: Cancelled on client - console.error( - 'GRPC (grpcAccountSubscriber) Cancelled on client caught:', - error - ); - return; - } else { - console.error( - 'GRPC (grpcAccountSubscriber) unexpected error caught:', - error - ); - } - }); - } - this.stream.on('data', (chunk: SubscribeUpdate) => { if (!chunk.account) { return; @@ -196,8 +172,6 @@ export class grpcAccountSubscriber extends WebSocketAccountSubscriber { reject(err); } }); - this.stream.cancel(); - this.stream.destroy(); }).catch((reason) => { console.error(reason); throw reason; diff --git a/sdk/src/accounts/grpcProgramAccountSubscriber.ts b/sdk/src/accounts/grpcProgramAccountSubscriber.ts index d669b508e9..f575c64229 100644 --- a/sdk/src/accounts/grpcProgramAccountSubscriber.ts +++ b/sdk/src/accounts/grpcProgramAccountSubscriber.ts @@ -20,7 +20,6 @@ export class grpcProgramAccountSubscriber< private stream: ClientDuplexStream; private commitmentLevel: CommitmentLevel; public listenerId?: number; - private enableReconnect: boolean; private constructor( client: Client, @@ -32,8 +31,7 @@ export class grpcProgramAccountSubscriber< options: { filters: MemcmpFilter[] } = { filters: [], }, - resubOpts?: ResubOpts, - enableReconnect = false + resubOpts?: ResubOpts ) { super( subscriptionName, @@ -45,7 +43,6 @@ export class grpcProgramAccountSubscriber< ); this.client = client; this.commitmentLevel = commitmentLevel; - this.enableReconnect = enableReconnect; } public static async create( @@ -76,8 +73,7 @@ export class grpcProgramAccountSubscriber< program, decodeBufferFn, options, - resubOpts, - grpcConfigs.enableReconnect + resubOpts ); } @@ -123,26 +119,6 @@ export class grpcProgramAccountSubscriber< entry: {}, transactionsStatus: {}, }; - - if (this.enableReconnect) { - this.stream.on('error', (error) => { - // @ts-ignore - if (error.code === 1) { - // expected: 1 CANCELLED: Cancelled on client - console.error( - 'GRPC (grpcProgramAccountSubscriber) Cancelled on client caught:', - error - ); - return; - } else { - console.error( - 'GRPC (grpcProgramAccountSubscriber) unexpected error caught:', - error - ); - } - }); - } - this.stream.on('data', (chunk: SubscribeUpdate) => { if (!chunk.account) { return; @@ -230,8 +206,6 @@ export class grpcProgramAccountSubscriber< reject(err); } }); - this.stream.cancel(); - this.stream.destroy(); }).catch((reason) => { console.error(reason); throw reason; From db1391932bcde061a4e5be4759d3f960b5d6eea0 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 15:48:53 +0000 Subject: [PATCH 040/216] sdk: release v2.131.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b3c0f20ad3..b3394dec80 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.1 \ No newline at end of file +2.131.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 302173ee33..3beb3fd950 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.1", + "version": "2.131.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 9bbafe26dbc9362a71446bbc88c8839bdda5337a Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 30 Jul 2025 12:54:26 -0400 Subject: [PATCH 041/216] sdk: backwards compatible idl --- sdk/src/idl/drift.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index def1f3eca3..ef340a4d95 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -13738,13 +13738,6 @@ "option": "u64" }, "index": false - }, - { - "name": "triggerPrice", - "type": { - "option": "u64" - }, - "index": false } ] }, From 2634444ea71f950598a7998caf89b62c61df3d5f Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 16:59:13 +0000 Subject: [PATCH 042/216] sdk: release v2.131.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b3394dec80..961ecff763 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.2 \ No newline at end of file +2.131.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 3beb3fd950..c80aebe6a1 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.2", + "version": "2.131.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From a640eda4434137c703fd217db3abeed9b350f8a8 Mon Sep 17 00:00:00 2001 From: LukasDeco Date: Wed, 30 Jul 2025 12:35:09 -0600 Subject: [PATCH 043/216] lukas/gill websocket sub (#1781) * websockets gill temp * feat: feature parity between gill version ws acct sub and reg one + optional passing into driftClient * fix: post rebase bugs and cleanup * chore: websocket account subscriber export * feat: logging string update on ws acct v2 * rm: useless logging * chore: cleanup ws subscriber v2 docs * chore: specific name on custom ws acct sub param * fix: post rebase again cleanup * fix: prettier fixed --- sdk/bun.lock | 101 ++++- sdk/package.json | 1 + .../README_WebSocketAccountSubscriberV2.md | 54 +++ .../accounts/webSocketAccountSubscriberV2.ts | 310 ++++++++++++++ .../webSocketDriftClientAccountSubscriber.ts | 24 +- sdk/src/driftClient.ts | 3 +- sdk/src/driftClientConfig.ts | 18 +- sdk/src/index.ts | 1 + sdk/yarn.lock | 403 +++++++++++++++++- 9 files changed, 903 insertions(+), 12 deletions(-) create mode 100644 sdk/src/accounts/README_WebSocketAccountSubscriberV2.md create mode 100644 sdk/src/accounts/webSocketAccountSubscriberV2.ts diff --git a/sdk/bun.lock b/sdk/bun.lock index f9c0573966..c77cb9180f 100644 --- a/sdk/bun.lock +++ b/sdk/bun.lock @@ -19,6 +19,7 @@ "@switchboard-xyz/on-demand": "2.4.1", "@triton-one/yellowstone-grpc": "1.3.0", "anchor-bankrun": "0.3.0", + "gill": "^0.10.2", "nanoid": "3.3.4", "node-cache": "5.1.2", "rpc-websockets": "7.5.1", @@ -187,23 +188,79 @@ "@sinonjs/text-encoding": ["@sinonjs/text-encoding@0.7.3", "", {}, "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA=="], + "@solana-program/address-lookup-table": ["@solana-program/address-lookup-table@0.7.0", "", { "peerDependencies": { "@solana/kit": "^2.1.0" } }, "sha512-dzCeIO5LtiK3bIg0AwO+TPeGURjSG2BKt0c4FRx7105AgLy7uzTktpUzUj6NXAK9SzbirI8HyvHUvw1uvL8O9A=="], + + "@solana-program/compute-budget": ["@solana-program/compute-budget@0.8.0", "", { "peerDependencies": { "@solana/kit": "^2.1.0" } }, "sha512-qPKxdxaEsFxebZ4K5RPuy7VQIm/tfJLa1+Nlt3KNA8EYQkz9Xm8htdoEaXVrer9kpgzzp9R3I3Bh6omwCM06tQ=="], + + "@solana-program/system": ["@solana-program/system@0.7.0", "", { "peerDependencies": { "@solana/kit": "^2.1.0" } }, "sha512-FKTBsKHpvHHNc1ATRm7SlC5nF/VdJtOSjldhcyfMN9R7xo712Mo2jHIzvBgn8zQO5Kg0DcWuKB7268Kv1ocicw=="], + + "@solana-program/token-2022": ["@solana-program/token-2022@0.4.2", "", { "peerDependencies": { "@solana/kit": "^2.1.0", "@solana/sysvars": "^2.1.0" } }, "sha512-zIpR5t4s9qEU3hZKupzIBxJ6nUV5/UVyIT400tu9vT1HMs5JHxaTTsb5GUhYjiiTvNwU0MQavbwc4Dl29L0Xvw=="], + + "@solana/accounts": ["@solana/accounts@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/rpc-spec": "2.3.0", "@solana/rpc-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-QgQTj404Z6PXNOyzaOpSzjgMOuGwG8vC66jSDB+3zHaRcEPRVRd2sVSrd1U6sHtnV3aiaS6YyDuPQMheg4K2jw=="], + + "@solana/addresses": ["@solana/addresses@2.3.0", "", { "dependencies": { "@solana/assertions": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/nominal-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ypTNkY2ZaRFpHLnHAgaW8a83N0/WoqdFvCqf4CQmnMdFsZSdC7qOwcbd7YzdaQn9dy+P2hybewzB+KP7LutxGA=="], + + "@solana/assertions": ["@solana/assertions@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-Ekoet3khNg3XFLN7MIz8W31wPQISpKUGDGTylLptI+JjCDWx3PIa88xjEMqFo02WJ8sBj2NLV64Xg1sBcsHjZQ=="], + "@solana/buffer-layout": ["@solana/buffer-layout@4.0.1", "", { "dependencies": { "buffer": "~6.0.3" } }, "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA=="], "@solana/buffer-layout-utils": ["@solana/buffer-layout-utils@0.2.0", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/web3.js": "^1.32.0", "bigint-buffer": "^1.1.5", "bignumber.js": "^9.0.1" } }, "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g=="], - "@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], + "@solana/codecs": ["@solana/codecs@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/codecs-data-structures": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/options": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-JVqGPkzoeyU262hJGdH64kNLH0M+Oew2CIPOa/9tR3++q2pEd4jU2Rxdfye9sd0Ce3XJrR5AIa8ZfbyQXzjh+g=="], - "@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/codecs-core": ["@solana/codecs-core@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw=="], "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-preview.4", "", { "dependencies": { "@solana/codecs-core": "2.0.0-preview.4", "@solana/codecs-numbers": "2.0.0-preview.4", "@solana/errors": "2.0.0-preview.4" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA=="], - "@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + "@solana/codecs-numbers": ["@solana/codecs-numbers@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg=="], - "@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + "@solana/codecs-strings": ["@solana/codecs-strings@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-y5pSBYwzVziXu521hh+VxqUtp0hYGTl1eWGoc1W+8mdvBdC1kTqm/X7aYQw33J42hw03JjryvYOvmGgk3Qz/Ug=="], "@solana/errors": ["@solana/errors@2.0.0-preview.4", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA=="], - "@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + "@solana/fast-stable-stringify": ["@solana/fast-stable-stringify@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw=="], + + "@solana/functional": ["@solana/functional@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-AgsPh3W3tE+nK3eEw/W9qiSfTGwLYEvl0rWaxHht/lRcuDVwfKRzeSa5G79eioWFFqr+pTtoCr3D3OLkwKz02Q=="], + + "@solana/instructions": ["@solana/instructions@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-PLMsmaIKu7hEAzyElrk2T7JJx4D+9eRwebhFZpy2PXziNSmFF929eRHKUsKqBFM3cYR1Yy3m6roBZfA+bGE/oQ=="], + + "@solana/keys": ["@solana/keys@2.3.0", "", { "dependencies": { "@solana/assertions": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/nominal-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZVVdga79pNH+2pVcm6fr2sWz9HTwfopDVhYb0Lh3dh+WBmJjwkabXEIHey2rUES7NjFa/G7sV8lrUn/v8LDCCQ=="], + + "@solana/kit": ["@solana/kit@2.3.0", "", { "dependencies": { "@solana/accounts": "2.3.0", "@solana/addresses": "2.3.0", "@solana/codecs": "2.3.0", "@solana/errors": "2.3.0", "@solana/functional": "2.3.0", "@solana/instructions": "2.3.0", "@solana/keys": "2.3.0", "@solana/programs": "2.3.0", "@solana/rpc": "2.3.0", "@solana/rpc-parsed-types": "2.3.0", "@solana/rpc-spec-types": "2.3.0", "@solana/rpc-subscriptions": "2.3.0", "@solana/rpc-types": "2.3.0", "@solana/signers": "2.3.0", "@solana/sysvars": "2.3.0", "@solana/transaction-confirmation": "2.3.0", "@solana/transaction-messages": "2.3.0", "@solana/transactions": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ=="], + + "@solana/nominal-types": ["@solana/nominal-types@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-uKlMnlP4PWW5UTXlhKM8lcgIaNj8dvd8xO4Y9l+FVvh9RvW2TO0GwUO6JCo7JBzCB0PSqRJdWWaQ8pu1Ti/OkA=="], + + "@solana/options": ["@solana/options@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/codecs-data-structures": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-PPnnZBRCWWoZQ11exPxf//DRzN2C6AoFsDI/u2AsQfYih434/7Kp4XLpfOMT/XESi+gdBMFNNfbES5zg3wAIkw=="], + + "@solana/programs": ["@solana/programs@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-UXKujV71VCI5uPs+cFdwxybtHZAIZyQkqDiDnmK+DawtOO9mBn4Nimdb/6RjR2CXT78mzO9ZCZ3qfyX+ydcB7w=="], + + "@solana/promises": ["@solana/promises@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-GjVgutZKXVuojd9rWy1PuLnfcRfqsaCm7InCiZc8bqmJpoghlyluweNc7ml9Y5yQn1P2IOyzh9+p/77vIyNybQ=="], + + "@solana/rpc": ["@solana/rpc@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/fast-stable-stringify": "2.3.0", "@solana/functional": "2.3.0", "@solana/rpc-api": "2.3.0", "@solana/rpc-spec": "2.3.0", "@solana/rpc-spec-types": "2.3.0", "@solana/rpc-transformers": "2.3.0", "@solana/rpc-transport-http": "2.3.0", "@solana/rpc-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-ZWN76iNQAOCpYC7yKfb3UNLIMZf603JckLKOOLTHuy9MZnTN8XV6uwvDFhf42XvhglgUjGCEnbUqWtxQ9pa/pQ=="], + + "@solana/rpc-api": ["@solana/rpc-api@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/keys": "2.3.0", "@solana/rpc-parsed-types": "2.3.0", "@solana/rpc-spec": "2.3.0", "@solana/rpc-transformers": "2.3.0", "@solana/rpc-types": "2.3.0", "@solana/transaction-messages": "2.3.0", "@solana/transactions": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-UUdiRfWoyYhJL9PPvFeJr4aJ554ob2jXcpn4vKmRVn9ire0sCbpQKYx6K8eEKHZWXKrDW8IDspgTl0gT/aJWVg=="], + + "@solana/rpc-parsed-types": ["@solana/rpc-parsed-types@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-B5pHzyEIbBJf9KHej+zdr5ZNAdSvu7WLU2lOUPh81KHdHQs6dEb310LGxcpCc7HVE8IEdO20AbckewDiAN6OCg=="], + + "@solana/rpc-spec": ["@solana/rpc-spec@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/rpc-spec-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-fA2LMX4BMixCrNB2n6T83AvjZ3oUQTu7qyPLyt8gHQaoEAXs8k6GZmu6iYcr+FboQCjUmRPgMaABbcr9j2J9Sw=="], + + "@solana/rpc-spec-types": ["@solana/rpc-spec-types@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-xQsb65lahjr8Wc9dMtP7xa0ZmDS8dOE2ncYjlvfyw/h4mpdXTUdrSMi6RtFwX33/rGuztQ7Hwaid5xLNSLvsFQ=="], + + "@solana/rpc-subscriptions": ["@solana/rpc-subscriptions@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/fast-stable-stringify": "2.3.0", "@solana/functional": "2.3.0", "@solana/promises": "2.3.0", "@solana/rpc-spec-types": "2.3.0", "@solana/rpc-subscriptions-api": "2.3.0", "@solana/rpc-subscriptions-channel-websocket": "2.3.0", "@solana/rpc-subscriptions-spec": "2.3.0", "@solana/rpc-transformers": "2.3.0", "@solana/rpc-types": "2.3.0", "@solana/subscribable": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-Uyr10nZKGVzvCOqwCZgwYrzuoDyUdwtgQRefh13pXIrdo4wYjVmoLykH49Omt6abwStB0a4UL5gX9V4mFdDJZg=="], + + "@solana/rpc-subscriptions-api": ["@solana/rpc-subscriptions-api@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/keys": "2.3.0", "@solana/rpc-subscriptions-spec": "2.3.0", "@solana/rpc-transformers": "2.3.0", "@solana/rpc-types": "2.3.0", "@solana/transaction-messages": "2.3.0", "@solana/transactions": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-9mCjVbum2Hg9KGX3LKsrI5Xs0KX390lS+Z8qB80bxhar6MJPugqIPH8uRgLhCW9GN3JprAfjRNl7our8CPvsPQ=="], + + "@solana/rpc-subscriptions-channel-websocket": ["@solana/rpc-subscriptions-channel-websocket@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/functional": "2.3.0", "@solana/rpc-subscriptions-spec": "2.3.0", "@solana/subscribable": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3", "ws": "^8.18.0" } }, "sha512-2oL6ceFwejIgeWzbNiUHI2tZZnaOxNTSerszcin7wYQwijxtpVgUHiuItM/Y70DQmH9sKhmikQp+dqeGalaJxw=="], + + "@solana/rpc-subscriptions-spec": ["@solana/rpc-subscriptions-spec@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/promises": "2.3.0", "@solana/rpc-spec-types": "2.3.0", "@solana/subscribable": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-rdmVcl4PvNKQeA2l8DorIeALCgJEMSu7U8AXJS1PICeb2lQuMeaR+6cs/iowjvIB0lMVjYN2sFf6Q3dJPu6wWg=="], + + "@solana/rpc-transformers": ["@solana/rpc-transformers@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/functional": "2.3.0", "@solana/nominal-types": "2.3.0", "@solana/rpc-spec-types": "2.3.0", "@solana/rpc-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-UuHYK3XEpo9nMXdjyGKkPCOr7WsZsxs7zLYDO1A5ELH3P3JoehvrDegYRAGzBS2VKsfApZ86ZpJToP0K3PhmMA=="], + + "@solana/rpc-transport-http": ["@solana/rpc-transport-http@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0", "@solana/rpc-spec": "2.3.0", "@solana/rpc-spec-types": "2.3.0", "undici-types": "^7.11.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-HFKydmxGw8nAF5N+S0NLnPBDCe5oMDtI2RAmW8DMqP4U3Zxt2XWhvV1SNkAldT5tF0U1vP+is6fHxyhk4xqEvg=="], + + "@solana/rpc-types": ["@solana/rpc-types@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/nominal-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-O09YX2hED2QUyGxrMOxQ9GzH1LlEwwZWu69QbL4oYmIf6P5dzEEHcqRY6L1LsDVqc/dzAdEs/E1FaPrcIaIIPw=="], + + "@solana/signers": ["@solana/signers@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0", "@solana/instructions": "2.3.0", "@solana/keys": "2.3.0", "@solana/nominal-types": "2.3.0", "@solana/transaction-messages": "2.3.0", "@solana/transactions": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-OSv6fGr/MFRx6J+ZChQMRqKNPGGmdjkqarKkRzkwmv7v8quWsIRnJT5EV8tBy3LI4DLO/A8vKiNSPzvm1TdaiQ=="], "@solana/spl-token": ["@solana/spl-token@0.4.13", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-group": "^0.0.7", "@solana/spl-token-metadata": "^0.1.6", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.95.5" } }, "sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w=="], @@ -211,6 +268,16 @@ "@solana/spl-token-metadata": ["@solana/spl-token-metadata@0.1.6", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA=="], + "@solana/subscribable": ["@solana/subscribable@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-DkgohEDbMkdTWiKAoatY02Njr56WXx9e/dKKfmne8/Ad6/2llUIrax78nCdlvZW9quXMaXPTxZvdQqo9N669Og=="], + + "@solana/sysvars": ["@solana/sysvars@2.3.0", "", { "dependencies": { "@solana/accounts": "2.3.0", "@solana/codecs": "2.3.0", "@solana/errors": "2.3.0", "@solana/rpc-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-LvjADZrpZ+CnhlHqfI5cmsRzX9Rpyb1Ox2dMHnbsRNzeKAMhu9w4ZBIaeTdO322zsTr509G1B+k2ABD3whvUBA=="], + + "@solana/transaction-confirmation": ["@solana/transaction-confirmation@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/keys": "2.3.0", "@solana/promises": "2.3.0", "@solana/rpc": "2.3.0", "@solana/rpc-subscriptions": "2.3.0", "@solana/rpc-types": "2.3.0", "@solana/transaction-messages": "2.3.0", "@solana/transactions": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-UiEuiHCfAAZEKdfne/XljFNJbsKAe701UQHKXEInYzIgBjRbvaeYZlBmkkqtxwcasgBTOmEaEKT44J14N9VZDw=="], + + "@solana/transaction-messages": ["@solana/transaction-messages@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-data-structures": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/errors": "2.3.0", "@solana/functional": "2.3.0", "@solana/instructions": "2.3.0", "@solana/nominal-types": "2.3.0", "@solana/rpc-types": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-bgqvWuy3MqKS5JdNLH649q+ngiyOu5rGS3DizSnWwYUd76RxZl1kN6CoqHSrrMzFMvis6sck/yPGG3wqrMlAww=="], + + "@solana/transactions": ["@solana/transactions@2.3.0", "", { "dependencies": { "@solana/addresses": "2.3.0", "@solana/codecs-core": "2.3.0", "@solana/codecs-data-structures": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/codecs-strings": "2.3.0", "@solana/errors": "2.3.0", "@solana/functional": "2.3.0", "@solana/instructions": "2.3.0", "@solana/keys": "2.3.0", "@solana/nominal-types": "2.3.0", "@solana/rpc-types": "2.3.0", "@solana/transaction-messages": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-LnTvdi8QnrQtuEZor5Msje61sDpPstTVwKg4y81tNxDhiyomjuvnSNLAq6QsB9gIxUqbNzPZgOG9IU4I4/Uaug=="], + "@solana/web3.js": ["@solana/web3.js@1.98.0", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "agentkeepalive": "^4.5.0", "bigint-buffer": "^1.1.5", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA=="], "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], @@ -607,6 +674,8 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "gill": ["gill@0.10.2", "", { "dependencies": { "@solana-program/address-lookup-table": "^0.7.0", "@solana-program/compute-budget": "^0.8.0", "@solana-program/system": "^0.7.0", "@solana-program/token-2022": "^0.4.1", "@solana/assertions": "^2.1.1", "@solana/codecs": "^2.1.1", "@solana/kit": "^2.1.1", "@solana/transaction-confirmation": "^2.1.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-upWoY2dfOzKHOcX3UnD+B3h9WUunPv0oxeKzsIgKSaLyURpWK9oI+K2NHWbwrUFsXEK6ozu/sgkhuqyAcVTZCg=="], + "glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -1109,6 +1178,12 @@ "@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/rpc-transport-http/undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], + + "@solana/spl-token-group/@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], + + "@solana/spl-token-metadata/@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], + "@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.1", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA=="], "@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], @@ -1199,6 +1274,22 @@ "@pythnetwork/solana-utils/bs58/base-x": ["base-x@4.0.0", "", {}, "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="], + "@solana/spl-token-group/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + + "@solana/spl-token-group/@solana/codecs/@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + "@solana/web3.js/rpc-websockets/@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], "@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], diff --git a/sdk/package.json b/sdk/package.json index c80aebe6a1..4910960c71 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -53,6 +53,7 @@ "@switchboard-xyz/on-demand": "2.4.1", "@triton-one/yellowstone-grpc": "1.3.0", "anchor-bankrun": "0.3.0", + "gill": "^0.10.2", "nanoid": "3.3.4", "node-cache": "5.1.2", "rpc-websockets": "7.5.1", diff --git a/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md b/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md new file mode 100644 index 0000000000..f53ebc1337 --- /dev/null +++ b/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md @@ -0,0 +1,54 @@ +# WebSocketAccountSubscriberV2 + +This is a new implementation of the WebSocket account subscriber that utilizes the [gill](https://www.npmjs.com/package/gill) library for improved RPC and WebSocket functionality. + +## Overview + +The `WebSocketAccountSubscriberV2` class provides the same interface as the original `WebSocketAccountSubscriber` but uses gill's modern TypeScript client library for Solana blockchain interactions. + +## Usage + +The usage is identical to the original `WebSocketAccountSubscriber`: + +```typescript +import { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscriberV2'; + +const subscriber = new WebSocketAccountSubscriberV2( + 'userAccount', // account name + program, // Anchor program instance + userAccountPublicKey, // PublicKey of the account to subscribe to + decodeBuffer, // optional custom decode function + resubOpts, // optional resubscription options + commitment // optional commitment level +); + +// Subscribe to account changes +await subscriber.subscribe((data) => { + console.log('Account updated:', data); +}); + +// Unsubscribe when done +await subscriber.unsubscribe(); +``` + +## Implementation Details + +### Gill Integration + +The implementation uses gill's `createSolanaClient` function to create RPC and WebSocket clients: + +```typescript +import { createSolanaClient } from 'gill'; + +const { rpc, rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: rpcUrl, // or "mainnet", "devnet", etc. +}); +``` + +### Key Differences from Original + +1. **RPC Client**: Uses gill's `rpc` client for account fetching +2. **WebSocket Subscriptions**: Uses gill's `rpcSubscriptions` for real-time updates +3. **Address Handling**: Converts `PublicKey` to gill's `Address` type for compatibility +4. **Response Formatting**: Converts gill responses to match the expected `AccountInfo` format +5. **Abort Signal**: Utilizes AbortSignal nodejs/web class to shutdown websocket connection synchronously diff --git a/sdk/src/accounts/webSocketAccountSubscriberV2.ts b/sdk/src/accounts/webSocketAccountSubscriberV2.ts new file mode 100644 index 0000000000..92a823757a --- /dev/null +++ b/sdk/src/accounts/webSocketAccountSubscriberV2.ts @@ -0,0 +1,310 @@ +import { + DataAndSlot, + AccountSubscriber, + ResubOpts, + BufferAndSlot, +} from './types'; +import { AnchorProvider, Program } from '@coral-xyz/anchor'; +import { capitalize } from './utils'; +import { + AccountInfoBase, + AccountInfoWithBase58EncodedData, + AccountInfoWithBase64EncodedData, + createSolanaClient, + isAddress, + type Address, + type Commitment, +} from 'gill'; +import { PublicKey } from '@solana/web3.js'; +import bs58 from 'bs58'; + +export class WebSocketAccountSubscriberV2 implements AccountSubscriber { + dataAndSlot?: DataAndSlot; + bufferAndSlot?: BufferAndSlot; + accountName: string; + logAccountName: string; + program: Program; + accountPublicKey: PublicKey; + decodeBufferFn: (buffer: Buffer) => T; + onChange: (data: T) => void; + listenerId?: number; + + resubOpts?: ResubOpts; + + commitment?: Commitment; + isUnsubscribing = false; + + timeoutId?: ReturnType; + + receivingData: boolean; + + // Gill client components + private rpc: ReturnType['rpc']; + private rpcSubscriptions: ReturnType< + typeof createSolanaClient + >['rpcSubscriptions']; + private abortController?: AbortController; + + public constructor( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => T, + resubOpts?: ResubOpts, + commitment?: Commitment + ) { + this.accountName = accountName; + this.logAccountName = `${accountName}-${accountPublicKey.toBase58()}-ws-acct-subscriber-v2`; + this.program = program; + this.accountPublicKey = accountPublicKey; + this.decodeBufferFn = decodeBuffer; + this.resubOpts = resubOpts; + if (this.resubOpts?.resubTimeoutMs < 1000) { + console.log( + `resubTimeoutMs should be at least 1000ms to avoid spamming resub ${this.logAccountName}` + ); + } + this.receivingData = false; + if ( + ['recent', 'single', 'singleGossip', 'root', 'max'].includes( + (this.program.provider as AnchorProvider).opts.commitment + ) + ) { + console.warn( + `using commitment ${ + (this.program.provider as AnchorProvider).opts.commitment + } that is not supported by gill, this may cause issues` + ); + } + this.commitment = + commitment ?? + ((this.program.provider as AnchorProvider).opts.commitment as Commitment); + + // Initialize gill client using the same RPC URL as the program provider + const rpcUrl = (this.program.provider as AnchorProvider).connection + .rpcEndpoint; + const { rpc, rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: rpcUrl, + }); + this.rpc = rpc; + this.rpcSubscriptions = rpcSubscriptions; + } + + async subscribe(onChange: (data: T) => void): Promise { + if (this.listenerId != null || this.isUnsubscribing) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Subscribe returning early - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` + ); + } + return; + } + + this.onChange = onChange; + if (!this.dataAndSlot) { + await this.fetch(); + } + + // Create abort controller for proper cleanup + const abortController = new AbortController(); + this.abortController = abortController; + + // Subscribe to account changes using gill's rpcSubscriptions + const pubkey = this.accountPublicKey.toBase58(); + if (isAddress(pubkey)) { + const subscription = await this.rpcSubscriptions + .accountNotifications(pubkey, { + commitment: this.commitment, + encoding: 'base64', + }) + .subscribe({ + abortSignal: abortController.signal, + }); + + for await (const notification of subscription) { + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + clearTimeout(this.timeoutId); + this.handleRpcResponse(notification.context, notification.value); + this.setTimeout(); + } else { + this.handleRpcResponse(notification.context, notification.value); + } + } + } + + this.listenerId = Math.random(); // Unique ID for logging purposes + + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + this.setTimeout(); + } + } + + setData(data: T, slot?: number): void { + const newSlot = slot || 0; + if (this.dataAndSlot && this.dataAndSlot.slot > newSlot) { + return; + } + + this.dataAndSlot = { + data, + slot, + }; + } + + protected setTimeout(): void { + if (!this.onChange) { + throw new Error('onChange callback function must be set'); + } + this.timeoutId = setTimeout( + async () => { + if (this.isUnsubscribing) { + // If we are in the process of unsubscribing, do not attempt to resubscribe + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Timeout fired but isUnsubscribing=true, skipping resubscribe` + ); + } + return; + } + + if (this.receivingData) { + if (this.resubOpts?.logResubMessages) { + console.log( + `No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` + ); + } + await this.unsubscribe(true); + this.receivingData = false; + await this.subscribe(this.onChange); + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Resubscribe completed - receivingData=${this.receivingData}, listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` + ); + } + } else { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.logAccountName}] Timeout fired but receivingData=false, skipping resubscribe` + ); + } + } + }, + this.resubOpts?.resubTimeoutMs + ); + } + + async fetch(): Promise { + // Use gill's rpc for fetching account info + const accountAddress = this.accountPublicKey.toBase58() as Address; + const rpcResponse = await this.rpc + .getAccountInfo(accountAddress, { + commitment: this.commitment, + encoding: 'base64', + }) + .send(); + + // Convert gill response to match the expected format + const context = { + slot: Number(rpcResponse.context.slot), + }; + + const accountInfo = rpcResponse.value; + + this.handleRpcResponse({ slot: BigInt(context.slot) }, accountInfo); + } + + handleRpcResponse( + context: { slot: bigint }, + accountInfo?: AccountInfoBase & + (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData) + ): void { + const newSlot = context.slot; + let newBuffer: Buffer | undefined = undefined; + + if (accountInfo) { + // Extract data from gill response + if (accountInfo.data) { + // Handle different data formats from gill + if (Array.isArray(accountInfo.data)) { + // If it's a tuple [data, encoding] + const [data, encoding] = accountInfo.data; + + if (encoding === 'base58') { + // we know encoding will be base58 + // Convert base58 to buffer using bs58 + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + } + + if (!this.bufferAndSlot) { + this.bufferAndSlot = { + buffer: newBuffer, + slot: Number(newSlot), + }; + if (newBuffer) { + const account = this.decodeBuffer(newBuffer); + this.dataAndSlot = { + data: account, + slot: Number(newSlot), + }; + this.onChange(account); + } + return; + } + + if (Number(newSlot) < this.bufferAndSlot.slot) { + return; + } + + const oldBuffer = this.bufferAndSlot.buffer; + if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) { + this.bufferAndSlot = { + buffer: newBuffer, + slot: Number(newSlot), + }; + const account = this.decodeBuffer(newBuffer); + this.dataAndSlot = { + data: account, + slot: Number(newSlot), + }; + this.onChange(account); + } + } + + decodeBuffer(buffer: Buffer): T { + if (this.decodeBufferFn) { + return this.decodeBufferFn(buffer); + } else { + return this.program.account[this.accountName].coder.accounts.decode( + capitalize(this.accountName), + buffer + ); + } + } + + unsubscribe(onResub = false): Promise { + if (!onResub && this.resubOpts) { + this.resubOpts.resubTimeoutMs = undefined; + } + this.isUnsubscribing = true; + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + // Abort the WebSocket subscription + if (this.abortController) { + this.abortController.abort('unsubscribing'); + this.abortController = undefined; + } + + this.listenerId = undefined; + this.isUnsubscribing = false; + + return Promise.resolve(); + } +} diff --git a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts index c49d425796..e8abca5c62 100644 --- a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts @@ -28,6 +28,7 @@ import { findAllMarketAndOracles } from '../config'; import { findDelistedPerpMarketsAndOracles } from './utils'; import { getOracleId } from '../oracles/oracleId'; import { OracleSource } from '../types'; +import { WebSocketAccountSubscriberV2 } from './webSocketAccountSubscriberV2'; const ORACLE_DEFAULT_ID = getOracleId( PublicKey.default, @@ -68,6 +69,14 @@ export class WebSocketDriftClientAccountSubscriber initialPerpMarketAccountData: Map; initialSpotMarketAccountData: Map; initialOraclePriceData: Map; + customPerpMarketAccountSubscriber?: new ( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => any, + resubOpts?: ResubOpts, + commitment?: Commitment + ) => AccountSubscriber; protected isSubscribing = false; protected subscriptionPromise: Promise; @@ -81,7 +90,15 @@ export class WebSocketDriftClientAccountSubscriber shouldFindAllMarketsAndOracles: boolean, delistedMarketSetting: DelistedMarketSetting, resubOpts?: ResubOpts, - commitment?: Commitment + commitment?: Commitment, + customPerpMarketAccountSubscriber?: new ( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => any, + resubOpts?: ResubOpts, + commitment?: Commitment + ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber ) { this.isSubscribed = false; this.program = program; @@ -93,6 +110,7 @@ export class WebSocketDriftClientAccountSubscriber this.delistedMarketSetting = delistedMarketSetting; this.resubOpts = resubOpts; this.commitment = commitment; + this.customPerpMarketAccountSubscriber = customPerpMarketAccountSubscriber; } public async subscribe(): Promise { @@ -292,7 +310,9 @@ export class WebSocketDriftClientAccountSubscriber this.program.programId, marketIndex ); - const accountSubscriber = new WebSocketAccountSubscriber( + const AccountSubscriberClass = + this.customPerpMarketAccountSubscriber || WebSocketAccountSubscriber; + const accountSubscriber = new AccountSubscriberClass( 'perpMarket', this.program, perpMarketPublicKey, diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 2fbfc96d1a..5cda46c3f0 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -446,7 +446,8 @@ export class DriftClient { resubTimeoutMs: config.accountSubscription?.resubTimeoutMs, logResubMessages: config.accountSubscription?.logResubMessages, }, - config.accountSubscription?.commitment + config.accountSubscription?.commitment, + config.accountSubscription?.perpMarketAccountSubscriber ); } this.eventEmitter = this.accountSubscriber.eventEmitter; diff --git a/sdk/src/driftClientConfig.ts b/sdk/src/driftClientConfig.ts index e833bd54e4..bf4db114a4 100644 --- a/sdk/src/driftClientConfig.ts +++ b/sdk/src/driftClientConfig.ts @@ -11,8 +11,14 @@ import { BulkAccountLoader } from './accounts/bulkAccountLoader'; import { DriftEnv } from './config'; import { TxSender } from './tx/types'; import { TxHandler, TxHandlerConfig } from './tx/txHandler'; -import { DelistedMarketSetting, GrpcConfigs } from './accounts/types'; -import { Coder } from '@coral-xyz/anchor'; +import { + GrpcConfigs, + ResubOpts, + DelistedMarketSetting, +} from './accounts/types'; +import { Coder, Program } from '@coral-xyz/anchor'; +import { WebSocketAccountSubscriber } from './accounts/webSocketAccountSubscriber'; +import { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscriberV2'; export type DriftClientConfig = { connection: Connection; @@ -57,6 +63,14 @@ export type DriftClientSubscriptionConfig = resubTimeoutMs?: number; logResubMessages?: boolean; commitment?: Commitment; + perpMarketAccountSubscriber?: new ( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => any, + resubOpts?: ResubOpts, + commitment?: Commitment + ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber; } | { type: 'polling'; diff --git a/sdk/src/index.ts b/sdk/src/index.ts index cd39f6abe3..d4710de118 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -12,6 +12,7 @@ export * from './accounts/fetch'; export * from './accounts/webSocketDriftClientAccountSubscriber'; export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber'; export * from './accounts/webSocketHighLeverageModeConfigAccountSubscriber'; +export { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscriberV2'; export * from './accounts/bulkAccountLoader'; export * from './accounts/bulkUserSubscription'; export * from './accounts/bulkUserStatsSubscription'; diff --git a/sdk/yarn.lock b/sdk/yarn.lock index bb142bcf3d..dd6247d0f0 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -543,6 +543,56 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz#282046f03e886e352b2d5f5da5eb755e01457f3f" integrity sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA== +"@solana-program/address-lookup-table@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@solana-program/address-lookup-table/-/address-lookup-table-0.7.0.tgz#6651657308547fefb59ce2e20b1871270e93d48d" + integrity sha512-dzCeIO5LtiK3bIg0AwO+TPeGURjSG2BKt0c4FRx7105AgLy7uzTktpUzUj6NXAK9SzbirI8HyvHUvw1uvL8O9A== + +"@solana-program/compute-budget@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@solana-program/compute-budget/-/compute-budget-0.8.0.tgz#0930aca4de1170ed607d64d89375074930aa8b93" + integrity sha512-qPKxdxaEsFxebZ4K5RPuy7VQIm/tfJLa1+Nlt3KNA8EYQkz9Xm8htdoEaXVrer9kpgzzp9R3I3Bh6omwCM06tQ== + +"@solana-program/system@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@solana-program/system/-/system-0.7.0.tgz#3e21c9fb31d3795eb65ba5cb663947c19b305bad" + integrity sha512-FKTBsKHpvHHNc1ATRm7SlC5nF/VdJtOSjldhcyfMN9R7xo712Mo2jHIzvBgn8zQO5Kg0DcWuKB7268Kv1ocicw== + +"@solana-program/token-2022@^0.4.1": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@solana-program/token-2022/-/token-2022-0.4.2.tgz#f90a638de82acb7933a114e884a24ac4be8ef635" + integrity sha512-zIpR5t4s9qEU3hZKupzIBxJ6nUV5/UVyIT400tu9vT1HMs5JHxaTTsb5GUhYjiiTvNwU0MQavbwc4Dl29L0Xvw== + +"@solana/accounts@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/accounts/-/accounts-2.3.0.tgz#957360edd572c1294772ee0eae3abd598189b16e" + integrity sha512-QgQTj404Z6PXNOyzaOpSzjgMOuGwG8vC66jSDB+3zHaRcEPRVRd2sVSrd1U6sHtnV3aiaS6YyDuPQMheg4K2jw== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-types" "2.3.0" + +"@solana/addresses@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/addresses/-/addresses-2.3.0.tgz#d89cba142819f01905a4bf30a1b990a1b55e490d" + integrity sha512-ypTNkY2ZaRFpHLnHAgaW8a83N0/WoqdFvCqf4CQmnMdFsZSdC7qOwcbd7YzdaQn9dy+P2hybewzB+KP7LutxGA== + dependencies: + "@solana/assertions" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/nominal-types" "2.3.0" + +"@solana/assertions@2.3.0", "@solana/assertions@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/assertions/-/assertions-2.3.0.tgz#f96f655088dea6fe9f79604da7615c745c64173b" + integrity sha512-Ekoet3khNg3XFLN7MIz8W31wPQISpKUGDGTylLptI+JjCDWx3PIa88xjEMqFo02WJ8sBj2NLV64Xg1sBcsHjZQ== + dependencies: + "@solana/errors" "2.3.0" + "@solana/buffer-layout-utils@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" @@ -574,7 +624,14 @@ dependencies: "@solana/errors" "2.0.0-rc.1" -"@solana/codecs-data-structures@2.0.0-preview.4", "@solana/codecs-data-structures@2.0.0-rc.1": +"@solana/codecs-core@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.3.0.tgz#6bf2bb565cb1ae880f8018635c92f751465d8695" + integrity sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw== + dependencies: + "@solana/errors" "2.3.0" + +"@solana/codecs-data-structures@2.0.0-preview.4", "@solana/codecs-data-structures@2.0.0-rc.1", "@solana/codecs-data-structures@2.3.0": version "2.0.0-preview.4" resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.4.tgz#f8a2470982a9792334737ea64000ccbdff287247" integrity sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA== @@ -599,6 +656,14 @@ "@solana/codecs-core" "2.0.0-rc.1" "@solana/errors" "2.0.0-rc.1" +"@solana/codecs-numbers@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.3.0.tgz#ac7e7f38aaf7fcd22ce2061fbdcd625e73828dc6" + integrity sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/codecs-strings@2.0.0-rc.1": version "2.0.0-rc.1" resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-rc.1.tgz#e1d9167075b8c5b0b60849f8add69c0f24307018" @@ -608,6 +673,15 @@ "@solana/codecs-numbers" "2.0.0-rc.1" "@solana/errors" "2.0.0-rc.1" +"@solana/codecs-strings@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.3.0.tgz#1b3a855dcd260283a732060aa6220f78b41251ae" + integrity sha512-y5pSBYwzVziXu521hh+VxqUtp0hYGTl1eWGoc1W+8mdvBdC1kTqm/X7aYQw33J42hw03JjryvYOvmGgk3Qz/Ug== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/codecs@2.0.0-rc.1": version "2.0.0-rc.1" resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.0.0-rc.1.tgz#146dc5db58bd3c28e04b4c805e6096c2d2a0a875" @@ -619,7 +693,18 @@ "@solana/codecs-strings" "2.0.0-rc.1" "@solana/options" "2.0.0-rc.1" -"@solana/errors@2.0.0-preview.4", "@solana/errors@2.0.0-rc.1": +"@solana/codecs@2.3.0", "@solana/codecs@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.3.0.tgz#75ea5811e2792d7344409b83ffbfd1d096292e36" + integrity sha512-JVqGPkzoeyU262hJGdH64kNLH0M+Oew2CIPOa/9tR3++q2pEd4jU2Rxdfye9sd0Ce3XJrR5AIa8ZfbyQXzjh+g== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/options" "2.3.0" + +"@solana/errors@2.0.0-preview.4", "@solana/errors@2.0.0-rc.1", "@solana/errors@2.3.0": version "2.0.0-preview.4" resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.0.0-preview.4.tgz#056ba76b6dd900dafa70117311bec3aef0f5250b" integrity sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA== @@ -627,6 +712,64 @@ chalk "^5.3.0" commander "^12.1.0" +"@solana/fast-stable-stringify@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/fast-stable-stringify/-/fast-stable-stringify-2.3.0.tgz#723b94e373952bad4549bdd2318f79f46313d85a" + integrity sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw== + +"@solana/functional@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/functional/-/functional-2.3.0.tgz#ac33815655e954bb78151446a571bc6c9fd9be28" + integrity sha512-AgsPh3W3tE+nK3eEw/W9qiSfTGwLYEvl0rWaxHht/lRcuDVwfKRzeSa5G79eioWFFqr+pTtoCr3D3OLkwKz02Q== + +"@solana/instructions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/instructions/-/instructions-2.3.0.tgz#ff25cbe545000a33fb3604d83f4e2b683de94ad3" + integrity sha512-PLMsmaIKu7hEAzyElrk2T7JJx4D+9eRwebhFZpy2PXziNSmFF929eRHKUsKqBFM3cYR1Yy3m6roBZfA+bGE/oQ== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/errors" "2.3.0" + +"@solana/keys@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/keys/-/keys-2.3.0.tgz#9d0b0ec09c2789a051b4df1945ed52631261186e" + integrity sha512-ZVVdga79pNH+2pVcm6fr2sWz9HTwfopDVhYb0Lh3dh+WBmJjwkabXEIHey2rUES7NjFa/G7sV8lrUn/v8LDCCQ== + dependencies: + "@solana/assertions" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/nominal-types" "2.3.0" + +"@solana/kit@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/kit/-/kit-2.3.0.tgz#92deb7c4883293617209aecac9a43d5e41ccf092" + integrity sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ== + dependencies: + "@solana/accounts" "2.3.0" + "@solana/addresses" "2.3.0" + "@solana/codecs" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/programs" "2.3.0" + "@solana/rpc" "2.3.0" + "@solana/rpc-parsed-types" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-subscriptions" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/signers" "2.3.0" + "@solana/sysvars" "2.3.0" + "@solana/transaction-confirmation" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + +"@solana/nominal-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/nominal-types/-/nominal-types-2.3.0.tgz#b67637241b4a45c756464e049c7a830880b6e944" + integrity sha512-uKlMnlP4PWW5UTXlhKM8lcgIaNj8dvd8xO4Y9l+FVvh9RvW2TO0GwUO6JCo7JBzCB0PSqRJdWWaQ8pu1Ti/OkA== + "@solana/options@2.0.0-rc.1": version "2.0.0-rc.1" resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-rc.1.tgz#06924ba316dc85791fc46726a51403144a85fc4d" @@ -638,6 +781,177 @@ "@solana/codecs-strings" "2.0.0-rc.1" "@solana/errors" "2.0.0-rc.1" +"@solana/options@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.3.0.tgz#f8a967b9ebae703b2c8adb8f4294df303ab866e8" + integrity sha512-PPnnZBRCWWoZQ11exPxf//DRzN2C6AoFsDI/u2AsQfYih434/7Kp4XLpfOMT/XESi+gdBMFNNfbES5zg3wAIkw== + dependencies: + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + +"@solana/programs@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/programs/-/programs-2.3.0.tgz#344193a0a4443217c177e2ec21bac7bc52afe4da" + integrity sha512-UXKujV71VCI5uPs+cFdwxybtHZAIZyQkqDiDnmK+DawtOO9mBn4Nimdb/6RjR2CXT78mzO9ZCZ3qfyX+ydcB7w== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/errors" "2.3.0" + +"@solana/promises@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/promises/-/promises-2.3.0.tgz#ae3fc000f4aef65561d9e4f9724d4635ed042750" + integrity sha512-GjVgutZKXVuojd9rWy1PuLnfcRfqsaCm7InCiZc8bqmJpoghlyluweNc7ml9Y5yQn1P2IOyzh9+p/77vIyNybQ== + +"@solana/rpc-api@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-api/-/rpc-api-2.3.0.tgz#c6e5f7353910bd7c7d2f8a6d4dab44d080bd313a" + integrity sha512-UUdiRfWoyYhJL9PPvFeJr4aJ554ob2jXcpn4vKmRVn9ire0sCbpQKYx6K8eEKHZWXKrDW8IDspgTl0gT/aJWVg== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/rpc-parsed-types" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + +"@solana/rpc-parsed-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-parsed-types/-/rpc-parsed-types-2.3.0.tgz#132b03f6b4c1b4688336ad48e76c2eea0d8c91d7" + integrity sha512-B5pHzyEIbBJf9KHej+zdr5ZNAdSvu7WLU2lOUPh81KHdHQs6dEb310LGxcpCc7HVE8IEdO20AbckewDiAN6OCg== + +"@solana/rpc-spec-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-spec-types/-/rpc-spec-types-2.3.0.tgz#010ea9de2f720e84bec2b93ca77ad3664b77235f" + integrity sha512-xQsb65lahjr8Wc9dMtP7xa0ZmDS8dOE2ncYjlvfyw/h4mpdXTUdrSMi6RtFwX33/rGuztQ7Hwaid5xLNSLvsFQ== + +"@solana/rpc-spec@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-spec/-/rpc-spec-2.3.0.tgz#2b679eb750c0f9270da6d451ea1bdc2c7783eb42" + integrity sha512-fA2LMX4BMixCrNB2n6T83AvjZ3oUQTu7qyPLyt8gHQaoEAXs8k6GZmu6iYcr+FboQCjUmRPgMaABbcr9j2J9Sw== + dependencies: + "@solana/errors" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + +"@solana/rpc-subscriptions-api@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-api/-/rpc-subscriptions-api-2.3.0.tgz#e779b8ad10e89b2f4a4ccb0fcd1a722d8bdd7729" + integrity sha512-9mCjVbum2Hg9KGX3LKsrI5Xs0KX390lS+Z8qB80bxhar6MJPugqIPH8uRgLhCW9GN3JprAfjRNl7our8CPvsPQ== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/rpc-subscriptions-spec" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + +"@solana/rpc-subscriptions-channel-websocket@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-channel-websocket/-/rpc-subscriptions-channel-websocket-2.3.0.tgz#11352ed281eccfa89a782a1b27444613ebeacca4" + integrity sha512-2oL6ceFwejIgeWzbNiUHI2tZZnaOxNTSerszcin7wYQwijxtpVgUHiuItM/Y70DQmH9sKhmikQp+dqeGalaJxw== + dependencies: + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/rpc-subscriptions-spec" "2.3.0" + "@solana/subscribable" "2.3.0" + +"@solana/rpc-subscriptions-spec@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions-spec/-/rpc-subscriptions-spec-2.3.0.tgz#a718a4ea97f57ed62291526b70740a42d576fada" + integrity sha512-rdmVcl4PvNKQeA2l8DorIeALCgJEMSu7U8AXJS1PICeb2lQuMeaR+6cs/iowjvIB0lMVjYN2sFf6Q3dJPu6wWg== + dependencies: + "@solana/errors" "2.3.0" + "@solana/promises" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/subscribable" "2.3.0" + +"@solana/rpc-subscriptions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-subscriptions/-/rpc-subscriptions-2.3.0.tgz#12639c17603e1a30113825350ddbfc3b50b6a031" + integrity sha512-Uyr10nZKGVzvCOqwCZgwYrzuoDyUdwtgQRefh13pXIrdo4wYjVmoLykH49Omt6abwStB0a4UL5gX9V4mFdDJZg== + dependencies: + "@solana/errors" "2.3.0" + "@solana/fast-stable-stringify" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/promises" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-subscriptions-api" "2.3.0" + "@solana/rpc-subscriptions-channel-websocket" "2.3.0" + "@solana/rpc-subscriptions-spec" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/subscribable" "2.3.0" + +"@solana/rpc-transformers@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-transformers/-/rpc-transformers-2.3.0.tgz#e008d2782047d574dbc74985c6cce26d7c3555f3" + integrity sha512-UuHYK3XEpo9nMXdjyGKkPCOr7WsZsxs7zLYDO1A5ELH3P3JoehvrDegYRAGzBS2VKsfApZ86ZpJToP0K3PhmMA== + dependencies: + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-types" "2.3.0" + +"@solana/rpc-transport-http@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-transport-http/-/rpc-transport-http-2.3.0.tgz#581601b9579b2a7fed9e0cb6fbcb95b4186e5b49" + integrity sha512-HFKydmxGw8nAF5N+S0NLnPBDCe5oMDtI2RAmW8DMqP4U3Zxt2XWhvV1SNkAldT5tF0U1vP+is6fHxyhk4xqEvg== + dependencies: + "@solana/errors" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + undici-types "^7.11.0" + +"@solana/rpc-types@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc-types/-/rpc-types-2.3.0.tgz#38adf5cb1c79c08086bd672edf7a56d19581d19f" + integrity sha512-O09YX2hED2QUyGxrMOxQ9GzH1LlEwwZWu69QbL4oYmIf6P5dzEEHcqRY6L1LsDVqc/dzAdEs/E1FaPrcIaIIPw== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/nominal-types" "2.3.0" + +"@solana/rpc@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/rpc/-/rpc-2.3.0.tgz#a65919520d14c122625fb887a2d72c95bf8691cf" + integrity sha512-ZWN76iNQAOCpYC7yKfb3UNLIMZf603JckLKOOLTHuy9MZnTN8XV6uwvDFhf42XvhglgUjGCEnbUqWtxQ9pa/pQ== + dependencies: + "@solana/errors" "2.3.0" + "@solana/fast-stable-stringify" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/rpc-api" "2.3.0" + "@solana/rpc-spec" "2.3.0" + "@solana/rpc-spec-types" "2.3.0" + "@solana/rpc-transformers" "2.3.0" + "@solana/rpc-transport-http" "2.3.0" + "@solana/rpc-types" "2.3.0" + +"@solana/signers@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/signers/-/signers-2.3.0.tgz#94569a7fb025a3f473661078fbca15b34ceacf94" + integrity sha512-OSv6fGr/MFRx6J+ZChQMRqKNPGGmdjkqarKkRzkwmv7v8quWsIRnJT5EV8tBy3LI4DLO/A8vKiNSPzvm1TdaiQ== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + "@solana/spl-token-group@^0.0.7": version "0.0.7" resolved "https://registry.yarnpkg.com/@solana/spl-token-group/-/spl-token-group-0.0.7.tgz#83c00f0cd0bda33115468cd28b89d94f8ec1fee4" @@ -685,6 +999,72 @@ "@solana/spl-token-metadata" "^0.1.2" buffer "^6.0.3" +"@solana/subscribable@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/subscribable/-/subscribable-2.3.0.tgz#4e48f1a4eeb1ccf22065b46fb8e3ed80d1a27f80" + integrity sha512-DkgohEDbMkdTWiKAoatY02Njr56WXx9e/dKKfmne8/Ad6/2llUIrax78nCdlvZW9quXMaXPTxZvdQqo9N669Og== + dependencies: + "@solana/errors" "2.3.0" + +"@solana/sysvars@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/sysvars/-/sysvars-2.3.0.tgz#eeea82f89716682014e801de5870344ddd02becd" + integrity sha512-LvjADZrpZ+CnhlHqfI5cmsRzX9Rpyb1Ox2dMHnbsRNzeKAMhu9w4ZBIaeTdO322zsTr509G1B+k2ABD3whvUBA== + dependencies: + "@solana/accounts" "2.3.0" + "@solana/codecs" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/rpc-types" "2.3.0" + +"@solana/transaction-confirmation@2.3.0", "@solana/transaction-confirmation@^2.1.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/transaction-confirmation/-/transaction-confirmation-2.3.0.tgz#f66e70334d797b5010b4ae27dc59de2f90b8ebe6" + integrity sha512-UiEuiHCfAAZEKdfne/XljFNJbsKAe701UQHKXEInYzIgBjRbvaeYZlBmkkqtxwcasgBTOmEaEKT44J14N9VZDw== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/promises" "2.3.0" + "@solana/rpc" "2.3.0" + "@solana/rpc-subscriptions" "2.3.0" + "@solana/rpc-types" "2.3.0" + "@solana/transaction-messages" "2.3.0" + "@solana/transactions" "2.3.0" + +"@solana/transaction-messages@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/transaction-messages/-/transaction-messages-2.3.0.tgz#e2a9c2f5565c7cc720aa09816aa3c17fb79c2abc" + integrity sha512-bgqvWuy3MqKS5JdNLH649q+ngiyOu5rGS3DizSnWwYUd76RxZl1kN6CoqHSrrMzFMvis6sck/yPGG3wqrMlAww== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@solana/rpc-types" "2.3.0" + +"@solana/transactions@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@solana/transactions/-/transactions-2.3.0.tgz#fc99f6ce6cc5706f2b8116bbf8a2f396c3ec3177" + integrity sha512-LnTvdi8QnrQtuEZor5Msje61sDpPstTVwKg4y81tNxDhiyomjuvnSNLAq6QsB9gIxUqbNzPZgOG9IU4I4/Uaug== + dependencies: + "@solana/addresses" "2.3.0" + "@solana/codecs-core" "2.3.0" + "@solana/codecs-data-structures" "2.3.0" + "@solana/codecs-numbers" "2.3.0" + "@solana/codecs-strings" "2.3.0" + "@solana/errors" "2.3.0" + "@solana/functional" "2.3.0" + "@solana/instructions" "2.3.0" + "@solana/keys" "2.3.0" + "@solana/nominal-types" "2.3.0" + "@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": version "1.98.0" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.98.0.tgz#21ecfe8198c10831df6f0cfde7f68370d0405917" @@ -2279,6 +2659,20 @@ get-proto@^1.0.0, get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" +gill@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/gill/-/gill-0.10.2.tgz#593c031c9964739739a07480199d0c409da40fb5" + integrity sha512-upWoY2dfOzKHOcX3UnD+B3h9WUunPv0oxeKzsIgKSaLyURpWK9oI+K2NHWbwrUFsXEK6ozu/sgkhuqyAcVTZCg== + dependencies: + "@solana-program/address-lookup-table" "^0.7.0" + "@solana-program/compute-budget" "^0.8.0" + "@solana-program/system" "^0.7.0" + "@solana-program/token-2022" "^0.4.1" + "@solana/assertions" "^2.1.1" + "@solana/codecs" "^2.1.1" + "@solana/kit" "^2.1.1" + "@solana/transaction-confirmation" "^2.1.1" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3816,6 +4210,11 @@ typescript@^5.7.3, typescript@^5.8.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +undici-types@^7.11.0: + version "7.12.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.12.0.tgz#15c5c7475c2a3ba30659529f5cdb4674b622fafb" + integrity sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" From dea787d508bb7ec2a591d4ef8c5adf7978bbebea Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Jul 2025 18:39:37 +0000 Subject: [PATCH 044/216] sdk: release v2.131.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 961ecff763..5e716be5b6 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.3 \ No newline at end of file +2.131.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 4910960c71..5540ad3e6c 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.3", + "version": "2.131.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 11004923ddf4b4e019a82ad7d91734c386f749ee Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 30 Jul 2025 21:12:17 -0700 Subject: [PATCH 045/216] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 498fe913ab..cc4d922902 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -304,7 +304,7 @@ jobs: VERSION: ${{ needs.release.outputs.version }} working-directory: ./sdk check-for-program-version-changes: - runs-on: ubuntu-latest + runs-on: ubicloud # Set job outputs to values from filter step outputs: program: ${{ steps.filter.outputs.program }} From 519318e6a574407dbe6700d1e2a4a0ccf68304de Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Thu, 31 Jul 2025 22:52:42 +0800 Subject: [PATCH 046/216] refactor(sdk): add MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT_GOV --- sdk/src/constants/insuranceFund.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sdk/src/constants/insuranceFund.ts diff --git a/sdk/src/constants/insuranceFund.ts b/sdk/src/constants/insuranceFund.ts new file mode 100644 index 0000000000..33b09cb64c --- /dev/null +++ b/sdk/src/constants/insuranceFund.ts @@ -0,0 +1,8 @@ +import { PERCENTAGE_PRECISION } from './numericConstants'; + +// follows program constant MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT_GOV in math/constants.rs +/** + * Max APR for DRIFT IF vault. + */ +export const MAX_APR_PER_REVENUE_SETTLE_TO_INSURANCE_FUND_VAULT_GOV = + PERCENTAGE_PRECISION.divn(22); From b6b7ece04405ceba426709de14444560ae993b97 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 14:58:24 +0000 Subject: [PATCH 047/216] sdk: release v2.131.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 5e716be5b6..295a1044f4 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.4 \ No newline at end of file +2.131.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 5540ad3e6c..6f682e4dfb 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.4", + "version": "2.131.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 4e82cff22ed7bed68810b739006a15a794ab101f Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Thu, 31 Jul 2025 23:07:46 +0800 Subject: [PATCH 048/216] refactor(sdk): improve exports --- sdk/src/constants/index.ts | 5 +++++ sdk/src/index.ts | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 sdk/src/constants/index.ts diff --git a/sdk/src/constants/index.ts b/sdk/src/constants/index.ts new file mode 100644 index 0000000000..5b3d49ec90 --- /dev/null +++ b/sdk/src/constants/index.ts @@ -0,0 +1,5 @@ +export * from './insuranceFund'; +export * from './numericConstants'; +export * from './perpMarkets'; +export * from './spotMarkets'; +export * from './txConstants'; diff --git a/sdk/src/index.ts b/sdk/src/index.ts index d4710de118..902f4a5337 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -7,7 +7,6 @@ export * from './oracles/types'; export * from './oracles/pythClient'; export * from './oracles/strictOraclePrice'; export * from './types'; -export * from './constants/perpMarkets'; export * from './accounts/fetch'; export * from './accounts/webSocketDriftClientAccountSubscriber'; export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber'; @@ -78,7 +77,6 @@ export * from './types'; export * from './math/utils'; export * from './math/fuel'; export * from './config'; -export * from './constants/numericConstants'; export * from './serum/serumSubscriber'; export * from './serum/serumFulfillmentConfigMap'; export * from './phoenix/phoenixSubscriber'; @@ -108,7 +106,6 @@ export * from './util/tps'; export * from './util/promiseTimeout'; export * from './util/pythOracleUtils'; export * from './math/spotBalance'; -export * from './constants/spotMarkets'; export * from './driftClientConfig'; export * from './dlob/DLOB'; export * from './dlob/DLOBNode'; @@ -132,7 +129,7 @@ export * from './util/chainClock'; export * from './util/TransactionConfirmationManager'; export * from './clock/clockSubscriber'; export * from './math/userStatus'; -export * from './constants/txConstants'; export * from './indicative-quotes/indicativeQuotesSender'; +export * from './constants'; export { BN, PublicKey, pyth }; From 69c4a8e78bb850cf33f614d8eaf0711178154e19 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 15:13:09 +0000 Subject: [PATCH 049/216] sdk: release v2.131.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 295a1044f4..73ab3b6dfd 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.5 \ No newline at end of file +2.131.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 6f682e4dfb..c67704f138 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.5", + "version": "2.131.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 40904e2ba972206979dd3d97f8e9ac5b12963a57 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Thu, 31 Jul 2025 12:20:02 -0400 Subject: [PATCH 050/216] sdk: updated idl --- sdk/src/idl/drift.json | 43 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index ef340a4d95..16ff48591f 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -7576,6 +7576,27 @@ } ], "args": [] + }, + { + "name": "updateFeatureBitFlagsMedianTriggerPrice", + "accounts": [ + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "state", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "enable", + "type": "bool" + } + ] } ], "accounts": [ @@ -11434,7 +11455,7 @@ { "name": "openBids", "docs": [ - "How many spot bids the user has open", + "How many spot non reduce only trigger orders the user has open", "precision: token mint precision" ], "type": "i64" @@ -11442,7 +11463,7 @@ { "name": "openAsks", "docs": [ - "How many spot asks the user has open", + "How many spot non reduce only trigger orders the user has open", "precision: token mint precision" ], "type": "i64" @@ -11541,7 +11562,7 @@ { "name": "openBids", "docs": [ - "The amount of open bids the user has in this perp market", + "The amount of non reduce only trigger orders the user has open", "precision: BASE_PRECISION" ], "type": "i64" @@ -11549,7 +11570,7 @@ { "name": "openAsks", "docs": [ - "The amount of open asks the user has in this perp market", + "The amount of non reduce only trigger orders the user has open", "precision: BASE_PRECISION" ], "type": "i64" @@ -12810,7 +12831,7 @@ "name": "MmOracleUpdate" }, { - "name": "EnableMedianTriggerPrice" + "name": "MedianTriggerPrice" } ] } @@ -12942,6 +12963,9 @@ }, { "name": "SafeTriggerOrder" + }, + { + "name": "NewTriggerReduceOnly" } ] } @@ -13738,6 +13762,13 @@ "option": "u64" }, "index": false + }, + { + "name": "triggerPrice", + "type": { + "option": "u64" + }, + "index": false } ] }, @@ -16046,4 +16077,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} +} \ No newline at end of file From ac60372beccefd0d4f36e9e7f4e7985e27962e32 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 16:25:05 +0000 Subject: [PATCH 051/216] sdk: release v2.131.0-beta.7 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 73ab3b6dfd..3f55a61dad 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.6 \ No newline at end of file +2.131.0-beta.7 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index c67704f138..45fcd5d233 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.6", + "version": "2.131.0-beta.7", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 37e21a4cf263998e49eca167aa5bc1127c86157a Mon Sep 17 00:00:00 2001 From: Lukas deConantsesznak Date: Thu, 31 Jul 2025 15:49:58 -0600 Subject: [PATCH 052/216] feat: initial websocket v2 program account sub --- ...DME_WebSocketProgramAccountSubscriberV2.md | 135 ++++ .../webSocketProgramAccountSubscriberV2.ts | 596 ++++++++++++++++++ .../orderSubscriber/WebsocketSubscription.ts | 6 +- 3 files changed, 734 insertions(+), 3 deletions(-) create mode 100644 sdk/src/accounts/README_WebSocketProgramAccountSubscriberV2.md create mode 100644 sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts diff --git a/sdk/src/accounts/README_WebSocketProgramAccountSubscriberV2.md b/sdk/src/accounts/README_WebSocketProgramAccountSubscriberV2.md new file mode 100644 index 0000000000..3f5d058e21 --- /dev/null +++ b/sdk/src/accounts/README_WebSocketProgramAccountSubscriberV2.md @@ -0,0 +1,135 @@ +# WebSocketProgramAccountSubscriberV2 + +This is a new implementation of the WebSocket program account subscriber that utilizes the [gill](https://www.npmjs.com/package/gill) library for improved RPC and WebSocket functionality, with additional smart polling logic for specific accounts. + +## Overview + +The `WebSocketProgramAccountSubscriberV2` class provides the same interface as the original `WebSocketProgramAccountSubscriber` but uses gill's modern TypeScript client library for Solana blockchain interactions. Additionally, it implements smart polling logic for accounts that don't update frequently (like long-tail markets) to prevent missing updates. + +## Key Features + +1. **Gill Integration**: Uses gill's `createSolanaClient` for RPC and WebSocket functionality +2. **Smart Polling**: Optional polling for specific accounts that don't update frequently +3. **Missed Update Detection**: Automatically detects when updates are missed and resubscribes +4. **Configurable Polling**: 30-second default polling interval, customizable per instance + +## Usage + +The usage is similar to the original `WebSocketProgramAccountSubscriber` with additional options: + +```typescript +import { WebSocketProgramAccountSubscriberV2 } from './accounts/webSocketProgramAccountSubscriberV2'; + +// Create subscriber with optional accounts to poll +const subscriber = new WebSocketProgramAccountSubscriberV2( + 'perpMarket', // account name + 'perpMarket', // account discriminator + program, // Anchor program instance + decodeBuffer, // decode function + { filters: [] }, // options + resubOpts, // optional resubscription options + [longTailMarket1, longTailMarket2] // optional list of accounts to poll +); + +// Subscribe to program account changes +await subscriber.subscribe((accountId, data, context, buffer) => { + console.log('Account updated:', accountId.toBase58(), data); +}); + +// Add more accounts to poll dynamically +subscriber.addAccountToPoll(newMarketPublicKey); + +// Remove accounts from polling +subscriber.removeAccountFromPoll(oldMarketPublicKey); + +// Change polling interval +subscriber.setPollingInterval(60000); // 60 seconds + +// Unsubscribe when done +await subscriber.unsubscribe(); +``` + +## Implementation Details + +### Gill Integration + +The implementation uses gill's `createSolanaClient` function to create RPC and WebSocket clients: + +```typescript +import { createSolanaClient } from 'gill'; + +const { rpc, rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: rpcUrl, // or "mainnet", "devnet", etc. +}); +``` + +### Smart Polling Logic + +1. **Account Selection**: Only accounts in the `accountsToMonitor` set are monitored +2. **Monitoring Period**: Default 30 seconds before starting to poll an account +3. **WebSocket Tracking**: Tracks the last WebSocket notification time for each account +4. **Conditional Polling**: Only starts polling if no WebSocket notification received in 30 seconds +5. **Batch Polling**: Uses `getMultipleAccounts` to poll all accounts in a single RPC call +6. **Dynamic Polling**: Stops polling individual accounts when WebSocket notifications are received +7. **Missed Update Detection**: Compares current slot and buffer with cached data +8. **Automatic Resubscription**: If a missed update is detected, the entire subscription is resubscribed + +### Key Differences from Original + +1. **RPC Client**: Uses gill's `rpc` client for account fetching +2. **WebSocket Subscriptions**: Uses gill's `rpcSubscriptions` for real-time updates +3. **Address Handling**: Converts `PublicKey` to gill's `Address` type for compatibility +4. **Response Formatting**: Converts gill responses to match the expected `AccountInfo` format +5. **Abort Signal**: Utilizes AbortSignal nodejs/web class to shutdown websocket connection synchronously +6. **Smart Polling**: Implements polling logic for specific accounts to prevent missed updates + +## Configuration Options + +### Constructor Parameters + +- `subscriptionName`: Name for logging purposes +- `accountDiscriminator`: Account discriminator for decoding +- `program`: Anchor program instance +- `decodeBufferFn`: Function to decode account data +- `options`: Subscription options (filters, commitment) +- `resubOpts`: Resubscription options +- `accountsToPoll`: Optional array of PublicKeys to poll + +### Polling Configuration + +- **Default Monitoring Period**: 30 seconds before starting to poll +- **WebSocket Tracking**: Records timestamp of last WebSocket notification per account +- **Conditional Polling**: Only polls accounts that haven't received WebSocket updates recently +- **Batch Polling**: Uses `getMultipleAccounts` for efficient polling of multiple accounts +- **Dynamic Management**: Automatically starts/stops polling based on WebSocket activity +- **Detection Logic**: Compares slot numbers and buffer contents +- **Resubscription**: Triggers when missed updates are detected +- **Logging**: Optional logging of polling events + +## Current Limitations + +1. **Gill API Compatibility**: Some type mismatches exist with the current gill version +2. **Account Key Handling**: The account key extraction from gill notifications needs refinement +3. **Encoding Types**: Some encoding type comparisons need to be resolved + +## Future Improvements + +1. **Better Gill Integration**: Resolve remaining type compatibility issues +2. **Enhanced Logging**: More detailed logging for debugging +3. **Performance Optimization**: Optimize polling frequency based on account activity +4. **Batch Polling**: Poll multiple accounts in a single RPC call + +## Migration from V1 + +The V2 implementation maintains the same interface as V1, making migration straightforward: + +```typescript +// V1 +const subscriber = new WebSocketProgramAccountSubscriber(...); + +// V2 (same interface) +const subscriber = new WebSocketProgramAccountSubscriberV2(...); + +// V2 with polling (new feature) +const subscriber = new WebSocketProgramAccountSubscriberV2(..., accountsToPoll); +``` diff --git a/sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts b/sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts new file mode 100644 index 0000000000..1176b86589 --- /dev/null +++ b/sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts @@ -0,0 +1,596 @@ +import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types'; +import { AnchorProvider, Program } from '@coral-xyz/anchor'; +import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; +import { + AccountInfoBase, + AccountInfoWithBase58EncodedData, + AccountInfoWithBase64EncodedData, + createSolanaClient, + isAddress, + type Address, + type Commitment as GillCommitment, +} from 'gill'; +import bs58 from 'bs58'; + +export class WebSocketProgramAccountSubscriberV2 + implements ProgramAccountSubscriber +{ + subscriptionName: string; + accountDiscriminator: string; + bufferAndSlot?: BufferAndSlot; + bufferAndSlotMap: Map = new Map(); + program: Program; + decodeBuffer: (accountName: string, ix: Buffer) => T; + onChange: ( + accountId: PublicKey, + data: T, + context: Context, + buffer: Buffer + ) => void; + listenerId?: number; + resubOpts?: ResubOpts; + isUnsubscribing = false; + timeoutId?: ReturnType; + options: { filters: MemcmpFilter[]; commitment?: Commitment }; + + receivingData = false; + + // Gill client components + private rpc: ReturnType['rpc']; + private rpcSubscriptions: ReturnType< + typeof createSolanaClient + >['rpcSubscriptions']; + private abortController?: AbortController; + + // Polling logic for specific accounts + private accountsToMonitor: Set = new Set(); + private pollingIntervalMs: number = 30000; // 30 seconds + private pollingTimeouts: Map> = + new Map(); + private lastWsNotificationTime: Map = new Map(); // Track last WS notification time per account + private accountsCurrentlyPolling: Set = new Set(); // Track which accounts are being polled + private batchPollingTimeout?: ReturnType; // Single timeout for batch polling + + public constructor( + subscriptionName: string, + accountDiscriminator: string, + program: Program, + decodeBufferFn: (accountName: string, ix: Buffer) => T, + options: { filters: MemcmpFilter[]; commitment?: Commitment } = { + filters: [], + }, + resubOpts?: ResubOpts, + accountsToMonitor?: PublicKey[] // Optional list of accounts to poll + ) { + this.subscriptionName = subscriptionName; + this.accountDiscriminator = accountDiscriminator; + this.program = program; + this.decodeBuffer = decodeBufferFn; + this.resubOpts = resubOpts; + if (this.resubOpts?.resubTimeoutMs < 1000) { + console.log( + 'resubTimeoutMs should be at least 1000ms to avoid spamming resub' + ); + } + this.options = options; + this.receivingData = false; + + // Initialize accounts to monitor + if (accountsToMonitor) { + accountsToMonitor.forEach((account) => { + this.accountsToMonitor.add(account.toBase58()); + }); + } + + // Initialize gill client using the same RPC URL as the program provider + const rpcUrl = (this.program.provider as AnchorProvider).connection + .rpcEndpoint; + const { rpc, rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: rpcUrl, + }); + this.rpc = rpc; + this.rpcSubscriptions = rpcSubscriptions; + } + + async subscribe( + onChange: ( + accountId: PublicKey, + data: T, + context: Context, + buffer: Buffer + ) => void + ): Promise { + if (this.listenerId != null || this.isUnsubscribing) { + return; + } + + this.onChange = onChange; + + // Create abort controller for proper cleanup + const abortController = new AbortController(); + this.abortController = abortController; + + // Subscribe to program account changes using gill's rpcSubscriptions + const programId = this.program.programId.toBase58(); + if (isAddress(programId)) { + const subscription = await this.rpcSubscriptions + .programNotifications(programId, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + filters: this.options.filters.map((filter) => ({ + memcmp: { + offset: BigInt(filter.memcmp.offset), + bytes: filter.memcmp.bytes as any, + encoding: 'base64' as const, + }, + })), + }) + .subscribe({ + abortSignal: abortController.signal, + }); + + for await (const notification of subscription) { + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + clearTimeout(this.timeoutId); + this.handleRpcResponse( + notification.context, + notification.value.account + ); + this.setTimeout(); + } else { + this.handleRpcResponse( + notification.context, + notification.value.account + ); + } + } + } + + this.listenerId = Math.random(); // Unique ID for logging purposes + + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + this.setTimeout(); + } + + // Start monitoring for accounts that may need polling if no WS event is received + this.startMonitoringForAccounts(); + } + + protected setTimeout(): void { + if (!this.onChange) { + throw new Error('onChange callback function must be set'); + } + this.timeoutId = setTimeout( + async () => { + if (this.isUnsubscribing) { + // If we are in the process of unsubscribing, do not attempt to resubscribe + return; + } + + if (this.receivingData) { + if (this.resubOpts?.logResubMessages) { + console.log( + `No ws data from ${this.subscriptionName} in ${this.resubOpts?.resubTimeoutMs}ms, resubscribing` + ); + } + await this.unsubscribe(true); + this.receivingData = false; + await this.subscribe(this.onChange); + } + }, + this.resubOpts?.resubTimeoutMs + ); + } + + handleRpcResponse( + context: { slot: bigint }, + accountInfo?: AccountInfoBase & + (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData) + ): void { + const newSlot = Number(context.slot); + let newBuffer: Buffer | undefined = undefined; + + if (accountInfo) { + // Extract data from gill response + if (accountInfo.data) { + // Handle different data formats from gill + if (Array.isArray(accountInfo.data)) { + // If it's a tuple [data, encoding] + const [data, encoding] = accountInfo.data; + + if (encoding === ('base58' as any)) { + // Convert base58 to buffer using bs58 + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + } + + // Convert gill's account key to PublicKey + // Note: accountInfo doesn't have a key property, we need to get it from the notification + // For now, we'll use a placeholder - this needs to be fixed based on the actual gill API + const accountId = new PublicKey('11111111111111111111111111111111'); // Placeholder + const accountIdString = accountId.toBase58(); + + const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString); + + // Track WebSocket notification time for this account + this.lastWsNotificationTime.set(accountIdString, Date.now()); + + // If this account was being polled, stop polling it + if (this.accountsCurrentlyPolling.has(accountIdString)) { + this.accountsCurrentlyPolling.delete(accountIdString); + + // If no more accounts are being polled, stop batch polling + if ( + this.accountsCurrentlyPolling.size === 0 && + this.batchPollingTimeout + ) { + clearTimeout(this.batchPollingTimeout); + this.batchPollingTimeout = undefined; + } + } + + if (!existingBufferAndSlot) { + if (newBuffer) { + this.bufferAndSlotMap.set(accountIdString, { + buffer: newBuffer, + slot: newSlot, + }); + const account = this.decodeBuffer(this.accountDiscriminator, newBuffer); + this.onChange(accountId, account, { slot: newSlot }, newBuffer); + } + return; + } + + if (newSlot < existingBufferAndSlot.slot) { + return; + } + + const oldBuffer = existingBufferAndSlot.buffer; + if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) { + this.bufferAndSlotMap.set(accountIdString, { + buffer: newBuffer, + slot: newSlot, + }); + const account = this.decodeBuffer(this.accountDiscriminator, newBuffer); + this.onChange(accountId, account, { slot: newSlot }, newBuffer); + } + } + + private startMonitoringForAccounts(): void { + // Clear any existing polling timeouts + this.clearPollingTimeouts(); + + // Start monitoring for each account in the accountsToMonitor set + this.accountsToMonitor.forEach((accountIdString) => { + this.startMonitoringForAccount(accountIdString); + }); + } + + private startMonitoringForAccount(accountIdString: string): void { + // Clear existing timeout for this account + const existingTimeout = this.pollingTimeouts.get(accountIdString); + if (existingTimeout) { + clearTimeout(existingTimeout); + } + + // Set up monitoring timeout - only start polling if no WS notification in 30s + const timeoutId = setTimeout(async () => { + // Check if we've received a WS notification for this account recently + const lastNotificationTime = + this.lastWsNotificationTime.get(accountIdString); + const currentTime = Date.now(); + + if ( + !lastNotificationTime || + currentTime - lastNotificationTime >= this.pollingIntervalMs + ) { + // No recent WS notification, start polling + await this.pollAccount(accountIdString); + // Schedule next poll + this.startPollingForAccount(accountIdString); + } else { + // We received a WS notification recently, continue monitoring + this.startMonitoringForAccount(accountIdString); + } + }, this.pollingIntervalMs); + + this.pollingTimeouts.set(accountIdString, timeoutId); + } + + private startPollingForAccount(accountIdString: string): void { + // Add account to polling set + this.accountsCurrentlyPolling.add(accountIdString); + + // If this is the first account being polled, start batch polling + if (this.accountsCurrentlyPolling.size === 1) { + this.startBatchPolling(); + } + } + + private startBatchPolling(): void { + // Clear existing batch polling timeout + if (this.batchPollingTimeout) { + clearTimeout(this.batchPollingTimeout); + } + + // Set up batch polling interval + this.batchPollingTimeout = setTimeout(async () => { + await this.pollAllAccounts(); + // Schedule next batch poll + this.startBatchPolling(); + }, this.pollingIntervalMs); + } + + private async pollAllAccounts(): Promise { + try { + // Get all accounts currently being polled + const accountsToPoll = Array.from(this.accountsCurrentlyPolling); + if (accountsToPoll.length === 0) { + return; + } + + // Fetch all accounts in a single batch request + const accountAddresses = accountsToPoll.map( + (accountId) => accountId as Address + ); + const rpcResponse = await this.rpc + .getMultipleAccounts(accountAddresses, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + }) + .send(); + + const currentSlot = Number(rpcResponse.context.slot); + + // Process each account response + for (let i = 0; i < accountsToPoll.length; i++) { + const accountIdString = accountsToPoll[i]; + const accountInfo = rpcResponse.value[i]; + + if (!accountInfo) { + continue; + } + + const existingBufferAndSlot = + this.bufferAndSlotMap.get(accountIdString); + + if (!existingBufferAndSlot) { + // Account not in our map yet, add it + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + newBuffer = Buffer.from(data, encoding); + } + } + + if (newBuffer) { + this.bufferAndSlotMap.set(accountIdString, { + buffer: newBuffer, + slot: currentSlot, + }); + const account = this.decodeBuffer( + this.accountDiscriminator, + newBuffer + ); + const accountId = new PublicKey(accountIdString); + this.onChange(accountId, account, { slot: currentSlot }, newBuffer); + } + continue; + } + + // Check if we missed an update + if (currentSlot > existingBufferAndSlot.slot) { + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + if (encoding === ('base58' as any)) { + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + + // Check if buffer has changed + if ( + newBuffer && + (!existingBufferAndSlot.buffer || + !newBuffer.equals(existingBufferAndSlot.buffer)) + ) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Batch polling detected missed update for account ${accountIdString}, resubscribing` + ); + } + // We missed an update, resubscribe + await this.unsubscribe(true); + this.receivingData = false; + await this.subscribe(this.onChange); + return; + } + } + } + } catch (error) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error batch polling accounts:`, + error + ); + } + } + } + + private async pollAccount(accountIdString: string): Promise { + try { + // Fetch current account data using gill's rpc + const accountAddress = accountIdString as Address; + const rpcResponse = await this.rpc + .getAccountInfo(accountAddress, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + }) + .send(); + + const currentSlot = Number(rpcResponse.context.slot); + const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString); + + if (!existingBufferAndSlot) { + // Account not in our map yet, add it + if (rpcResponse.value) { + let newBuffer: Buffer | undefined = undefined; + if (rpcResponse.value.data) { + if (Array.isArray(rpcResponse.value.data)) { + const [data, encoding] = rpcResponse.value.data; + newBuffer = Buffer.from(data, encoding); + } + } + + if (newBuffer) { + this.bufferAndSlotMap.set(accountIdString, { + buffer: newBuffer, + slot: currentSlot, + }); + const account = this.decodeBuffer( + this.accountDiscriminator, + newBuffer + ); + const accountId = new PublicKey(accountIdString); + this.onChange(accountId, account, { slot: currentSlot }, newBuffer); + } + } + return; + } + + // Check if we missed an update + if (currentSlot > existingBufferAndSlot.slot) { + let newBuffer: Buffer | undefined = undefined; + if (rpcResponse.value) { + if (rpcResponse.value.data) { + if (Array.isArray(rpcResponse.value.data)) { + const [data, encoding] = rpcResponse.value.data; + if (encoding === ('base58' as any)) { + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + } + + // Check if buffer has changed + if ( + newBuffer && + (!existingBufferAndSlot.buffer || + !newBuffer.equals(existingBufferAndSlot.buffer)) + ) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Polling detected missed update for account ${accountIdString}, resubscribing` + ); + } + // We missed an update, resubscribe + await this.unsubscribe(true); + this.receivingData = false; + await this.subscribe(this.onChange); + return; + } + } + } catch (error) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error polling account ${accountIdString}:`, + error + ); + } + } + } + + private clearPollingTimeouts(): void { + this.pollingTimeouts.forEach((timeoutId) => { + clearTimeout(timeoutId); + }); + this.pollingTimeouts.clear(); + + // Clear batch polling timeout + if (this.batchPollingTimeout) { + clearTimeout(this.batchPollingTimeout); + this.batchPollingTimeout = undefined; + } + + // Clear accounts currently polling + this.accountsCurrentlyPolling.clear(); + } + + unsubscribe(onResub = false): Promise { + if (!onResub) { + this.resubOpts.resubTimeoutMs = undefined; + } + this.isUnsubscribing = true; + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + // Clear polling timeouts + this.clearPollingTimeouts(); + + // Abort the WebSocket subscription + if (this.abortController) { + this.abortController.abort('unsubscribing'); + this.abortController = undefined; + } + + this.listenerId = undefined; + this.isUnsubscribing = false; + + return Promise.resolve(); + } + + // Method to add accounts to the polling list + addAccountToMonitor(accountId: PublicKey): void { + const accountIdString = accountId.toBase58(); + this.accountsToMonitor.add(accountIdString); + + // If already subscribed, start monitoring for this account + if (this.listenerId != null && !this.isUnsubscribing) { + this.startMonitoringForAccount(accountIdString); + } + } + + // Method to remove accounts from the polling list + removeAccountFromMonitor(accountId: PublicKey): void { + const accountIdString = accountId.toBase58(); + this.accountsToMonitor.delete(accountIdString); + + // Clear monitoring timeout for this account + const timeoutId = this.pollingTimeouts.get(accountIdString); + if (timeoutId) { + clearTimeout(timeoutId); + this.pollingTimeouts.delete(accountIdString); + } + + // Remove from currently polling set if it was being polled + this.accountsCurrentlyPolling.delete(accountIdString); + + // If no more accounts are being polled, stop batch polling + if (this.accountsCurrentlyPolling.size === 0 && this.batchPollingTimeout) { + clearTimeout(this.batchPollingTimeout); + this.batchPollingTimeout = undefined; + } + } + + // Method to set polling interval + setPollingInterval(intervalMs: number): void { + this.pollingIntervalMs = intervalMs; + // Restart monitoring with new interval if already subscribed + if (this.listenerId != null && !this.isUnsubscribing) { + this.startMonitoringForAccounts(); + } + } +} diff --git a/sdk/src/orderSubscriber/WebsocketSubscription.ts b/sdk/src/orderSubscriber/WebsocketSubscription.ts index ade8efc6ae..2fb648663d 100644 --- a/sdk/src/orderSubscriber/WebsocketSubscription.ts +++ b/sdk/src/orderSubscriber/WebsocketSubscription.ts @@ -1,9 +1,9 @@ import { OrderSubscriber } from './OrderSubscriber'; import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; -import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; import { UserAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; +import { WebSocketProgramAccountSubscriberV2 } from '../accounts/webSocketProgramAccountSubscriberV2'; export class WebsocketSubscription { private orderSubscriber: OrderSubscriber; @@ -12,7 +12,7 @@ export class WebsocketSubscription { private resubOpts?: ResubOpts; private resyncIntervalMs?: number; - private subscriber?: WebSocketProgramAccountSubscriber; + private subscriber?: WebSocketProgramAccountSubscriberV2; private resyncTimeoutId?: ReturnType; private decoded?: boolean; @@ -45,7 +45,7 @@ export class WebsocketSubscription { return; } - this.subscriber = new WebSocketProgramAccountSubscriber( + this.subscriber = new WebSocketProgramAccountSubscriberV2( 'OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, From 5ae39ae77140e7c65dd36fcd9fd08bd3b22f99a0 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 21:55:30 +0000 Subject: [PATCH 053/216] sdk: release v2.131.0-beta.8 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 3f55a61dad..a3c3ab00d2 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.7 \ No newline at end of file +2.131.0-beta.8 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 45fcd5d233..7766b12fca 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.7", + "version": "2.131.0-beta.8", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From cc69f2c9086c428fac4c728cdac0fb9a8615764c Mon Sep 17 00:00:00 2001 From: LukasDeco Date: Thu, 31 Jul 2025 16:41:17 -0600 Subject: [PATCH 054/216] fix: ws v2 subscriber hangs on async iterable loop (#1793) --- .../accounts/webSocketAccountSubscriberV2.ts | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/sdk/src/accounts/webSocketAccountSubscriberV2.ts b/sdk/src/accounts/webSocketAccountSubscriberV2.ts index 92a823757a..9068f1b156 100644 --- a/sdk/src/accounts/webSocketAccountSubscriberV2.ts +++ b/sdk/src/accounts/webSocketAccountSubscriberV2.ts @@ -90,6 +90,19 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { this.rpcSubscriptions = rpcSubscriptions; } + private async handleNotificationLoop(subscription: AsyncIterable) { + for await (const notification of subscription) { + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + clearTimeout(this.timeoutId); + this.handleRpcResponse(notification.context, notification.value); + this.setTimeout(); + } else { + this.handleRpcResponse(notification.context, notification.value); + } + } + } + async subscribe(onChange: (data: T) => void): Promise { if (this.listenerId != null || this.isUnsubscribing) { if (this.resubOpts?.logResubMessages) { @@ -109,6 +122,13 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { const abortController = new AbortController(); this.abortController = abortController; + this.listenerId = Math.random(); // Unique ID for logging purposes + + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + this.setTimeout(); + } + // Subscribe to account changes using gill's rpcSubscriptions const pubkey = this.accountPublicKey.toBase58(); if (isAddress(pubkey)) { @@ -121,23 +141,8 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { abortSignal: abortController.signal, }); - for await (const notification of subscription) { - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - clearTimeout(this.timeoutId); - this.handleRpcResponse(notification.context, notification.value); - this.setTimeout(); - } else { - this.handleRpcResponse(notification.context, notification.value); - } - } - } - - this.listenerId = Math.random(); // Unique ID for logging purposes - - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - this.setTimeout(); + // Start notification loop without awaiting + this.handleNotificationLoop(subscription); } } From 88250f9940edab67e69c8f5f391c82c269143edf Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 22:46:24 +0000 Subject: [PATCH 055/216] sdk: release v2.131.0-beta.9 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index a3c3ab00d2..5f666349f2 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.8 \ No newline at end of file +2.131.0-beta.9 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 7766b12fca..5d872b9a7d 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.8", + "version": "2.131.0-beta.9", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 1c3328bc95088279c96bad831caec8c2df83604d Mon Sep 17 00:00:00 2001 From: Lukas deConantsesznak Date: Thu, 31 Jul 2025 16:54:46 -0600 Subject: [PATCH 056/216] fix: websocket order subscriber using v2 too soon --- sdk/src/orderSubscriber/WebsocketSubscription.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/orderSubscriber/WebsocketSubscription.ts b/sdk/src/orderSubscriber/WebsocketSubscription.ts index 2fb648663d..829eadbfcf 100644 --- a/sdk/src/orderSubscriber/WebsocketSubscription.ts +++ b/sdk/src/orderSubscriber/WebsocketSubscription.ts @@ -3,7 +3,7 @@ import { getNonIdleUserFilter, getUserFilter } from '../memcmp'; import { UserAccount } from '../types'; import { Commitment, Context, PublicKey } from '@solana/web3.js'; import { ResubOpts } from '../accounts/types'; -import { WebSocketProgramAccountSubscriberV2 } from '../accounts/webSocketProgramAccountSubscriberV2'; +import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber'; export class WebsocketSubscription { private orderSubscriber: OrderSubscriber; @@ -12,7 +12,7 @@ export class WebsocketSubscription { private resubOpts?: ResubOpts; private resyncIntervalMs?: number; - private subscriber?: WebSocketProgramAccountSubscriberV2; + private subscriber?: WebSocketProgramAccountSubscriber; private resyncTimeoutId?: ReturnType; private decoded?: boolean; @@ -45,7 +45,7 @@ export class WebsocketSubscription { return; } - this.subscriber = new WebSocketProgramAccountSubscriberV2( + this.subscriber = new WebSocketProgramAccountSubscriber( 'OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, From bec44455d20f386788db6cf7f268b312d38c1879 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 23:01:37 +0000 Subject: [PATCH 057/216] sdk: release v2.131.0-beta.10 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 5f666349f2..c876a695ca 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.9 \ No newline at end of file +2.131.0-beta.10 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 5d872b9a7d..9dcb853983 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.9", + "version": "2.131.0-beta.10", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From b13eca3a1b5b6886005969277f56a19aca73a926 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:27:09 -0400 Subject: [PATCH 058/216] sdk: enter-high-leverage-mode-false (#1791) --- sdk/src/driftClient.ts | 25 ++++++++++++---------- sdk/src/user.ts | 47 ++++++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 5cda46c3f0..e13af3b15e 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -9854,17 +9854,20 @@ export class DriftClient { }) : undefined; - const ix = await this.program.instruction.disableUserHighLeverageMode({ - accounts: { - state: await this.getStatePublicKey(), - user, - authority: this.wallet.publicKey, - highLeverageModeConfig: getHighLeverageModeConfigPublicKey( - this.program.programId - ), - }, - remainingAccounts, - }); + const ix = await this.program.instruction.disableUserHighLeverageMode( + false, + { + accounts: { + state: await this.getStatePublicKey(), + user, + authority: this.wallet.publicKey, + highLeverageModeConfig: getHighLeverageModeConfigPublicKey( + this.program.programId + ), + }, + remainingAccounts, + } + ); return ix; } diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 215d0450fd..ec474f29b0 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -662,7 +662,7 @@ export class User { public getPerpBuyingPower( marketIndex: number, collateralBuffer = ZERO, - enterHighLeverageMode = false + enterHighLeverageMode = undefined ): BN { const perpPosition = this.getPerpPositionWithLPSettle( marketIndex, @@ -697,7 +697,7 @@ export class User { marketIndex: number, freeCollateral: BN, baseAssetAmount: BN, - enterHighLeverageMode = false + enterHighLeverageMode = undefined ): BN { const marginRatio = calculateMarketMarginRatio( this.driftClient.getPerpMarketAccount(marketIndex), @@ -716,7 +716,7 @@ export class User { */ public getFreeCollateral( marginCategory: MarginCategory = 'Initial', - enterHighLeverageMode = false + enterHighLeverageMode = undefined ): BN { const totalCollateral = this.getTotalCollateral(marginCategory, true); const marginRequirement = @@ -735,7 +735,7 @@ export class User { liquidationBuffer?: BN, strict = false, includeOpenOrders = true, - enteringHighLeverage = false + enteringHighLeverage = undefined ): BN { return this.getTotalPerpPositionLiability( marginCategory, @@ -757,7 +757,7 @@ export class User { /** * @returns The initial margin requirement in USDC. : QUOTE_PRECISION */ - public getInitialMarginRequirement(enterHighLeverageMode = false): BN { + public getInitialMarginRequirement(enterHighLeverageMode = undefined): BN { return this.getMarginRequirement( 'Initial', undefined, @@ -1432,7 +1432,7 @@ export class User { liquidationBuffer?: BN, includeOpenOrders?: boolean, strict = false, - enteringHighLeverage = false + enteringHighLeverage = undefined ): BN { const market = this.driftClient.getPerpMarketAccount( perpPosition.marketIndex @@ -1476,13 +1476,17 @@ export class User { } if (marginCategory) { + const userCustomMargin = this.getUserAccount().maxMarginRatio; let marginRatio = new BN( calculateMarketMarginRatio( market, baseAssetAmount.abs(), marginCategory, - this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode(marginCategory) || enteringHighLeverage + enteringHighLeverage === false + ? Math.max(market.marginRatioInitial, userCustomMargin) + : userCustomMargin, + this.isHighLeverageMode(marginCategory) || + enteringHighLeverage === true ) ); @@ -1570,7 +1574,7 @@ export class User { liquidationBuffer?: BN, includeOpenOrders?: boolean, strict = false, - enteringHighLeverage = false + enteringHighLeverage = undefined ): BN { return this.getActivePerpPositions().reduce( (totalPerpValue, perpPosition) => { @@ -1894,7 +1898,7 @@ export class User { perpMarketIndex: number, _marginCategory: MarginCategory = 'Initial', isLp = false, - enterHighLeverageMode = false + enterHighLeverageMode = undefined ): BN { const market = this.driftClient.getPerpMarketAccount(perpMarketIndex); const marketPrice = @@ -2245,7 +2249,7 @@ export class User { marginCategory: MarginCategory = 'Maintenance', includeOpenOrders = false, offsetCollateral = ZERO, - enteringHighLeverage = false + enteringHighLeverage = undefined ): BN { const totalCollateral = this.getTotalCollateral( marginCategory, @@ -2370,7 +2374,7 @@ export class User { positionBaseSizeChange: BN, estimatedEntryPrice: BN, includeOpenOrders: boolean, - enteringHighLeverage = false, + enteringHighLeverage = undefined, marginCategory: MarginCategory = 'Maintenance' ): BN { let freeCollateralChange = ZERO; @@ -2423,12 +2427,15 @@ export class User { ); } + const userCustomMargin = this.getUserAccount().maxMarginRatio; const marginRatio = calculateMarketMarginRatio( market, baseAssetAmount.abs(), marginCategory, - this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode(marginCategory) || enteringHighLeverage + enteringHighLeverage === false + ? Math.max(market.marginRatioInitial, userCustomMargin) + : userCustomMargin, + this.isHighLeverageMode(marginCategory) || enteringHighLeverage === true ); return liabilityValue.mul(new BN(marginRatio)).div(MARGIN_PRECISION); @@ -2457,7 +2464,7 @@ export class User { oraclePrice: BN, marginCategory: MarginCategory = 'Maintenance', includeOpenOrders = false, - enteringHighLeverage = false + enteringHighLeverage = undefined ): BN | undefined { const baseAssetAmount = includeOpenOrders ? calculateWorstCaseBaseAssetAmount(perpPosition, market, oraclePrice) @@ -2470,12 +2477,16 @@ export class User { const proposedBaseAssetAmount = baseAssetAmount.add(positionBaseSizeChange); + const userCustomMargin = this.getUserAccount().maxMarginRatio; + const marginRatio = calculateMarketMarginRatio( market, proposedBaseAssetAmount.abs(), marginCategory, - this.getUserAccount().maxMarginRatio, - this.isHighLeverageMode(marginCategory) || enteringHighLeverage + enteringHighLeverage === false + ? Math.max(market.marginRatioInitial, userCustomMargin) + : userCustomMargin, + this.isHighLeverageMode(marginCategory) || enteringHighLeverage === true ); const marginRatioQuotePrecision = new BN(marginRatio) @@ -2631,7 +2642,7 @@ export class User { targetMarketIndex: number, tradeSide: PositionDirection, isLp = false, - enterHighLeverageMode = false + enterHighLeverageMode = undefined ): { tradeSize: BN; oppositeSideTradeSize: BN } { let tradeSize = ZERO; let oppositeSideTradeSize = ZERO; From 370b158b95564e7da28713e3010d162e867e5e6c Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 00:32:22 +0000 Subject: [PATCH 059/216] sdk: release v2.131.0-beta.11 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index c876a695ca..d4a032442c 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.10 \ No newline at end of file +2.131.0-beta.11 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 9dcb853983..7e2b75e5aa 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.10", + "version": "2.131.0-beta.11", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 4045af1c574aac2d030bc8dfcfba047ef4d76a93 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Mon, 4 Aug 2025 16:49:07 +0800 Subject: [PATCH 060/216] refactor(sdk): allow user client input for cancel orders (#1797) --- sdk/src/driftClient.ts | 47 +++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index e13af3b15e..e1f1e09594 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -4357,14 +4357,24 @@ export class DriftClient { }); } + /** + * Sends a transaction to cancel the provided order ids. + * + * @param orderIds - The order ids to cancel. + * @param txParams - The transaction parameters. + * @param subAccountId - The sub account id to cancel the orders for. + * @param user - The user to cancel the orders for. If provided, it will be prioritized over the subAccountId. + * @returns The transaction signature. + */ public async cancelOrdersByIds( orderIds?: number[], txParams?: TxParams, - subAccountId?: number + subAccountId?: number, + user?: User ): Promise { const { txSig } = await this.sendTransaction( await this.buildTransaction( - await this.getCancelOrdersByIdsIx(orderIds, subAccountId), + await this.getCancelOrdersByIdsIx(orderIds, subAccountId, user), txParams ), [], @@ -4373,21 +4383,34 @@ export class DriftClient { return txSig; } + /** + * Returns the transaction instruction to cancel the provided order ids. + * + * @param orderIds - The order ids to cancel. + * @param subAccountId - The sub account id to cancel the orders for. + * @param user - The user to cancel the orders for. If provided, it will be prioritized over the subAccountId. + * @returns The transaction instruction to cancel the orders. + */ public async getCancelOrdersByIdsIx( orderIds?: number[], - subAccountId?: number + subAccountId?: number, + user?: User ): Promise { - const user = await this.getUserAccountPublicKey(subAccountId); + const userAccountPubKey = + user?.userAccountPublicKey ?? + (await this.getUserAccountPublicKey(subAccountId)); + const userAccount = + user?.getUserAccount() ?? this.getUserAccount(subAccountId); const remainingAccounts = this.getRemainingAccounts({ - userAccounts: [this.getUserAccount(subAccountId)], + userAccounts: [userAccount], useMarketLastSlotCache: true, }); return await this.program.instruction.cancelOrdersByIds(orderIds, { accounts: { state: await this.getStatePublicKey(), - user, + user: userAccountPubKey, authority: this.wallet.publicKey, }, remainingAccounts, @@ -6969,6 +6992,12 @@ export class DriftClient { return txSig; } + /** + * @param orderParams: The parameters for the order to modify. + * @param subAccountId: Optional - The subaccount ID of the user to modify the order for. + * @param userPublicKey: Optional - The public key of the user to modify the order for. This takes precedence over subAccountId. + * @returns + */ public async getModifyOrderIx( { orderId, @@ -7003,9 +7032,11 @@ export class DriftClient { maxTs?: BN; policy?: number; }, - subAccountId?: number + subAccountId?: number, + userPublicKey?: PublicKey ): Promise { - const user = await this.getUserAccountPublicKey(subAccountId); + const user = + userPublicKey ?? (await this.getUserAccountPublicKey(subAccountId)); const remainingAccounts = this.getRemainingAccounts({ userAccounts: [this.getUserAccount(subAccountId)], From a38e4cc45c46ba9212ac14bc726b414385cdba35 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Mon, 4 Aug 2025 16:51:32 +0800 Subject: [PATCH 061/216] refactor(sdk): minimize type for getPerpPositionValue --- sdk/src/math/margin.ts | 2 +- sdk/src/user.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/math/margin.ts b/sdk/src/math/margin.ts index cba701015e..63fada436b 100644 --- a/sdk/src/math/margin.ts +++ b/sdk/src/math/margin.ts @@ -126,7 +126,7 @@ export function calculateOraclePriceForPerpMargin( export function calculateBaseAssetValueWithOracle( market: PerpMarketAccount, perpPosition: PerpPosition, - oraclePriceData: OraclePriceData, + oraclePriceData: Pick, includeOpenOrders = false ): BN { let price = oraclePriceData.price; diff --git a/sdk/src/user.ts b/sdk/src/user.ts index ec474f29b0..abcf6481b3 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -1598,7 +1598,7 @@ export class User { */ public getPerpPositionValue( marketIndex: number, - oraclePriceData: OraclePriceData, + oraclePriceData: Pick, includeOpenOrders = false ): BN { const userPosition = From 5d6dc640c8485a37f4d71da650144ba1f367f4e5 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 08:55:36 +0000 Subject: [PATCH 062/216] sdk: release v2.131.0-beta.12 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index d4a032442c..a147ad0838 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.11 \ No newline at end of file +2.131.0-beta.12 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 7e2b75e5aa..7d7e26e7a7 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.11", + "version": "2.131.0-beta.12", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 7469e0d8b16f04ba7bd76d6197fc888152ca6db2 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 4 Aug 2025 10:06:06 -0400 Subject: [PATCH 063/216] program: relax fill validation for spot/swap (#1796) * program: relax fill validation for spot/swap * account for external fills * CHANGELOG --- CHANGELOG.md | 2 ++ programs/drift/src/controller/orders.rs | 6 ++++++ programs/drift/src/math/orders.rs | 17 +++++++++++++++++ programs/drift/src/math/orders/tests.rs | 8 ++++++++ programs/drift/src/math/spot_swap.rs | 5 ++++- 5 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aedc573dc..297962f7b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- program: less aggressive fill bands for spot swaps ([#1796](https://github.com/drift-labs/protocol-v2/pull/1796)) + ### Breaking ## [2.130.0] - 2025-07-29 diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 2ec096ce77..b357538286 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -1336,6 +1336,7 @@ pub fn fill_perp_order( .oracle_guard_rails .max_oracle_twap_5min_percent_divergence(), perp_market.is_prediction_market(), + None, )?; perp_market.last_fill_price = fill_price; @@ -4102,6 +4103,11 @@ pub fn fill_spot_order( .oracle_guard_rails .max_oracle_twap_5min_percent_divergence(), false, + if fulfillment_params.is_external() { + Some(order_direction) + } else { + None + }, )?; } diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 62eb8c135c..3628d7f231 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -491,6 +491,7 @@ pub fn validate_fill_price_within_price_bands( margin_ratio_initial: u32, oracle_twap_5min_percent_divergence: u64, is_prediction_market: bool, + direction: Option, ) -> DriftResult { if is_prediction_market { validate!( @@ -504,6 +505,22 @@ pub fn validate_fill_price_within_price_bands( return Ok(()); } + if let Some(direction) = direction { + if direction == PositionDirection::Long { + if fill_price < oracle_price.cast::()? + && fill_price < oracle_twap_5min.cast::()? + { + return Ok(()); + } + } else { + if fill_price > oracle_price.cast::()? + && fill_price > oracle_twap_5min.cast::()? + { + return Ok(()); + } + } + } + let max_oracle_diff = margin_ratio_initial.cast::()?; let max_oracle_twap_diff = oracle_twap_5min_percent_divergence.cast::()?; // 50% diff --git a/programs/drift/src/math/orders/tests.rs b/programs/drift/src/math/orders/tests.rs index a60b35a87d..112144451d 100644 --- a/programs/drift/src/math/orders/tests.rs +++ b/programs/drift/src/math/orders/tests.rs @@ -3177,6 +3177,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_ok()) } @@ -3195,6 +3196,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_ok()) } @@ -3214,6 +3216,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_err()) } @@ -3233,6 +3236,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_err()) } @@ -3252,6 +3256,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_err()) } @@ -3271,6 +3276,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_err()) } @@ -3290,6 +3296,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_err()) } @@ -3309,6 +3316,7 @@ pub mod validate_fill_price_within_price_bands { margin_ratio_initial, (PERCENTAGE_PRECISION / 2) as u64, false, + None, ) .is_err()) } diff --git a/programs/drift/src/math/spot_swap.rs b/programs/drift/src/math/spot_swap.rs index c08852d479..10bcc87e16 100644 --- a/programs/drift/src/math/spot_swap.rs +++ b/programs/drift/src/math/spot_swap.rs @@ -100,7 +100,7 @@ pub fn validate_price_bands_for_swap( out_price: i64, oracle_twap_5min_percent_divergence: u64, ) -> DriftResult { - let (fill_price, oracle_price, oracle_twap_5min, margin_ratio) = { + let (fill_price, direction, oracle_price, oracle_twap_5min, margin_ratio) = { let in_market_margin_ratio = in_market.get_margin_ratio(&MarginRequirementType::Initial)?; if in_market_margin_ratio != 0 { @@ -113,6 +113,7 @@ pub fn validate_price_bands_for_swap( ( fill_price, + PositionDirection::Short, in_price, in_market.historical_oracle_data.last_oracle_price_twap_5min, in_market_margin_ratio, @@ -123,6 +124,7 @@ pub fn validate_price_bands_for_swap( ( fill_price, + PositionDirection::Long, out_price, out_market .historical_oracle_data @@ -139,6 +141,7 @@ pub fn validate_price_bands_for_swap( margin_ratio, oracle_twap_5min_percent_divergence, false, + Some(direction), )?; Ok(()) From 9aa793e23521635100fb60358cb9db07b3d06c6b Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:04:12 -0400 Subject: [PATCH 064/216] program: update fee tier (#1792) * program: determine-fee-tier-update (reorg) * update user stats check logic * remove unnecessary clone * less mul operations * rm unwrap * update sdk * fix styling * use satsub * update changelog --- CHANGELOG.md | 1 + programs/drift/src/controller/orders.rs | 2 +- programs/drift/src/math/fees.rs | 117 +++++++++++------ programs/drift/src/math/fees/tests.rs | 161 ++++++++++++++++++++++++ sdk/src/user.ts | 56 ++++++--- 5 files changed, 278 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 297962f7b1..f0264c4bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Features +- program: update stake + volume fee tier determination ([#1792](https://github.com/drift-labs/protocol-v2/pull/1792)) ### Fixes diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index b357538286..84c2010c66 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2164,7 +2164,7 @@ pub fn fulfill_perp_order_with_amm( limit_price, override_fill_price, existing_base_asset_amount, - fee_tier, + &fee_tier, )?; let fill_price = if user.orders[order_index].post_only { diff --git a/programs/drift/src/math/fees.rs b/programs/drift/src/math/fees.rs index 8cc6643c90..6e1dc0a472 100644 --- a/programs/drift/src/math/fees.rs +++ b/programs/drift/src/math/fees.rs @@ -8,7 +8,7 @@ use crate::math::casting::Cast; use crate::math::constants::{ FIFTY_MILLION_QUOTE, FIVE_MILLION_QUOTE, ONE_HUNDRED_MILLION_QUOTE, ONE_HUNDRED_THOUSAND_QUOTE, ONE_MILLION_QUOTE, ONE_THOUSAND_QUOTE, TEN_BPS, TEN_MILLION_QUOTE, TEN_THOUSAND_QUOTE, - TWENTY_FIVE_THOUSAND_QUOTE, + TWENTY_FIVE_THOUSAND_QUOTE, TWO_HUNDRED_FIFTY_THOUSAND_QUOTE, }; use crate::math::helpers::get_proportion_u128; use crate::math::safe_math::SafeMath; @@ -55,7 +55,7 @@ pub fn calculate_fee_for_fulfillment_with_amm( // if there was a quote_asset_amount_surplus, the order was a maker order and fee_to_market comes from surplus if is_post_only { - let maker_rebate = calculate_maker_rebate(quote_asset_amount, fee_tier, fee_adjustment)?; + let maker_rebate = calculate_maker_rebate(quote_asset_amount, &fee_tier, fee_adjustment)?; let fee = quote_asset_amount_surplus .cast::()? @@ -94,7 +94,7 @@ pub fn calculate_fee_for_fulfillment_with_amm( referee_discount: 0, }) } else { - let mut fee = calculate_taker_fee(quote_asset_amount, fee_tier, fee_adjustment)?; + let mut fee = calculate_taker_fee(quote_asset_amount, &fee_tier, fee_adjustment)?; if user_high_leverage_mode { fee = fee.safe_mul(2)?; @@ -103,7 +103,7 @@ pub fn calculate_fee_for_fulfillment_with_amm( let (fee, referee_discount, referrer_reward) = if reward_referrer { calculate_referee_fee_and_referrer_reward( fee, - fee_tier, + &fee_tier, fee_structure.referrer_reward_epoch_upper_bound, referrer_stats, )? @@ -299,7 +299,7 @@ pub fn calculate_fee_for_fulfillment_with_match( determine_user_fee_tier(taker_stats, fee_structure, market_type, false)? }; - let mut taker_fee = calculate_taker_fee(quote_asset_amount, taker_fee_tier, fee_adjustment)?; + let mut taker_fee = calculate_taker_fee(quote_asset_amount, &taker_fee_tier, fee_adjustment)?; if user_high_leverage_mode { taker_fee = taker_fee.safe_mul(2)?; @@ -308,7 +308,7 @@ pub fn calculate_fee_for_fulfillment_with_match( let (taker_fee, referee_discount, referrer_reward) = if reward_referrer { calculate_referee_fee_and_referrer_reward( taker_fee, - taker_fee_tier, + &taker_fee_tier, fee_structure.referrer_reward_epoch_upper_bound, referrer_stats, )? @@ -316,7 +316,7 @@ pub fn calculate_fee_for_fulfillment_with_match( (taker_fee, 0, 0) }; - let maker_rebate = calculate_maker_rebate(quote_asset_amount, maker_fee_tier, fee_adjustment)?; + let maker_rebate = calculate_maker_rebate(quote_asset_amount, &maker_fee_tier, fee_adjustment)?; let filler_reward = if filler_multiplier == 0 { 0_u64 @@ -370,7 +370,7 @@ pub fn calculate_fee_for_fulfillment_with_external_market( let taker_fee_tier = determine_user_fee_tier(user_stats, fee_structure, &MarketType::Spot, false)?; - let fee = calculate_taker_fee(quote_asset_amount, taker_fee_tier, fee_adjustment)?; + let fee = calculate_taker_fee(quote_asset_amount, &taker_fee_tier, fee_adjustment)?; let fee_plus_referrer_rebate = external_market_fee.safe_add(unsettled_referrer_rebate)?; @@ -420,52 +420,91 @@ pub fn determine_user_fee_tier<'a>( fee_structure: &'a FeeStructure, market_type: &MarketType, user_high_leverage_mode: bool, -) -> DriftResult<&'a FeeTier> { +) -> DriftResult { match market_type { - MarketType::Perp if user_high_leverage_mode => Ok(&fee_structure.fee_tiers[0]), + MarketType::Perp if user_high_leverage_mode => Ok(fee_structure.fee_tiers[0]), MarketType::Perp => determine_perp_fee_tier(user_stats, fee_structure), - MarketType::Spot => determine_spot_fee_tier(user_stats, fee_structure), + MarketType::Spot => Ok(*determine_spot_fee_tier(user_stats, fee_structure)?), } } -fn determine_perp_fee_tier<'a>( +fn determine_perp_fee_tier( user_stats: &UserStats, - fee_structure: &'a FeeStructure, -) -> DriftResult<&'a FeeTier> { + fee_structure: &FeeStructure, +) -> DriftResult { let total_30d_volume = user_stats.get_total_30d_volume()?; let staked_gov_token_amount = user_stats.if_staked_gov_token_amount; - if total_30d_volume >= ONE_HUNDRED_MILLION_QUOTE - || staked_gov_token_amount >= ONE_HUNDRED_THOUSAND_QUOTE + 19_500 * QUOTE_PRECISION_U64 - { - return Ok(&fee_structure.fee_tiers[5]); - } - - if total_30d_volume >= FIFTY_MILLION_QUOTE - || staked_gov_token_amount >= ONE_HUNDRED_THOUSAND_QUOTE - QUOTE_PRECISION_U64 - { - return Ok(&fee_structure.fee_tiers[4]); - } - - if total_30d_volume >= TEN_MILLION_QUOTE - || staked_gov_token_amount >= TWENTY_FIVE_THOUSAND_QUOTE * 2 - QUOTE_PRECISION_U64 - { - return Ok(&fee_structure.fee_tiers[3]); + const TIER_LENGTH: usize = 5; + + const VOLUME_THRESHOLDS: [u64; TIER_LENGTH] = [ + ONE_MILLION_QUOTE * 2, + FIVE_MILLION_QUOTE * 2, + TEN_MILLION_QUOTE * 2, + FIFTY_MILLION_QUOTE * 2, + ONE_HUNDRED_MILLION_QUOTE * 2, + ]; + + const STAKE_THRESHOLDS: [u64; TIER_LENGTH] = [ + ONE_THOUSAND_QUOTE - QUOTE_PRECISION_U64, + TEN_THOUSAND_QUOTE - QUOTE_PRECISION_U64, + (TWENTY_FIVE_THOUSAND_QUOTE * 2) - QUOTE_PRECISION_U64, + ONE_HUNDRED_THOUSAND_QUOTE - QUOTE_PRECISION_U64, + TWO_HUNDRED_FIFTY_THOUSAND_QUOTE - QUOTE_PRECISION_U64 * 5, + ]; + + const STAKE_BENEFIT_FRAC: [u32; TIER_LENGTH + 1] = [0, 5, 10, 20, 30, 40]; + + let mut fee_tier_index = TIER_LENGTH; + for i in 0..TIER_LENGTH { + if total_30d_volume < VOLUME_THRESHOLDS[i] { + fee_tier_index = i; + break; + } } - if total_30d_volume >= FIVE_MILLION_QUOTE - || staked_gov_token_amount >= TEN_THOUSAND_QUOTE - QUOTE_PRECISION_U64 - { - return Ok(&fee_structure.fee_tiers[2]); + let mut stake_benefit_index = TIER_LENGTH; + for i in 0..TIER_LENGTH { + if staked_gov_token_amount < STAKE_THRESHOLDS[i] { + stake_benefit_index = i; + break; + } } - if total_30d_volume >= ONE_MILLION_QUOTE - || staked_gov_token_amount >= ONE_THOUSAND_QUOTE - QUOTE_PRECISION_U64 - { - return Ok(&fee_structure.fee_tiers[1]); + let stake_benefit = STAKE_BENEFIT_FRAC[stake_benefit_index]; + + let mut tier = fee_structure.fee_tiers[fee_tier_index]; + + if stake_benefit > 0 { + if let Some(div_scalar) = match stake_benefit { + 5 => Some(20), + 10 => Some(10), + 20 => Some(5), + _ => None, + } { + // Fast path for 5%, 10%, 20% using no mul + tier.fee_numerator = tier + .fee_numerator + .saturating_sub(tier.fee_numerator.safe_div_ceil(div_scalar)?); + + tier.maker_rebate_numerator = tier + .maker_rebate_numerator + .safe_add(tier.maker_rebate_numerator.safe_div(div_scalar)?)?; + } else { + // General path with mul/div + tier.fee_numerator = tier + .fee_numerator + .safe_mul(100_u32.saturating_sub(stake_benefit))? + .safe_div_ceil(100_u32)?; + + tier.maker_rebate_numerator = tier + .maker_rebate_numerator + .safe_mul(100_u32.saturating_add(stake_benefit))? + .safe_div(100_u32)?; + } } - Ok(&fee_structure.fee_tiers[0]) + Ok(tier) } fn determine_spot_fee_tier<'a>( diff --git a/programs/drift/src/math/fees/tests.rs b/programs/drift/src/math/fees/tests.rs index dd561f5125..00b1ef639d 100644 --- a/programs/drift/src/math/fees/tests.rs +++ b/programs/drift/src/math/fees/tests.rs @@ -917,3 +917,164 @@ mod calculate_fee_for_fulfillment_with_serum { assert_eq!(filler_reward, 2000); } } + +mod calcuate_fee_tiers { + + use crate::math::constants::QUOTE_PRECISION_U64; + use crate::math::constants::{ + FEE_DENOMINATOR, FEE_PERCENTAGE_DENOMINATOR, MAX_REFERRER_REWARD_EPOCH_UPPER_BOUND, + }; + use crate::math::fees::{determine_user_fee_tier, OrderFillerRewardStructure}; + use crate::state::state::{FeeStructure, FeeTier}; + use crate::state::user::MarketType; + use crate::state::user::UserStats; + + #[test] + fn test_calc_taker_tiers() { + let mut taker_stats = UserStats::default(); + let mut fee_tiers = [FeeTier::default(); 10]; + + fee_tiers[0] = FeeTier { + fee_numerator: 35, + fee_denominator: FEE_DENOMINATOR, // 3.5 bps + maker_rebate_numerator: 25, + maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps + referrer_reward_numerator: 10, + referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee + referee_fee_numerator: 5, + referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5% + }; + fee_tiers[1] = FeeTier { + fee_numerator: 30, + fee_denominator: FEE_DENOMINATOR, // 3 bps + maker_rebate_numerator: 25, + maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps + referrer_reward_numerator: 10, + referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee + referee_fee_numerator: 5, + referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5% + }; + fee_tiers[2] = FeeTier { + fee_numerator: 275, + fee_denominator: FEE_DENOMINATOR * 10, // 2.75 bps + maker_rebate_numerator: 25, + maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps + referrer_reward_numerator: 10, + referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee + referee_fee_numerator: 5, + referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5% + }; + fee_tiers[3] = FeeTier { + fee_numerator: 25, + fee_denominator: FEE_DENOMINATOR, // 2.5 bps + maker_rebate_numerator: 25, + maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps + referrer_reward_numerator: 10, + referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee + referee_fee_numerator: 5, + referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5% + }; + fee_tiers[4] = FeeTier { + fee_numerator: 225, + fee_denominator: FEE_DENOMINATOR * 10, // 2.25 bps + maker_rebate_numerator: 25, + maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps + referrer_reward_numerator: 10, + referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee + referee_fee_numerator: 5, + referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5% + }; + fee_tiers[5] = FeeTier { + fee_numerator: 20, + fee_denominator: FEE_DENOMINATOR, // 2 bps + maker_rebate_numerator: 25, + maker_rebate_denominator: FEE_DENOMINATOR * 10, // .25 bps + referrer_reward_numerator: 10, + referrer_reward_denominator: FEE_PERCENTAGE_DENOMINATOR, // 10% of taker fee + referee_fee_numerator: 5, + referee_fee_denominator: FEE_PERCENTAGE_DENOMINATOR, // 5% + }; + let fee_structure = FeeStructure { + fee_tiers, + filler_reward_structure: OrderFillerRewardStructure { + reward_numerator: 10, + reward_denominator: FEE_PERCENTAGE_DENOMINATOR, + time_based_reward_lower_bound: 10_000, // 1 cent + }, + flat_filler_fee: 10_000, + referrer_reward_epoch_upper_bound: MAX_REFERRER_REWARD_EPOCH_UPPER_BOUND, + }; + + let res = determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) + .unwrap(); + assert_eq!(res.fee_numerator, 35); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 25); + assert_eq!(res.maker_rebate_denominator, 1000000); + + taker_stats.taker_volume_30d = 80_000_000 * QUOTE_PRECISION_U64; + + let res: FeeTier = + determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) + .unwrap(); + assert_eq!(res.fee_numerator, 25); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 25); + assert_eq!(res.maker_rebate_denominator, 1000000); + + taker_stats.if_staked_gov_token_amount = 50_000 * QUOTE_PRECISION_U64 - 8970; // still counts for 50K tier + let res: FeeTier = + determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) + .unwrap(); + + assert_eq!(res.fee_numerator, 20); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 30); + assert_eq!(res.maker_rebate_denominator, 1000000); + + taker_stats.if_staked_gov_token_amount = 150_000 * QUOTE_PRECISION_U64 - 8970; // still counts for 100K tier + let res: FeeTier = + determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) + .unwrap(); + + assert_eq!(res.fee_numerator, 18); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 32); + assert_eq!(res.maker_rebate_denominator, 1000000); + + taker_stats.if_staked_gov_token_amount = 800_000 * QUOTE_PRECISION_U64; + let res: FeeTier = + determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) + .unwrap(); + + assert_eq!(res.fee_numerator, 15); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 35); + assert_eq!(res.maker_rebate_denominator, 1000000); + + taker_stats.taker_volume_30d = 280_000_000 * QUOTE_PRECISION_U64; + let res: FeeTier = + determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) + .unwrap(); + + assert_eq!(res.fee_numerator, 12); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 35); + assert_eq!(res.maker_rebate_denominator, 1000000); + + let res: FeeTier = + determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, true).unwrap(); + + assert_eq!(res.fee_numerator, 35); + assert_eq!(res.fee_denominator, 100000); + + assert_eq!(res.maker_rebate_numerator, 25); + assert_eq!(res.maker_rebate_denominator, 1000000); + } +} diff --git a/sdk/src/user.ts b/sdk/src/user.ts index abcf6481b3..7abbe2755d 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -3509,34 +3509,52 @@ export class User { userStatsAccount, now ); - const stakedGovAssetAmount = userStatsAccount.ifStakedGovTokenAmount; - const volumeTiers = [ - new BN(100_000_000).mul(QUOTE_PRECISION), - new BN(50_000_000).mul(QUOTE_PRECISION), + + const volumeThresholds = [ + new BN(2_000_000).mul(QUOTE_PRECISION), new BN(10_000_000).mul(QUOTE_PRECISION), - new BN(5_000_000).mul(QUOTE_PRECISION), - new BN(1_000_000).mul(QUOTE_PRECISION), + new BN(20_000_000).mul(QUOTE_PRECISION), + new BN(100_000_000).mul(QUOTE_PRECISION), + new BN(200_000_000).mul(QUOTE_PRECISION), ]; - const stakedTiers = [ - new BN(120000 - 1).mul(QUOTE_PRECISION), - new BN(100000 - 1).mul(QUOTE_PRECISION), - new BN(25000 - 1).mul(QUOTE_PRECISION), - new BN(10000 - 1).mul(QUOTE_PRECISION), - new BN(1000 - 1).mul(QUOTE_PRECISION), + const stakeThresholds = [ + new BN(1_000 - 1).mul(QUOTE_PRECISION), + new BN(10_000 - 1).mul(QUOTE_PRECISION), + new BN(50_000 - 1).mul(QUOTE_PRECISION), + new BN(100_000 - 1).mul(QUOTE_PRECISION), + new BN(250_000 - 5).mul(QUOTE_PRECISION), ]; + const stakeBenefitFrac = [0, 5, 10, 20, 30, 40]; - for (let i = 0; i < volumeTiers.length; i++) { - if ( - total30dVolume.gte(volumeTiers[i]) || - stakedGovAssetAmount.gte(stakedTiers[i]) - ) { - feeTierIndex = 5 - i; + let feeTierIndex = 5; + for (let i = 0; i < volumeThresholds.length; i++) { + if (total30dVolume.lt(volumeThresholds[i])) { + feeTierIndex = i; break; } } - return state.perpFeeStructure.feeTiers[feeTierIndex]; + let stakeBenefitIndex = 5; + for (let i = 0; i < stakeThresholds.length; i++) { + if (stakedGovAssetAmount.lt(stakeThresholds[i])) { + stakeBenefitIndex = i; + break; + } + } + + const stakeBenefit = stakeBenefitFrac[stakeBenefitIndex]; + + let tier = { ...state.perpFeeStructure.feeTiers[feeTierIndex] }; + + if (stakeBenefit > 0) { + tier.feeNumerator = (tier.feeNumerator * (100 - stakeBenefit)) / 100; + + tier.makerRebateNumerator = + (tier.makerRebateNumerator * (100 + stakeBenefit)) / 100; + } + + return tier; } return state.spotFeeStructure.feeTiers[feeTierIndex]; From 9a090c178d3a24e3402115e622e65dd9824276ae Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 4 Aug 2025 11:05:15 -0400 Subject: [PATCH 065/216] sdk lint --- sdk/src/user.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 7abbe2755d..8224b110ad 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -3495,7 +3495,7 @@ export class User { public getUserFeeTier(marketType: MarketType, now?: BN) { const state = this.driftClient.getStateAccount(); - let feeTierIndex = 0; + const feeTierIndex = 0; if (isVariant(marketType, 'perp')) { if (this.isHighLeverageMode('Initial')) { return state.perpFeeStructure.feeTiers[0]; @@ -3545,7 +3545,7 @@ export class User { const stakeBenefit = stakeBenefitFrac[stakeBenefitIndex]; - let tier = { ...state.perpFeeStructure.feeTiers[feeTierIndex] }; + const tier = { ...state.perpFeeStructure.feeTiers[feeTierIndex] }; if (stakeBenefit > 0) { tier.feeNumerator = (tier.feeNumerator * (100 - stakeBenefit)) / 100; From 8706140f49bc8d11114806642b3002312aad7703 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:10:29 +0000 Subject: [PATCH 066/216] sdk: release v2.131.0-beta.13 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index a147ad0838..eb55250fa9 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.12 \ No newline at end of file +2.131.0-beta.13 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 7d7e26e7a7..7fec37ebda 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.12", + "version": "2.131.0-beta.13", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From f88a22ee9534cdc1e42669ca00d0c5cb692ed64e Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 4 Aug 2025 08:40:09 -0700 Subject: [PATCH 067/216] v2.131.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 4 ++-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0264c4bcf..428e9358a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Features + +### Fixes + +### Breaking + +## [2.131.0] - 2025-08-04 + ### Features - program: update stake + volume fee tier determination ([#1792](https://github.com/drift-labs/protocol-v2/pull/1792)) diff --git a/Cargo.lock b/Cargo.lock index bacc94f31c..471e15b39e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.130.0" +version = "2.131.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 92ad4d488d..e75ba797f1 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.130.0" +version = "2.131.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index 7fec37ebda..48b2ee45b0 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0-beta.13", + "version": "2.131.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 16ff48591f..871c55361f 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.130.0", + "version": "2.131.0", "name": "drift", "instructions": [ { @@ -16077,4 +16077,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} \ No newline at end of file +} From 3c8dffa4c642a70e890efa28d423d7db68eaa19f Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:54:10 +0000 Subject: [PATCH 068/216] sdk: release v2.132.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index eb55250fa9..60c49dda37 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.131.0-beta.13 \ No newline at end of file +2.132.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 48b2ee45b0..b5a6d11363 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.0", + "version": "2.132.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 1901fc7cc35504d1a4c00ff19f72303946f0dde2 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:35:07 -0400 Subject: [PATCH 069/216] program: update-fee-tier-validates (#1798) * program: update-fee-tier-validates * inc max --- programs/drift/src/validation/fee_structure.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/drift/src/validation/fee_structure.rs b/programs/drift/src/validation/fee_structure.rs index beca1209bb..c44ff3fd0f 100644 --- a/programs/drift/src/validation/fee_structure.rs +++ b/programs/drift/src/validation/fee_structure.rs @@ -45,7 +45,7 @@ pub fn validate_fee_tier( fee_tier: &FeeTier, filler_reward_numerator: u32, ) -> DriftResult { - let fee_valid = fee_tier.fee_numerator <= 100 && fee_tier.fee_denominator == FEE_DENOMINATOR; // <= 10bps + let fee_valid = fee_tier.fee_numerator <= 300 && fee_tier.fee_denominator >= FEE_DENOMINATOR; // <= 30bps validate!( fee_valid, @@ -56,7 +56,7 @@ pub fn validate_fee_tier( )?; let maker_rebate_valid = fee_tier.maker_rebate_numerator <= 30 - && fee_tier.maker_rebate_denominator == FEE_DENOMINATOR; // <= 3bps + && fee_tier.maker_rebate_denominator >= FEE_DENOMINATOR; // <= 3bps validate!( maker_rebate_valid, From 2e3cfc9c2cba5c945b0c0786375eb2a723473259 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 4 Aug 2025 09:45:49 -0700 Subject: [PATCH 070/216] v2.131.1 --- CHANGELOG.md | 9 +++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 428e9358a2..1467837d14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking +## [2.131.1] - 2025-08-04 + +### Features + +### Fixes +- program: update-fee-tier-validates ([#1798](https://github.com/drift-labs/protocol-v2/pull/1798)) + +### Breaking + ## [2.131.0] - 2025-08-04 ### Features diff --git a/Cargo.lock b/Cargo.lock index 471e15b39e..cc292536f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.131.0" +version = "2.131.1" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index e75ba797f1..8d6c841602 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.131.0" +version = "2.131.1" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index b5a6d11363..82d8f3ac22 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.0", + "version": "2.131.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 871c55361f..d91c595b0e 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.131.0", + "version": "2.131.1", "name": "drift", "instructions": [ { From 38061968c6fd920b4698e50d53a88571bb397ad5 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:54:34 +0000 Subject: [PATCH 071/216] sdk: release v2.132.0-beta.0 --- sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/package.json b/sdk/package.json index 82d8f3ac22..b5a6d11363 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.131.1", + "version": "2.132.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From fd78db00c3810eddcbe30ca248aff63b7761f5b0 Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Tue, 5 Aug 2025 13:24:52 -0400 Subject: [PATCH 072/216] sdk: add missing param for updatePerpMarketAmmSpreadAdjustment --- sdk/src/adminClient.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index 7c59de7afb..9b90cc1425 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -4052,13 +4052,15 @@ export class AdminClient extends DriftClient { public async updatePerpMarketAmmSpreadAdjustment( perpMarketIndex: number, ammSpreadAdjustment: number, - ammInventorySpreadAdjustment: number + ammInventorySpreadAdjustment: number, + referencePriceOffset: number ): Promise { const updatePerpMarketAmmSpreadAdjustmentIx = await this.getUpdatePerpMarketAmmSpreadAdjustmentIx( perpMarketIndex, ammSpreadAdjustment, - ammInventorySpreadAdjustment + ammInventorySpreadAdjustment, + referencePriceOffset ); const tx = await this.buildTransaction( updatePerpMarketAmmSpreadAdjustmentIx @@ -4071,7 +4073,8 @@ export class AdminClient extends DriftClient { public async getUpdatePerpMarketAmmSpreadAdjustmentIx( perpMarketIndex: number, ammSpreadAdjustment: number, - ammInventorySpreadAdjustment: number + ammInventorySpreadAdjustment: number, + referencePriceOffset: number ): Promise { const perpMarketPublicKey = await getPerpMarketPublicKey( this.program.programId, @@ -4081,6 +4084,7 @@ export class AdminClient extends DriftClient { return await this.program.instruction.updatePerpMarketAmmSpreadAdjustment( ammSpreadAdjustment, ammInventorySpreadAdjustment, + referencePriceOffset, { accounts: { admin: this.useHotWalletAdmin From 1762890f8c9d85ab3b557a15cbee55171619dcdc Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:30:30 +0000 Subject: [PATCH 073/216] sdk: release v2.132.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 60c49dda37..c834aabfce 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.132.0-beta.0 \ No newline at end of file +2.132.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index b5a6d11363..24ed53f0f8 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.0", + "version": "2.132.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From d69f73e019f64b950e734e76c32eef3154abdeeb Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Tue, 5 Aug 2025 13:54:17 -0400 Subject: [PATCH 074/216] sdk: fix consts in calculateWithdrawLimit --- sdk/src/math/spotBalance.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/src/math/spotBalance.ts b/sdk/src/math/spotBalance.ts index 62044098b9..32d0de80d3 100644 --- a/sdk/src/math/spotBalance.ts +++ b/sdk/src/math/spotBalance.ts @@ -588,10 +588,10 @@ export function calculateWithdrawLimit( spotMarket.withdrawGuardThreshold, BN.min( BN.max( - marketDepositTokenAmount.div(new BN(6)), - borrowTokenTwapLive.add(lesserDepositAmount.div(new BN(10))) + marketDepositTokenAmount.div(new BN(3)), + borrowTokenTwapLive.add(lesserDepositAmount.div(new BN(7))) ), - lesserDepositAmount.sub(lesserDepositAmount.div(new BN(5))) + lesserDepositAmount.sub(lesserDepositAmount.div(new BN(8))) ) ); // main pool between ~15-80% utilization with 10% friction on twap } else { From 1904d0f9b8be551616c7fc1e9e1eb5913d0df568 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 18:00:09 +0000 Subject: [PATCH 075/216] sdk: release v2.132.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index c834aabfce..5d2374f644 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.132.0-beta.1 \ No newline at end of file +2.132.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 24ed53f0f8..636a9237d0 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.1", + "version": "2.132.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 8f42aadcb3db73003033db1cf283f6b79cd1d761 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 5 Aug 2025 15:44:29 -0400 Subject: [PATCH 076/216] sdk: rm legacy code for updateAmms --- sdk/src/driftClient.ts | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index e1f1e09594..305ffa0155 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -4166,25 +4166,20 @@ export class DriftClient { public async getUpdateAMMsIx( marketIndexes: number[] ): Promise { - for (let i = marketIndexes.length; i < 5; i++) { - marketIndexes.push(100); - } const marketAccountInfos = []; const oracleAccountInfos = []; for (const marketIndex of marketIndexes) { - if (marketIndex !== 100) { - const market = this.getPerpMarketAccount(marketIndex); - marketAccountInfos.push({ - pubkey: market.pubkey, - isWritable: true, - isSigner: false, - }); - oracleAccountInfos.push({ - pubkey: market.amm.oracle, - isWritable: false, - isSigner: false, - }); - } + const market = this.getPerpMarketAccount(marketIndex); + marketAccountInfos.push({ + pubkey: market.pubkey, + isWritable: true, + isSigner: false, + }); + oracleAccountInfos.push({ + pubkey: market.amm.oracle, + isWritable: false, + isSigner: false, + }); } const remainingAccounts = oracleAccountInfos.concat(marketAccountInfos); From a4e255b3537429f805096c457fdf9944d655ea5c Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:49:39 +0000 Subject: [PATCH 077/216] sdk: release v2.132.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 5d2374f644..a6ac879051 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.132.0-beta.2 \ No newline at end of file +2.132.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 636a9237d0..85a076df6c 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.2", + "version": "2.132.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 68c0b3999dd9b07f97cc407c8a86396b5943e941 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Wed, 6 Aug 2025 15:44:59 +0800 Subject: [PATCH 078/216] refactor(sdk): refine oracle price data input types --- sdk/src/math/market.ts | 6 +++--- sdk/src/math/position.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index 26857777c6..78f95048ae 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -186,7 +186,7 @@ export function calculateUnrealizedAssetWeight( quoteSpotMarket: SpotMarketAccount, unrealizedPnl: BN, marginCategory: MarginCategory, - oraclePriceData: OraclePriceData + oraclePriceData: Pick ): BN { let assetWeight: BN; switch (marginCategory) { @@ -252,7 +252,7 @@ export function calculateMarketMaxAvailableInsurance( export function calculateNetUserPnl( perpMarket: PerpMarketAccount, - oraclePriceData: OraclePriceData + oraclePriceData: Pick ): BN { const netUserPositionValue = perpMarket.amm.baseAssetAmountWithAmm .add(perpMarket.amm.baseAssetAmountWithUnsettledLp) @@ -272,7 +272,7 @@ export function calculateNetUserPnl( export function calculateNetUserPnlImbalance( perpMarket: PerpMarketAccount, spotMarket: SpotMarketAccount, - oraclePriceData: OraclePriceData, + oraclePriceData: Pick, applyFeePoolDiscount = true ): BN { const netUserPnl = calculateNetUserPnl(perpMarket, oraclePriceData); diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index 52b3a888bd..c64b86d73f 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -103,7 +103,7 @@ export function calculatePositionPNL( market: PerpMarketAccount, perpPosition: PerpPosition, withFunding = false, - oraclePriceData: OraclePriceData + oraclePriceData: Pick ): BN { if (perpPosition.baseAssetAmount.eq(ZERO)) { return perpPosition.quoteAssetAmount; @@ -135,7 +135,7 @@ export function calculateClaimablePnl( market: PerpMarketAccount, spotMarket: SpotMarketAccount, perpPosition: PerpPosition, - oraclePriceData: OraclePriceData + oraclePriceData: Pick ): BN { const unrealizedPnl = calculatePositionPNL( market, From 9e351faa31733d034a116d4dd98a9da1a486f0c4 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 07:49:36 +0000 Subject: [PATCH 079/216] sdk: release v2.132.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index a6ac879051..43dc3e51dc 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.132.0-beta.3 \ No newline at end of file +2.132.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 85a076df6c..b85ec8b161 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.3", + "version": "2.132.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From bcbac3024950b99523fede3363bb8efc5565319c Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:13:58 -0400 Subject: [PATCH 080/216] program: update-max-borrow-token-amount-deltas (#1801) * program: update-max-borrow-token-amount-deltas * fix tests * sdk match * update changelog --- CHANGELOG.md | 4 ++++ .../src/controller/spot_balance/tests.rs | 20 +++++++++---------- programs/drift/src/math/spot_withdraw.rs | 10 +++++----- sdk/src/math/spotBalance.ts | 6 +++--- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1467837d14..c5612e3c95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: update max borrow delta/utilization thresholds ([#1760](https://github.com/drift-labs/protocol-v2/pull/1801)) + ### Fixes ### Breaking @@ -18,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features ### Fixes + - program: update-fee-tier-validates ([#1798](https://github.com/drift-labs/protocol-v2/pull/1798)) ### Breaking @@ -25,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.131.0] - 2025-08-04 ### Features + - program: update stake + volume fee tier determination ([#1792](https://github.com/drift-labs/protocol-v2/pull/1792)) ### Fixes diff --git a/programs/drift/src/controller/spot_balance/tests.rs b/programs/drift/src/controller/spot_balance/tests.rs index 291e3d6516..1bece9f257 100644 --- a/programs/drift/src/controller/spot_balance/tests.rs +++ b/programs/drift/src/controller/spot_balance/tests.rs @@ -489,7 +489,7 @@ fn test_check_withdraw_limits() { 0, ) .unwrap(); - assert_eq!(mbt, 642857); + assert_eq!(mbt, 700000); let valid_withdraw = check_withdraw_limits(&spot_market, Some(&user), Some(0)).unwrap(); assert!(valid_withdraw); @@ -578,7 +578,7 @@ fn test_check_withdraw_limits_below_optimal_utilization() { assert_eq!(mdt_dep, 153000000000000); assert_eq!(max_bor, 142800000000000); - assert_eq!(mbt_bor, 151342857142857); + assert_eq!(mbt_bor, 163000000000000); let valid_withdraw = check_withdraw_limits(&sol_spot_market, None, None).unwrap(); assert_eq!(valid_withdraw, true); @@ -626,17 +626,17 @@ fn test_check_withdraw_limits_above_optimal_utilization() { initial_liability_weight: 12 * SPOT_WEIGHT_PRECISION / 10, maintenance_liability_weight: 11 * SPOT_WEIGHT_PRECISION / 10, deposit_balance: 200_000 * SPOT_BALANCE_PRECISION, // 200k sol - borrow_balance: 155_000 * SPOT_BALANCE_PRECISION, + borrow_balance: 160_000 * SPOT_BALANCE_PRECISION, liquidator_fee: LIQUIDATION_FEE_PRECISION / 1000, deposit_token_twap: 204000000000000_u64, - borrow_token_twap: 192200000000000_u64, - utilization_twap: 890000, // 89% + borrow_token_twap: 199200000000000_u64, + utilization_twap: 929000, // 92.9% status: MarketStatus::Active, ..SpotMarket::default() }; - assert_eq!(sol_spot_market.get_utilization().unwrap(), 928480); + assert_eq!(sol_spot_market.get_utilization().unwrap(), 958431); assert!( sol_spot_market.get_utilization().unwrap() > sol_spot_market.optimal_utilization as u128 ); // below optimal util @@ -673,14 +673,14 @@ fn test_check_withdraw_limits_above_optimal_utilization() { .unwrap(); assert_eq!(deposit_tokens_1, 204000000000000); - assert_eq!(borrow_tokens_1, 189410000000000); + assert_eq!(borrow_tokens_1, 195520000000000); // utilization bands differ from others - assert_eq!(min_dep, 200433862433862); + assert_eq!(min_dep, 202716433385173); assert_eq!(mdt_dep, 153000000000000); - assert_eq!(max_bor, 192780000000000); - assert_eq!(mbt_bor, 178500000000000); + assert_eq!(max_bor, 196758000000000); + assert_eq!(mbt_bor, 189428571428572); // without passing a user, since borrows are above the built in limit of 80% will fail let valid_withdraw = check_withdraw_limits(&sol_spot_market, None, None).unwrap(); diff --git a/programs/drift/src/math/spot_withdraw.rs b/programs/drift/src/math/spot_withdraw.rs index 4e82989e9f..9f0b3e0b5a 100644 --- a/programs/drift/src/math/spot_withdraw.rs +++ b/programs/drift/src/math/spot_withdraw.rs @@ -39,22 +39,22 @@ pub fn calculate_max_borrow_token_amount( let lesser_deposit_amount = deposit_token_amount.min(deposit_token_twap); let max_borrow_token = if pool_id == 0 { - // main pool between ~30-87.5% utilization with friction on twap in 14% increments + // main pool between ~30-92.5% utilization with friction on twap in 20% increments withdraw_guard_threshold .max( (lesser_deposit_amount / 3) - .max(borrow_token_twap.safe_add(lesser_deposit_amount / 7)?) - .min(lesser_deposit_amount.safe_sub(lesser_deposit_amount / 8)?), + .max(borrow_token_twap.safe_add(lesser_deposit_amount / 5)?) + .min(lesser_deposit_amount.safe_sub(lesser_deposit_amount / 14)?), ) .min(max_token_borrows) } else { - // isolated pools between 50-90% utilization with friction on twap in 33% increments + // isolated pools between 50-95% utilization with friction on twap in 33% increments withdraw_guard_threshold .max( (lesser_deposit_amount / 2) .max(borrow_token_twap.safe_add(lesser_deposit_amount / 3)?) - .min(lesser_deposit_amount.safe_sub(lesser_deposit_amount / 10)?), + .min(lesser_deposit_amount.safe_sub(lesser_deposit_amount / 20)?), ) .min(max_token_borrows) }; diff --git a/sdk/src/math/spotBalance.ts b/sdk/src/math/spotBalance.ts index 32d0de80d3..cd6bb053d7 100644 --- a/sdk/src/math/spotBalance.ts +++ b/sdk/src/math/spotBalance.ts @@ -593,7 +593,7 @@ export function calculateWithdrawLimit( ), lesserDepositAmount.sub(lesserDepositAmount.div(new BN(8))) ) - ); // main pool between ~15-80% utilization with 10% friction on twap + ); // main pool between ~30-92.5% utilization with friction on twap in 20% increments } else { maxBorrowTokensTwap = BN.max( spotMarket.withdrawGuardThreshold, @@ -602,9 +602,9 @@ export function calculateWithdrawLimit( marketDepositTokenAmount.div(new BN(2)), borrowTokenTwapLive.add(lesserDepositAmount.div(new BN(3))) ), - lesserDepositAmount.sub(lesserDepositAmount.div(new BN(10))) + lesserDepositAmount.sub(lesserDepositAmount.div(new BN(20))) ) - ); // isolated pool between ~50-90% utilization with 33% friction on twap + ); // isolated pools between 50-95% utilization with friction on twap in 33% increments } const minDepositTokensTwap = depositTokenTwapLive.sub( From 4e9a43d17409e067fda31cef424f3dcb36c6125b Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:19:44 +0000 Subject: [PATCH 081/216] sdk: release v2.132.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 43dc3e51dc..8795d06677 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.132.0-beta.4 \ No newline at end of file +2.132.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index b85ec8b161..3da6600d08 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.4", + "version": "2.132.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 83956060ced44dbff05a04bddcf5e728a8fcbf18 Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 6 Aug 2025 09:20:13 -0700 Subject: [PATCH 082/216] v2.132.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5612e3c95..e3da98c705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +### Fixes + +### Breaking + +## [2.132.0] - 2025-08-06 + +### Features + - program: update max borrow delta/utilization thresholds ([#1760](https://github.com/drift-labs/protocol-v2/pull/1801)) ### Fixes diff --git a/Cargo.lock b/Cargo.lock index cc292536f7..0f03e84fa0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.131.1" +version = "2.132.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 8d6c841602..b70381cf82 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.131.1" +version = "2.132.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index 3da6600d08..ba245f7cca 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0-beta.5", + "version": "2.132.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index d91c595b0e..2b6f9bbcea 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.131.1", + "version": "2.132.0", "name": "drift", "instructions": [ { From a789820ae406ef54583a9afb3213e9b0c7edd491 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:29:06 +0000 Subject: [PATCH 083/216] sdk: release v2.133.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8795d06677..59368a1a0b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.132.0-beta.5 \ No newline at end of file +2.133.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index ba245f7cca..add884e9d7 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.132.0", + "version": "2.133.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 0310d21336fc17be73cb4364b5114ddb6329ad9c Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Fri, 8 Aug 2025 10:26:29 -0400 Subject: [PATCH 084/216] sdk: fix modify order post only --- sdk/src/driftClient.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 305ffa0155..29bf9647df 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -64,6 +64,7 @@ import { ProtectedMakerModeConfig, SignedMsgOrderParamsDelegateMessage, TokenProgramFlag, + PostOnlyParams, } from './types'; import driftIDL from './idl/drift.json'; @@ -6968,7 +6969,7 @@ export class DriftClient { auctionStartPrice?: BN; auctionEndPrice?: BN; reduceOnly?: boolean; - postOnly?: boolean; + postOnly?: PostOnlyParams; bitFlags?: number; maxTs?: BN; policy?: number; @@ -7022,7 +7023,7 @@ export class DriftClient { auctionStartPrice?: BN; auctionEndPrice?: BN; reduceOnly?: boolean; - postOnly?: boolean; + postOnly?: PostOnlyParams; bitFlags?: number; maxTs?: BN; policy?: number; @@ -7097,7 +7098,7 @@ export class DriftClient { auctionStartPrice?: BN; auctionEndPrice?: BN; reduceOnly?: boolean; - postOnly?: boolean; + postOnly?: PostOnlyParams; bitFlags?: number; policy?: ModifyOrderPolicy; maxTs?: BN; @@ -7145,7 +7146,7 @@ export class DriftClient { auctionStartPrice?: BN; auctionEndPrice?: BN; reduceOnly?: boolean; - postOnly?: boolean; + postOnly?: PostOnlyParams; bitFlags?: number; policy?: ModifyOrderPolicy; maxTs?: BN; From b70979ad90317c477bfd681a8f73d2b30aeb022b Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:31:43 +0000 Subject: [PATCH 085/216] sdk: release v2.133.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 59368a1a0b..bc2d37d5c9 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.133.0-beta.0 \ No newline at end of file +2.133.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index add884e9d7..8d4e5f98a4 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.133.0-beta.0", + "version": "2.133.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 9840e462fb3dd920837f732ddae364acc9a55798 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:52:29 -0400 Subject: [PATCH 086/216] program: amm-spread-inventory-spread-adj-base-spread-lb (#1803) --- programs/drift/src/controller/position/tests.rs | 17 +++++++++-------- programs/drift/src/math/amm_spread.rs | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index b1c9926051..71d7520efc 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -940,10 +940,10 @@ fn amm_ref_price_offset_decay_logic() { assert_eq!( sspreads, [ - 7147, 6937, 6727, 6517, 6307, 6097, 6077, 6057, 6037, 6017, 5807, 5596, 5386, 5176, - 4966, 4756, 4546, 4336, 4126, 3916, 3706, 3496, 3286, 3076, 2866, 2656, 2446, 2236, - 2026, 1816, 1626, 1455, 1302, 1164, 1040, 928, 827, 736, 654, 581, 515, 456, 402, 354, - 311, 272, 237, 205, 177, 151, 128, 107, 87, 67, 47, 27, 7, 6, 6, 6 + 7150, 6940, 6730, 6520, 6310, 6100, 6080, 6060, 6040, 6020, 5810, 5600, 5390, 5180, + 4970, 4760, 4550, 4340, 4130, 3920, 3710, 3500, 3290, 3080, 2870, 2660, 2450, 2240, + 2030, 1820, 1630, 1459, 1306, 1168, 1044, 932, 831, 740, 658, 585, 519, 460, 406, 358, + 315, 276, 241, 209, 181, 155, 132, 111, 91, 71, 51, 31, 11, 10, 10, 10 ] ); } @@ -1108,10 +1108,11 @@ fn amm_negative_ref_price_offset_decay_logic() { assert_eq!( sspreads, [ - 207, 207, 207, 207, 207, 207, 17, 17, 17, 17, 207, 206, 206, 206, 206, 206, 206, 206, - 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, - 206, 206, 206, 126, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 + 210, 210, 210, 210, 210, 210, 20, 20, 20, 20, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 130, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10 ] ); assert_eq!( diff --git a/programs/drift/src/math/amm_spread.rs b/programs/drift/src/math/amm_spread.rs index 5953b04d0f..384831d198 100644 --- a/programs/drift/src/math/amm_spread.rs +++ b/programs/drift/src/math/amm_spread.rs @@ -458,24 +458,24 @@ pub fn calculate_spread( let adjustment: u64 = amm_inventory_spread_adjustment .cast::()? .unsigned_abs(); - long_spread = long_vol_spread.max( + long_spread = max(half_base_spread_u64, long_vol_spread).max( long_spread .saturating_sub(long_spread.saturating_mul(adjustment).safe_div(100)?) .max(1), ); - short_spread = short_vol_spread.max( + short_spread = max(half_base_spread_u64, short_vol_spread).max( short_spread .saturating_sub(short_spread.saturating_mul(adjustment).safe_div(100)?) .max(1), ); } else if amm_inventory_spread_adjustment > 0 { let adjustment = amm_inventory_spread_adjustment.cast()?; - long_spread = long_vol_spread.max( + long_spread = max(half_base_spread_u64, long_vol_spread).max( long_spread .saturating_add(long_spread.saturating_mul(adjustment).safe_div_ceil(100)?) .max(1), ); - short_spread = short_vol_spread.max( + short_spread = max(half_base_spread_u64, short_vol_spread).max( short_spread .saturating_add(short_spread.saturating_mul(adjustment).safe_div_ceil(100)?) .max(1), From 06a3e9dc2ae305d6e99adb0fd3293ec19c7ee3a8 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 11 Aug 2025 21:07:33 -0400 Subject: [PATCH 087/216] program: always fail settle pnl even if try_settle used (#1809) * program: always fail settle pnl even if try_settle used * CHANGELOG * disable test --------- Co-authored-by: wphan --- CHANGELOG.md | 2 ++ programs/drift/src/state/settle_pnl_mode.rs | 2 +- programs/drift/src/state/settle_pnl_mode/tests.rs | 12 ++++++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3da98c705..5c67236472 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking +- program: have TRY_SETTLE pnl mode fail ([#1809](https://github.com/drift-labs/protocol-v2/pull/1809)) + ## [2.132.0] - 2025-08-06 ### Features diff --git a/programs/drift/src/state/settle_pnl_mode.rs b/programs/drift/src/state/settle_pnl_mode.rs index f13002d7db..346506da4d 100644 --- a/programs/drift/src/state/settle_pnl_mode.rs +++ b/programs/drift/src/state/settle_pnl_mode.rs @@ -27,7 +27,7 @@ impl SettlePnlMode { ); match self { SettlePnlMode::MustSettle => Err(error_code), - SettlePnlMode::TrySettle => Ok(()), + SettlePnlMode::TrySettle => Err(error_code), } } } diff --git a/programs/drift/src/state/settle_pnl_mode/tests.rs b/programs/drift/src/state/settle_pnl_mode/tests.rs index a53c2b5bed..58bb96a8e8 100644 --- a/programs/drift/src/state/settle_pnl_mode/tests.rs +++ b/programs/drift/src/state/settle_pnl_mode/tests.rs @@ -9,10 +9,10 @@ mod test { assert_eq!(result, Err(ErrorCode::DefaultError)); } - #[test] - fn test_try_settle_returns_ok() { - let mode = SettlePnlMode::TrySettle; - let result = mode.result(ErrorCode::DefaultError, 0, "Try settle error"); - assert_eq!(result, Ok(())); - } + // #[test] + // fn test_try_settle_returns_ok() { + // let mode = SettlePnlMode::TrySettle; + // let result = mode.result(ErrorCode::DefaultError, 0, "Try settle error"); + // assert_eq!(result, Ok(())); + // } } From ce9a159e1a3337d3fd4ddd20bc4f5baea809d08e Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 11 Aug 2025 18:28:49 -0700 Subject: [PATCH 088/216] v2.133.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c67236472..1ea9deb95f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking +## [2.133.0] - 2025-08-11 + +### Features + +### Fixes + +### Breaking + - program: have TRY_SETTLE pnl mode fail ([#1809](https://github.com/drift-labs/protocol-v2/pull/1809)) ## [2.132.0] - 2025-08-06 diff --git a/Cargo.lock b/Cargo.lock index 0f03e84fa0..23ba8c26d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.132.0" +version = "2.133.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index b70381cf82..35e5d51bda 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.132.0" +version = "2.133.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index 8d4e5f98a4..4029ebb2f1 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.133.0-beta.1", + "version": "2.133.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 2b6f9bbcea..ab0af742ba 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.132.0", + "version": "2.133.0", "name": "drift", "instructions": [ { From 6a901a34c704eb913cb2a9680357d098f7dee971 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 01:37:17 +0000 Subject: [PATCH 089/216] sdk: release v2.134.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index bc2d37d5c9..b722491b00 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.133.0-beta.1 \ No newline at end of file +2.134.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 4029ebb2f1..1b68d2e86e 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.133.0", + "version": "2.134.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 10f6035ed3d2e998fa74bfd98283c8848e6b5c60 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 12 Aug 2025 11:51:15 +0800 Subject: [PATCH 090/216] refactor(sdk): add getActivePositions to user --- sdk/src/user.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 8224b110ad..8a39e672a2 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -4131,4 +4131,25 @@ export class User { private getOracleDataForSpotMarket(marketIndex: number): OraclePriceData { return this.driftClient.getOracleDataForSpotMarket(marketIndex); } + + /** + * Get the active perp and spot positions of the user. + */ + public getActivePositions(): { + activePerpPositions: number[]; + activeSpotPositions: number[]; + } { + const activePerpMarkets = this.getActivePerpPositions().map( + (position) => position.marketIndex + ); + + const activeSpotMarkets = this.getActiveSpotPositions().map( + (position) => position.marketIndex + ); + + return { + activePerpPositions: activePerpMarkets, + activeSpotPositions: activeSpotMarkets, + }; + } } From 6742151533339d5ab8a72753c509e6359eb2743d Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Aug 2025 03:56:29 +0000 Subject: [PATCH 091/216] sdk: release v2.134.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b722491b00..1e5d8dc137 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.0 \ No newline at end of file +2.134.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 1b68d2e86e..b0a8503ba0 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.0", + "version": "2.134.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 4bca9a28f778f33aa599bac7eac8f5146322c18d Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:37:25 -0400 Subject: [PATCH 092/216] sdk: add spot-market-index-57 to constants (#1815) --- sdk/src/constants/spotMarkets.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdk/src/constants/spotMarkets.ts b/sdk/src/constants/spotMarkets.ts index a732ea2270..085c5e1651 100644 --- a/sdk/src/constants/spotMarkets.ts +++ b/sdk/src/constants/spotMarkets.ts @@ -906,6 +906,18 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [ '0x7a01fca212788bba7c5bf8c9efd576a8a722f070d2c17596ff7bb609b8d5c3b9', pythLazerId: 1578, }, + { + symbol: 'syrupUSDC', + marketIndex: 57, + poolId: 0, + oracle: new PublicKey('GqqkoqHU5pqgTvL88xSCipH9txbPETyzvAvybQ3zRpzw'), + oracleSource: OracleSource.PYTH_PULL, + mint: new PublicKey('AvZZF1YaZDziPY2RCK4oJrRVrbN3mTD9NL24hPeaZeUj'), + precision: new BN(10).pow(SIX), + precisionExp: SIX, + pythFeedId: + '0x2ad31d1c4a85fbf2156ce57fab4104124c5ef76a6386375ecfc8da1ed5ce1486', + }, ]; export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = { From 2f4a0c2871dc7ccb9d398680bf0a8ab1104651df Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 15:42:45 +0000 Subject: [PATCH 093/216] sdk: release v2.134.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 1e5d8dc137..510ee444ef 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.1 \ No newline at end of file +2.134.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index b0a8503ba0..0faac864a3 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.1", + "version": "2.134.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 50059ec6e8b2dc93bb1ba0327dca31d27347006e Mon Sep 17 00:00:00 2001 From: moosecat Date: Wed, 13 Aug 2025 08:56:35 -0700 Subject: [PATCH 094/216] lazer oracle migration (#1813) * lazer oracle migration * spot markets too --- sdk/src/constants/perpMarkets.ts | 30 ++++++++++++++++++------------ sdk/src/constants/spotMarkets.ts | 15 +++++++++------ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/sdk/src/constants/perpMarkets.ts b/sdk/src/constants/perpMarkets.ts index d23acbe648..03643e7d06 100644 --- a/sdk/src/constants/perpMarkets.ts +++ b/sdk/src/constants/perpMarkets.ts @@ -752,11 +752,12 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [ symbol: 'KMNO-PERP', baseAssetSymbol: 'KMNO', marketIndex: 28, - oracle: new PublicKey('7aqj2wH1BH8XT3QQ3MWtvt3My7RAGf5Stm3vx5fiysJz'), + oracle: new PublicKey('6ua3DK1sHoYyNi15dsxy6RYwUcZPDDXfyChzaRMaheQF'), launchTs: 1712240681000, - oracleSource: OracleSource.PYTH_PULL, + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0xb17e5bc5de742a8a378b54c9c75442b7d51e30ada63f28d9bd28d3c0e26511a0', + pythLazerId: 464, }, { fullName: 'Tensor', @@ -790,11 +791,12 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [ symbol: 'CLOUD-PERP', baseAssetSymbol: 'CLOUD', marketIndex: 31, - oracle: new PublicKey('4EgPYJkEyNkoUcaMDbeokUiR4b1f3Hs3DK5S4NzqseR2'), + oracle: new PublicKey('9Ennia27iT83kNAk3JtRKxSMzuCzsVtT4MzuxpE7anME'), launchTs: 1717597648000, - oracleSource: OracleSource.PYTH_PULL, + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0x7358313661dcd4f842a1423aa4f7a05f009001c9113201c719621d3f1aa80a73', + pythLazerId: 404, }, { fullName: 'IO', @@ -935,11 +937,12 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [ symbol: 'MOTHER-PERP', baseAssetSymbol: 'MOTHER', marketIndex: 44, - oracle: new PublicKey('56ap2coZG7FPWUigVm9XrpQs3xuCwnwQaWtjWZcffEUG'), + oracle: new PublicKey('469WQgfJ6AJ3eJ8FUcdhiZawf7yNChA3hseTSyhFatHZ'), launchTs: 1727291859000, - oracleSource: OracleSource.PYTH_PULL, + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0x62742a997d01f7524f791fdb2dd43aaf0e567d765ebf8fd0406a994239e874d4', + pythLazerId: 501, }, { fullName: 'MOODENG', @@ -947,11 +950,12 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [ symbol: 'MOODENG-PERP', baseAssetSymbol: 'MOODENG', marketIndex: 45, - oracle: new PublicKey('21gjgEcuDppthwV16J1QpFzje3vmgMp2uSzh7pJsG7ob'), + oracle: new PublicKey('CVy5m6JqhEdjbz11idgVeb2KnH5NpFowKnYPVMdfc7FC'), launchTs: 1727965864000, - oracleSource: OracleSource.PYTH_PULL, + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0xffff73128917a90950cd0473fd2551d7cd274fd5a6cc45641881bbcc6ee73417', + pythLazerId: 500, }, { fullName: 'WARWICK-FIGHT-WIN-BET', @@ -1036,11 +1040,12 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [ symbol: 'GOAT-PERP', baseAssetSymbol: 'GOAT', marketIndex: 53, - oracle: new PublicKey('5RgXW13Kq1RgCLEsJhhchWt3W4R2XLJnd6KqgZk6dSY7'), + oracle: new PublicKey('4uBrnNZyD2wUkpzytuyfiEYp2eWA3WdxXSbWEQbZzs45'), launchTs: 1731443152000, - oracleSource: OracleSource.PYTH_PULL, + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0xf7731dc812590214d3eb4343bfb13d1b4cfa9b1d4e020644b5d5d8e07d60c66c', + pythLazerId: 437, }, { fullName: 'FWOG', @@ -1289,11 +1294,12 @@ export const MainnetPerpMarkets: PerpMarketConfig[] = [ symbol: 'LAUNCHCOIN-PERP', baseAssetSymbol: 'LAUNCHCOIN', marketIndex: 74, - oracle: new PublicKey('6Dtdqcr6dEKYMRKCvdDa8mSf5qVFbQVDZHTi6WhU1Foj'), + oracle: new PublicKey('GAzR3C5cn7gGVvuqJB57wSYTPWP3n2Lw4mRJRxvTvqYy'), launchTs: 1747318237000, - oracleSource: OracleSource.PYTH_PULL, + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0x6d74813ee17291d5be18a355fe4d43fd300d625caea6554d49f740e7d112141e', + pythLazerId: 1571, }, { fullName: 'PUMP', diff --git a/sdk/src/constants/spotMarkets.ts b/sdk/src/constants/spotMarkets.ts index 085c5e1651..7fa6b7e77d 100644 --- a/sdk/src/constants/spotMarkets.ts +++ b/sdk/src/constants/spotMarkets.ts @@ -465,14 +465,15 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [ symbol: 'CLOUD', marketIndex: 21, poolId: 0, - oracle: new PublicKey('4EgPYJkEyNkoUcaMDbeokUiR4b1f3Hs3DK5S4NzqseR2'), - oracleSource: OracleSource.PYTH_PULL, + oracle: new PublicKey('9Ennia27iT83kNAk3JtRKxSMzuCzsVtT4MzuxpE7anME'), + oracleSource: OracleSource.PYTH_LAZER, pythFeedId: '0x7358313661dcd4f842a1423aa4f7a05f009001c9113201c719621d3f1aa80a73', mint: new PublicKey('CLoUDKc4Ane7HeQcPpE3YHnznRxhMimJ4MyaUqyHFzAu'), precision: new BN(10).pow(NINE), precisionExp: NINE, launchTs: 1721316817000, + pythLazerId: 404, }, { symbol: 'PYUSD', @@ -528,13 +529,14 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [ symbol: 'MOTHER', marketIndex: 26, poolId: 0, - oracle: new PublicKey('56ap2coZG7FPWUigVm9XrpQs3xuCwnwQaWtjWZcffEUG'), - oracleSource: OracleSource.PYTH_PULL, + oracle: new PublicKey('469WQgfJ6AJ3eJ8FUcdhiZawf7yNChA3hseTSyhFatHZ'), + oracleSource: OracleSource.PYTH_LAZER, mint: new PublicKey('3S8qX1MsMqRbiwKg2cQyx7nis1oHMgaCuc9c4VfvVdPN'), precision: new BN(10).pow(SIX), precisionExp: SIX, pythFeedId: '0x62742a997d01f7524f791fdb2dd43aaf0e567d765ebf8fd0406a994239e874d4', + pythLazerId: 501, }, { symbol: 'cbBTC', @@ -783,14 +785,15 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [ symbol: 'ZEUS', marketIndex: 46, poolId: 0, - oracle: new PublicKey('ABetWkmf1dpQX8gbtrt947ma2j7KzTPjVAdFWHkuEzi3'), - oracleSource: OracleSource.PYTH_PULL, + oracle: new PublicKey('8cH72H3vqYPArV9QvkYJkwzTdsdNPPgVPrusz9sMmgNN'), + oracleSource: OracleSource.PYTH_LAZER, mint: new PublicKey('ZEUS1aR7aX8DFFJf5QjWj2ftDDdNTroMNGo8YoQm3Gq'), precision: new BN(10).pow(SIX), precisionExp: SIX, pythFeedId: '0x31558e9ccb18c151af6c52bf78afd03098a7aca1b9cf171a65b693b464c2f066', launchTs: 1747155600000, + pythLazerId: 643, }, { symbol: 'USDC-4', From 5e11313172e265aa2ec4c43b6860992fc5578edc Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:01:43 +0000 Subject: [PATCH 095/216] sdk: release v2.134.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 510ee444ef..ad6cb794c3 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.2 \ No newline at end of file +2.134.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 0faac864a3..82d3afbbad 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.2", + "version": "2.134.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 6b28168d5a843be4b678a65ba7164f23a4db5bb7 Mon Sep 17 00:00:00 2001 From: Lukas deConantsesznak Date: Wed, 13 Aug 2025 06:01:16 -0600 Subject: [PATCH 096/216] feat: option for custom oracle ws subscriber --- .../webSocketDriftClientAccountSubscriber.ts | 21 ++++++++++++++++++- sdk/src/driftClientConfig.ts | 8 +++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts index e8abca5c62..973e5ac007 100644 --- a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts @@ -77,6 +77,14 @@ export class WebSocketDriftClientAccountSubscriber resubOpts?: ResubOpts, commitment?: Commitment ) => AccountSubscriber; + customOracleAccountSubscriber?: new ( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => any, + resubOpts?: ResubOpts, + commitment?: Commitment + ) => AccountSubscriber; protected isSubscribing = false; protected subscriptionPromise: Promise; @@ -98,6 +106,14 @@ export class WebSocketDriftClientAccountSubscriber decodeBuffer?: (buffer: Buffer) => any, resubOpts?: ResubOpts, commitment?: Commitment + ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber, + customOracleAccountSubscriber?: new ( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => any, + resubOpts?: ResubOpts, + commitment?: Commitment ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber ) { this.isSubscribed = false; @@ -111,6 +127,7 @@ export class WebSocketDriftClientAccountSubscriber this.resubOpts = resubOpts; this.commitment = commitment; this.customPerpMarketAccountSubscriber = customPerpMarketAccountSubscriber; + this.customOracleAccountSubscriber = customOracleAccountSubscriber; } public async subscribe(): Promise { @@ -381,7 +398,9 @@ export class WebSocketDriftClientAccountSubscriber this.program.provider.connection, this.program ); - const accountSubscriber = new WebSocketAccountSubscriber( + const AccountSubscriberClass = + this.customOracleAccountSubscriber || WebSocketAccountSubscriber; + const accountSubscriber = new AccountSubscriberClass( 'oracle', this.program, oracleInfo.publicKey, diff --git a/sdk/src/driftClientConfig.ts b/sdk/src/driftClientConfig.ts index bf4db114a4..ebfa4b6954 100644 --- a/sdk/src/driftClientConfig.ts +++ b/sdk/src/driftClientConfig.ts @@ -71,6 +71,14 @@ export type DriftClientSubscriptionConfig = resubOpts?: ResubOpts, commitment?: Commitment ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber; + oracleAccountSubscriber?: new ( + accountName: string, + program: Program, + accountPublicKey: PublicKey, + decodeBuffer?: (buffer: Buffer) => any, + resubOpts?: ResubOpts, + commitment?: Commitment + ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber; } | { type: 'polling'; From fab1c38ba8f4b0e6dcde22061f34a9fe71d784b3 Mon Sep 17 00:00:00 2001 From: Lukas deConantsesznak Date: Wed, 13 Aug 2025 06:30:04 -0600 Subject: [PATCH 097/216] fix: pass custom oracle ws sub option in dc constructor --- sdk/src/driftClient.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 29bf9647df..fa5e441574 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -448,7 +448,8 @@ export class DriftClient { logResubMessages: config.accountSubscription?.logResubMessages, }, config.accountSubscription?.commitment, - config.accountSubscription?.perpMarketAccountSubscriber + config.accountSubscription?.perpMarketAccountSubscriber, + config.accountSubscription?.oracleAccountSubscriber ); } this.eventEmitter = this.accountSubscriber.eventEmitter; From d4341c2d2c71a5d4510db397a3e5b688dec49234 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 19:37:27 +0000 Subject: [PATCH 098/216] sdk: release v2.134.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index ad6cb794c3..667fcc3054 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.3 \ No newline at end of file +2.134.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 82d3afbbad..4f95175a5a 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.3", + "version": "2.134.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From ffc67507c69f3417e94bc1c4432208203e0310e6 Mon Sep 17 00:00:00 2001 From: lil perp Date: Wed, 13 Aug 2025 15:45:20 -0400 Subject: [PATCH 099/216] program: settle pnl invariants (#1812) * program: settle pnl invariants * add test * fix lint * lints * add msg * CHANGELOG * cargo fmt -- --- CHANGELOG.md | 2 + programs/drift/src/controller/pnl.rs | 28 +- programs/drift/src/state/settle_pnl_mode.rs | 2 +- programs/drift/src/state/spot_market.rs | 6 + sdk/src/idl/drift.json | 2 +- test-scripts/run-anchor-tests.sh | 1 + tests/settlePNLInvariant.ts | 426 ++++++++++++++++++++ 7 files changed, 464 insertions(+), 3 deletions(-) create mode 100644 tests/settlePNLInvariant.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ea9deb95f..c950f04cbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: add new settle pnl invariants ([#1812](https://github.com/drift-labs/protocol-v2/pull/1812)) + ### Fixes ### Breaking diff --git a/programs/drift/src/controller/pnl.rs b/programs/drift/src/controller/pnl.rs index 13c29ada7d..5514123de8 100644 --- a/programs/drift/src/controller/pnl.rs +++ b/programs/drift/src/controller/pnl.rs @@ -59,13 +59,20 @@ pub fn settle_pnl( clock: &Clock, state: &State, meets_margin_requirement: Option, - mode: SettlePnlMode, + mut mode: SettlePnlMode, ) -> DriftResult { validate!(!user.is_bankrupt(), ErrorCode::UserBankrupt)?; let now = clock.unix_timestamp; + let tvl_before; + let deposits_balance_before; + let borrows_balance_before; { let spot_market = &mut spot_market_map.get_quote_spot_market_mut()?; update_spot_market_cumulative_interest(spot_market, None, now)?; + + tvl_before = spot_market.get_tvl()?; + deposits_balance_before = spot_market.deposit_balance; + borrows_balance_before = spot_market.borrow_balance; } let mut market = perp_market_map.get_ref_mut(&market_index)?; @@ -281,6 +288,15 @@ pub fn settle_pnl( now, )?; + // if the spot market balance has changed, we have to fail if we are in try settle mode + if (spot_market.deposit_balance != deposits_balance_before + || spot_market.borrow_balance != borrows_balance_before) + && mode == SettlePnlMode::TrySettle + { + msg!("Spot market balance has changed, switch to MUST_SETTLE mode"); + mode = SettlePnlMode::MustSettle; + } + if user_unsettled_pnl == 0 { let msg = format!("User has no unsettled pnl for market {}", market_index); return mode.result(ErrorCode::NoUnsettledPnl, market_index, &msg); @@ -350,6 +366,16 @@ pub fn settle_pnl( explanation: SettlePnlExplanation::None, }); + let tvl_after = spot_market.get_tvl()?; + + validate!( + tvl_before.safe_sub(tvl_after)? <= 10, + ErrorCode::DefaultError, + "Settle Pnl TVL mismatch: before={}, after={}", + tvl_before, + tvl_after + )?; + Ok(()) } diff --git a/programs/drift/src/state/settle_pnl_mode.rs b/programs/drift/src/state/settle_pnl_mode.rs index 346506da4d..f13002d7db 100644 --- a/programs/drift/src/state/settle_pnl_mode.rs +++ b/programs/drift/src/state/settle_pnl_mode.rs @@ -27,7 +27,7 @@ impl SettlePnlMode { ); match self { SettlePnlMode::MustSettle => Err(error_code), - SettlePnlMode::TrySettle => Err(error_code), + SettlePnlMode::TrySettle => Ok(()), } } } diff --git a/programs/drift/src/state/spot_market.rs b/programs/drift/src/state/spot_market.rs index 0f238e96ac..84a0cdd89d 100644 --- a/programs/drift/src/state/spot_market.rs +++ b/programs/drift/src/state/spot_market.rs @@ -454,6 +454,12 @@ impl SpotMarket { get_token_amount(self.borrow_balance, self, &SpotBalanceType::Borrow) } + pub fn get_tvl(&self) -> DriftResult { + let deposits = self.get_deposits()?; + let borrows = self.get_borrows()?; + deposits.safe_sub(borrows) + } + pub fn validate_max_token_deposits_and_borrows( &self, do_max_borrow_check: bool, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index ab0af742ba..9f827311b6 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -16077,4 +16077,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} +} \ No newline at end of file diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 2b10bd1bf7..5b03f289eb 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -74,6 +74,7 @@ test_files=( referrer.ts roundInFavorBaseAsset.ts serumTest.ts + settlePNLInvariant.ts spotDepositWithdraw.ts spotDepositWithdraw22.ts spotDepositWithdraw22TransferHooks.ts diff --git a/tests/settlePNLInvariant.ts b/tests/settlePNLInvariant.ts new file mode 100644 index 0000000000..f51bbbefbd --- /dev/null +++ b/tests/settlePNLInvariant.ts @@ -0,0 +1,426 @@ +import * as anchor from '@coral-xyz/anchor'; +import { assert } from 'chai'; + +import { Program } from '@coral-xyz/anchor'; + +import { PublicKey } from '@solana/web3.js'; + +import { + TestClient, + BN, + EventSubscriber, + SPOT_MARKET_RATE_PRECISION, + SpotBalanceType, + isVariant, + OracleSource, + SPOT_MARKET_WEIGHT_PRECISION, + SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION, + OracleInfo, +} from '../sdk/src'; + +import { + createUserWithUSDCAccount, + createUserWithUSDCAndWSOLAccount, + mockOracleNoProgram, + mockUSDCMint, + mockUserUSDCAccount, + sleep, +} from './testHelpers'; +import { getBalance } from '../sdk/src/math/spotBalance'; +import { NATIVE_MINT } from '@solana/spl-token'; +import { + QUOTE_PRECISION, + ZERO, + SPOT_MARKET_BALANCE_PRECISION, + PRICE_PRECISION, + PositionDirection, + BASE_PRECISION, + PEG_PRECISION, +} from '../sdk'; +import { startAnchor } from 'solana-bankrun'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; + +describe('spot deposit and withdraw', () => { + const chProgram = anchor.workspace.Drift as Program; + + let admin: TestClient; + let eventSubscriber: EventSubscriber; + + let bulkAccountLoader: TestBulkAccountLoader; + + let bankrunContextWrapper: BankrunContextWrapper; + + let solOracle: PublicKey; + + let usdcMint; + + let firstUserDriftClient: TestClient; + let firstUserDriftClientUSDCAccount: PublicKey; + + let secondUserDriftClient: TestClient; + let secondUserDriftClientWSOLAccount: PublicKey; + + const usdcAmount = new BN(10 * 10 ** 6); + const largeUsdcAmount = new BN(10_000 * 10 ** 6); + + const solAmount = new BN(1 * 10 ** 9); + + let marketIndexes: number[]; + let spotMarketIndexes: number[]; + let oracleInfos: OracleInfo[]; + + before(async () => { + const context = await startAnchor('', [], []); + + 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); + await mockUserUSDCAccount(usdcMint, largeUsdcAmount, bankrunContextWrapper); + + solOracle = await mockOracleNoProgram(bankrunContextWrapper, 30); + + marketIndexes = [0]; + spotMarketIndexes = [0, 1]; + oracleInfos = [{ publicKey: solOracle, source: OracleSource.PYTH }]; + + admin = 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 admin.initialize(usdcMint.publicKey, true); + await admin.subscribe(); + }); + + after(async () => { + await admin.unsubscribe(); + await eventSubscriber.unsubscribe(); + await firstUserDriftClient.unsubscribe(); + await secondUserDriftClient.unsubscribe(); + }); + + it('Initialize USDC Market', async () => { + const optimalUtilization = SPOT_MARKET_RATE_PRECISION.div( + new BN(2) + ).toNumber(); // 50% utilization + const optimalRate = SPOT_MARKET_RATE_PRECISION.mul(new BN(20)).toNumber(); // 2000% APR + const maxRate = SPOT_MARKET_RATE_PRECISION.mul(new BN(50)).toNumber(); // 5000% APR + const initialAssetWeight = SPOT_MARKET_WEIGHT_PRECISION.toNumber(); + const maintenanceAssetWeight = SPOT_MARKET_WEIGHT_PRECISION.toNumber(); + const initialLiabilityWeight = SPOT_MARKET_WEIGHT_PRECISION.toNumber(); + const maintenanceLiabilityWeight = SPOT_MARKET_WEIGHT_PRECISION.toNumber(); + await admin.initializeSpotMarket( + usdcMint.publicKey, + optimalUtilization, + optimalRate, + maxRate, + PublicKey.default, + OracleSource.QUOTE_ASSET, + initialAssetWeight, + maintenanceAssetWeight, + initialLiabilityWeight, + maintenanceLiabilityWeight + ); + const txSig = await admin.updateWithdrawGuardThreshold( + 0, + new BN(10 ** 10).mul(QUOTE_PRECISION) + ); + bankrunContextWrapper.printTxLogs(txSig); + await admin.fetchAccounts(); + const spotMarket = await admin.getSpotMarketAccount(0); + assert(spotMarket.marketIndex === 0); + assert(spotMarket.optimalUtilization === optimalUtilization); + assert(spotMarket.optimalBorrowRate === optimalRate); + assert(spotMarket.maxBorrowRate === maxRate); + assert( + spotMarket.cumulativeBorrowInterest.eq( + SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION + ) + ); + assert( + spotMarket.cumulativeDepositInterest.eq( + SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION + ) + ); + assert(spotMarket.initialAssetWeight === initialAssetWeight); + assert(spotMarket.maintenanceAssetWeight === maintenanceAssetWeight); + assert(spotMarket.initialLiabilityWeight === initialLiabilityWeight); + assert(spotMarket.maintenanceAssetWeight === maintenanceAssetWeight); + + assert(admin.getStateAccount().numberOfSpotMarkets === 1); + }); + + it('Initialize SOL Market', async () => { + const optimalUtilization = SPOT_MARKET_RATE_PRECISION.div( + new BN(2) + ).toNumber(); // 50% utilization + const optimalRate = SPOT_MARKET_RATE_PRECISION.mul(new BN(20)).toNumber(); // 2000% APR + const maxRate = SPOT_MARKET_RATE_PRECISION.mul(new BN(50)).toNumber(); // 5000% APR + const initialAssetWeight = SPOT_MARKET_WEIGHT_PRECISION.mul(new BN(8)) + .div(new BN(10)) + .toNumber(); + const maintenanceAssetWeight = SPOT_MARKET_WEIGHT_PRECISION.mul(new BN(9)) + .div(new BN(10)) + .toNumber(); + const initialLiabilityWeight = SPOT_MARKET_WEIGHT_PRECISION.mul(new BN(12)) + .div(new BN(10)) + .toNumber(); + const maintenanceLiabilityWeight = SPOT_MARKET_WEIGHT_PRECISION.mul( + new BN(11) + ) + .div(new BN(10)) + .toNumber(); + + await admin.initializeSpotMarket( + NATIVE_MINT, + optimalUtilization, + optimalRate, + maxRate, + solOracle, + OracleSource.PYTH, + initialAssetWeight, + maintenanceAssetWeight, + initialLiabilityWeight, + maintenanceLiabilityWeight + ); + + const txSig = await admin.updateWithdrawGuardThreshold( + 1, + new BN(10 ** 10).mul(QUOTE_PRECISION) + ); + bankrunContextWrapper.printTxLogs(txSig); + await admin.fetchAccounts(); + const spotMarket = await admin.getSpotMarketAccount(1); + assert(spotMarket.marketIndex === 1); + assert(spotMarket.optimalUtilization === optimalUtilization); + assert(spotMarket.optimalBorrowRate === optimalRate); + assert(spotMarket.maxBorrowRate === maxRate); + assert( + spotMarket.cumulativeBorrowInterest.eq( + SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION + ) + ); + assert( + spotMarket.cumulativeDepositInterest.eq( + SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION + ) + ); + assert(spotMarket.initialAssetWeight === initialAssetWeight); + assert(spotMarket.maintenanceAssetWeight === maintenanceAssetWeight); + assert(spotMarket.initialLiabilityWeight === initialLiabilityWeight); + assert(spotMarket.maintenanceAssetWeight === maintenanceAssetWeight); + + console.log(spotMarket.historicalOracleData); + assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.eq(ZERO)); + + assert( + spotMarket.historicalOracleData.lastOraclePrice.eq( + new BN(30 * PRICE_PRECISION.toNumber()) + ) + ); + assert( + spotMarket.historicalOracleData.lastOraclePriceTwap.eq( + new BN(30 * PRICE_PRECISION.toNumber()) + ) + ); + assert( + spotMarket.historicalOracleData.lastOraclePriceTwap5Min.eq( + new BN(30 * PRICE_PRECISION.toNumber()) + ) + ); + + assert(admin.getStateAccount().numberOfSpotMarkets === 2); + }); + + it('First User Deposit USDC', async () => { + [firstUserDriftClient, firstUserDriftClientUSDCAccount] = + await createUserWithUSDCAccount( + bankrunContextWrapper, + usdcMint, + chProgram, + usdcAmount, + marketIndexes, + spotMarketIndexes, + oracleInfos, + bulkAccountLoader + ); + + const marketIndex = 0; + await sleep(100); + await firstUserDriftClient.fetchAccounts(); + const txSig = await firstUserDriftClient.deposit( + usdcAmount, + marketIndex, + firstUserDriftClientUSDCAccount + ); + bankrunContextWrapper.printTxLogs(txSig); + + const spotMarket = await admin.getSpotMarketAccount(marketIndex); + assert( + spotMarket.depositBalance.eq( + new BN(10 * SPOT_MARKET_BALANCE_PRECISION.toNumber()) + ) + ); + + const vaultAmount = new BN( + ( + await bankrunContextWrapper.connection.getTokenAccount(spotMarket.vault) + ).amount.toString() + ); + assert(vaultAmount.eq(usdcAmount)); + + const expectedBalance = getBalance( + usdcAmount, + spotMarket, + SpotBalanceType.DEPOSIT + ); + const spotPosition = firstUserDriftClient.getUserAccount().spotPositions[0]; + assert(isVariant(spotPosition.balanceType, 'deposit')); + assert(spotPosition.scaledBalance.eq(expectedBalance)); + + assert(firstUserDriftClient.getUserAccount().totalDeposits.eq(usdcAmount)); + }); + + it('Second User Deposit SOL', async () => { + [ + secondUserDriftClient, + secondUserDriftClientWSOLAccount, + secondUserDriftClientUSDCAccount, + ] = await createUserWithUSDCAndWSOLAccount( + bankrunContextWrapper, + usdcMint, + chProgram, + solAmount, + ZERO, + marketIndexes, + spotMarketIndexes, + oracleInfos, + bulkAccountLoader + ); + + const marketIndex = 1; + const txSig = await secondUserDriftClient.deposit( + solAmount, + marketIndex, + secondUserDriftClientWSOLAccount + ); + bankrunContextWrapper.printTxLogs(txSig); + + const spotMarket = await admin.getSpotMarketAccount(marketIndex); + assert(spotMarket.depositBalance.eq(SPOT_MARKET_BALANCE_PRECISION)); + console.log(spotMarket.historicalOracleData); + assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.gt(ZERO)); + assert( + spotMarket.historicalOracleData.lastOraclePrice.eq( + new BN(30 * PRICE_PRECISION.toNumber()) + ) + ); + assert( + spotMarket.historicalOracleData.lastOraclePriceTwap.eq( + new BN(30 * PRICE_PRECISION.toNumber()) + ) + ); + assert( + spotMarket.historicalOracleData.lastOraclePriceTwap5Min.eq( + new BN(30 * PRICE_PRECISION.toNumber()) + ) + ); + + const vaultAmount = new BN( + ( + await bankrunContextWrapper.connection.getTokenAccount(spotMarket.vault) + ).amount.toString() + ); + assert(vaultAmount.eq(solAmount)); + + const expectedBalance = getBalance( + solAmount, + spotMarket, + SpotBalanceType.DEPOSIT + ); + const spotPosition = + secondUserDriftClient.getUserAccount().spotPositions[1]; + assert(isVariant(spotPosition.balanceType, 'deposit')); + assert(spotPosition.scaledBalance.eq(expectedBalance)); + + assert( + secondUserDriftClient + .getUserAccount() + .totalDeposits.eq(new BN(30).mul(PRICE_PRECISION)) + ); + }); + + it('Initialize Market', async () => { + const periodicity = new BN(60 * 60); // 1 HOUR + const mantissaSqrtScale = new BN(100000); + const ammInitialQuoteAssetAmount = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + const ammInitialBaseAssetAmount = new anchor.BN(5 * 10 ** 13).mul( + mantissaSqrtScale + ); + await admin.initializePerpMarket( + 0, + solOracle, + ammInitialBaseAssetAmount, + ammInitialQuoteAssetAmount, + periodicity, + new BN(30).mul(PEG_PRECISION) + ); + + await admin.updatePerpAuctionDuration(new BN(0)); + }); + + it('Trade and settle pnl', async () => { + const marketIndex = 0; + const baseAssetAmount = BASE_PRECISION; + await secondUserDriftClient.openPosition( + PositionDirection.LONG, + baseAssetAmount, + marketIndex + ); + + await secondUserDriftClient.settlePNL( + await secondUserDriftClient.getUserAccountPublicKey(), + secondUserDriftClient.getUserAccount(), + marketIndex + ); + + await secondUserDriftClient.fetchAccounts(); + + const quoteTokenAmount = await secondUserDriftClient.getTokenAmount(0); + + assert(quoteTokenAmount.eq(new BN(-30003))); + + const settlePnlRecord = eventSubscriber.getEventsArray('SettlePnlRecord'); + assert(settlePnlRecord.length === 1); + assert(settlePnlRecord[0].pnl.eq(new BN(-30002))); + }); +}); From 58cb261fc0a18889dc67f3bb93f5b26333ba512c Mon Sep 17 00:00:00 2001 From: lil perp Date: Wed, 13 Aug 2025 15:46:41 -0400 Subject: [PATCH 100/216] program: add_update_perp_pnl_pool (#1810) * program: add_update_perp_pnl_pool * test * CHANGELOG --- CHANGELOG.md | 1 + programs/drift/src/instructions/admin.rs | 53 ++++++++++++++++++++++++ programs/drift/src/lib.rs | 7 ++++ sdk/src/adminClient.ts | 36 ++++++++++++++++ sdk/src/idl/drift.json | 36 ++++++++++++++++ tests/admin.ts | 47 +++++++++++++++++++++ 6 files changed, 180 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c950f04cbd..70ee2f27e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features - program: add new settle pnl invariants ([#1812](https://github.com/drift-labs/protocol-v2/pull/1812)) +- program: add update_perp_market_pnl_pool ([#1810](https://github.com/drift-labs/protocol-v2/pull/1810)) ### Fixes diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 3b17025310..a019e58a57 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -1967,6 +1967,36 @@ pub fn handle_deposit_into_perp_market_fee_pool<'c: 'info, 'info>( Ok(()) } +#[access_control( + perp_market_valid(&ctx.accounts.perp_market) +)] +pub fn handle_update_perp_market_pnl_pool<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, UpdatePerpMarketPnlPool<'info>>, + amount: u64, +) -> Result<()> { + let perp_market = &mut load_mut!(ctx.accounts.perp_market)?; + + let spot_market = &mut load_mut!(ctx.accounts.spot_market)?; + + controller::spot_balance::update_spot_balances( + amount.cast::()?, + &SpotBalanceType::Deposit, + spot_market, + &mut perp_market.pnl_pool, + false, + )?; + + validate_spot_market_vault_amount(spot_market, ctx.accounts.spot_market_vault.amount)?; + + msg!( + "updating perp market {} pnl pool with amount {}", + perp_market.market_index, + amount + ); + + Ok(()) +} + #[access_control( deposit_not_paused(&ctx.accounts.state) spot_market_valid(&ctx.accounts.spot_market) @@ -5261,6 +5291,29 @@ pub struct SettleExpiredMarketPoolsToRevenuePool<'info> { pub perp_market: AccountLoader<'info, PerpMarket>, } +#[derive(Accounts)] +pub struct UpdatePerpMarketPnlPool<'info> { + #[account( + has_one = admin + )] + pub state: Box>, + pub admin: Signer<'info>, + #[account( + seeds = [b"spot_market", 0_u16.to_le_bytes().as_ref()], + bump, + mut + )] + pub spot_market: AccountLoader<'info, SpotMarket>, + #[account( + mut, + seeds = [b"spot_market_vault".as_ref(), 0_u16.to_le_bytes().as_ref()], + bump, + )] + pub spot_market_vault: Box>, + #[account(mut)] + pub perp_market: AccountLoader<'info, PerpMarket>, +} + #[derive(Accounts)] pub struct DepositIntoMarketFeePool<'info> { #[account( diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index debfa97229..92229f7e6a 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -1087,6 +1087,13 @@ pub mod drift { handle_deposit_into_perp_market_fee_pool(ctx, amount) } + pub fn update_perp_market_pnl_pool<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, UpdatePerpMarketPnlPool<'info>>, + amount: u64, + ) -> Result<()> { + handle_update_perp_market_pnl_pool(ctx, amount) + } + pub fn deposit_into_spot_market_vault<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DepositIntoSpotMarketVault<'info>>, amount: u64, diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index 9b90cc1425..352e051bf2 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -1079,6 +1079,42 @@ export class AdminClient extends DriftClient { }); } + public async updatePerpMarketPnlPool( + perpMarketIndex: number, + amount: BN + ): Promise { + const updatePerpMarketPnlPoolIx = await this.getUpdatePerpMarketPnlPoolIx( + perpMarketIndex, + amount + ); + + const tx = await this.buildTransaction(updatePerpMarketPnlPoolIx); + + const { txSig } = await this.sendTransaction(tx, [], this.opts); + + return txSig; + } + + public async getUpdatePerpMarketPnlPoolIx( + perpMarketIndex: number, + amount: BN + ): Promise { + return await this.program.instruction.updatePerpMarketPnlPool(amount, { + accounts: { + admin: this.isSubscribed + ? this.getStateAccount().admin + : this.wallet.publicKey, + state: await this.getStatePublicKey(), + perpMarket: await getPerpMarketPublicKey( + this.program.programId, + perpMarketIndex + ), + spotMarket: this.getQuoteSpotMarketAccount().pubkey, + spotMarketVault: this.getQuoteSpotMarketAccount().vault, + }, + }); + } + public async depositIntoSpotMarketVault( spotMarketIndex: number, amount: BN, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 9f827311b6..12654be9e1 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -4832,6 +4832,42 @@ } ] }, + { + "name": "updatePerpMarketPnlPool", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "spotMarket", + "isMut": true, + "isSigner": false + }, + { + "name": "spotMarketVault", + "isMut": true, + "isSigner": false + }, + { + "name": "perpMarket", + "isMut": true, + "isSigner": false + } + ], + "args": [ + { + "name": "amount", + "type": "u64" + } + ] + }, { "name": "depositIntoSpotMarketVault", "accounts": [ diff --git a/tests/admin.ts b/tests/admin.ts index 6faef7d2d6..7eb87c396a 100644 --- a/tests/admin.ts +++ b/tests/admin.ts @@ -6,9 +6,11 @@ import { BN, ExchangeStatus, getPythLazerOraclePublicKey, + getTokenAmount, loadKeypair, OracleGuardRails, OracleSource, + SpotBalanceType, TestClient, Wallet, } from '../sdk/src'; @@ -19,6 +21,7 @@ import { initializeQuoteSpotMarket, mockOracleNoProgram, mockUSDCMint, + mockUserUSDCAccount, } from './testHelpers'; import { PublicKey } from '@solana/web3.js'; import { @@ -26,6 +29,7 @@ import { Connection, } from '../sdk/src/bankrun/bankrunConnection'; import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { createTransferCheckedInstruction } from '@solana/spl-token'; describe('admin', () => { const chProgram = anchor.workspace.Drift as Program; @@ -36,6 +40,10 @@ describe('admin', () => { let usdcMint; + let userUSDCAccount; + + const usdcAmount = new BN(10 * 10 ** 6); + let bankrunContextWrapper: BankrunContextWrapper; before(async () => { @@ -72,6 +80,13 @@ describe('admin', () => { }, }); + userUSDCAccount = await mockUserUSDCAccount( + usdcMint, + usdcAmount, + bankrunContextWrapper, + driftClient.wallet.publicKey + ); + await driftClient.initialize(usdcMint.publicKey, true); await driftClient.subscribe(); await driftClient.initializeUserAccount(0); @@ -457,6 +472,38 @@ describe('admin', () => { assert(perpMarket.amm.ammSpreadAdjustment == ammSpreadAdjustment); }); + it('update pnl pool', async () => { + const quoteVault = driftClient.getSpotMarketAccount(0).vault; + + const splTransferIx = createTransferCheckedInstruction( + userUSDCAccount.publicKey, + usdcMint.publicKey, + quoteVault, + driftClient.wallet.publicKey, + usdcAmount.toNumber(), + 6 + ); + + const tx = await driftClient.buildTransaction(splTransferIx); + // @ts-ignore + await driftClient.sendTransaction(tx); + + await driftClient.updatePerpMarketPnlPool(0, usdcAmount); + + await driftClient.fetchAccounts(); + + const perpMarket = driftClient.getPerpMarketAccount(0); + const spotMarket = driftClient.getSpotMarketAccount(0); + + const tokenAmount = getTokenAmount( + perpMarket.pnlPool.scaledBalance, + spotMarket, + SpotBalanceType.DEPOSIT + ); + + assert(tokenAmount.eq(usdcAmount)); + }); + it('Update admin', async () => { const newAdminKey = PublicKey.default; From 68b8cf190fb3b8db19b45eed6bc07036e71575f1 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 19:50:11 +0000 Subject: [PATCH 101/216] sdk: release v2.134.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 667fcc3054..5d3a8cab27 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.4 \ No newline at end of file +2.134.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 4f95175a5a..f0727bc524 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.4", + "version": "2.134.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From bfe0ee66f76f3a7bb83007dbbc1ba255dd1507f0 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:57:34 -0400 Subject: [PATCH 102/216] program: update-mark-twap-integer-bias (#1783) * program: update-mark-twap-integer-bias * changelog update --- CHANGELOG.md | 2 ++ programs/drift/src/controller/spot_balance.rs | 21 ++++++++---- .../src/controller/spot_balance/tests.rs | 14 +++++--- programs/drift/src/instructions/keeper.rs | 33 ++++++++++++++----- programs/drift/src/math/amm.rs | 14 ++++++++ programs/drift/src/math/amm/tests.rs | 8 ++--- programs/drift/src/math/stats.rs | 21 +++++++----- 7 files changed, 81 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70ee2f27e8..bbc07e5e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- program: fix small number mark-twap-integer-bias ([#1783](https://github.com/drift-labs/protocol-v2/pull/1783)) + ### Breaking ## [2.133.0] - 2025-08-11 diff --git a/programs/drift/src/controller/spot_balance.rs b/programs/drift/src/controller/spot_balance.rs index 47cf95b22a..6e34edb369 100644 --- a/programs/drift/src/controller/spot_balance.rs +++ b/programs/drift/src/controller/spot_balance.rs @@ -9,7 +9,7 @@ use crate::error::{DriftResult, ErrorCode}; use crate::math::amm::sanitize_new_price; use crate::math::casting::Cast; use crate::math::constants::{ - FIVE_MINUTE, IF_FACTOR_PRECISION, ONE_HOUR, QUOTE_SPOT_MARKET_INDEX, + FIVE_MINUTE, IF_FACTOR_PRECISION, ONE_HOUR, ONE_MINUTE, QUOTE_SPOT_MARKET_INDEX, SPOT_MARKET_TOKEN_TWAP_WINDOW, }; use crate::math::spot_balance::{ @@ -55,6 +55,7 @@ pub fn update_spot_market_twap_stats( spot_market.deposit_token_twap.cast()?, since_last, from_start, + None, )? .cast()?; @@ -63,6 +64,7 @@ pub fn update_spot_market_twap_stats( spot_market.borrow_token_twap.cast()?, since_last, from_start, + None, )? .cast()?; @@ -73,6 +75,7 @@ pub fn update_spot_market_twap_stats( spot_market.utilization_twap.cast()?, since_last, from_start, + None, )? .cast()?; @@ -103,15 +106,19 @@ pub fn update_spot_market_twap_stats( FIVE_MINUTE as i64, )?; - spot_market.historical_oracle_data.last_oracle_price_twap = oracle_price_twap; - spot_market - .historical_oracle_data - .last_oracle_price_twap_5min = oracle_price_twap_5min; - spot_market.historical_oracle_data.last_oracle_price = oracle_price_data.price; spot_market.historical_oracle_data.last_oracle_conf = oracle_price_data.confidence; spot_market.historical_oracle_data.last_oracle_delay = oracle_price_data.delay; - spot_market.historical_oracle_data.last_oracle_price_twap_ts = now; + + if oracle_price_twap != spot_market.historical_oracle_data.last_oracle_price_twap + || since_last >= (ONE_MINUTE as i64) + { + spot_market.historical_oracle_data.last_oracle_price_twap = oracle_price_twap; + spot_market + .historical_oracle_data + .last_oracle_price_twap_5min = oracle_price_twap_5min; + spot_market.historical_oracle_data.last_oracle_price_twap_ts = now; + } } spot_market.last_twap_ts = now.cast()?; diff --git a/programs/drift/src/controller/spot_balance/tests.rs b/programs/drift/src/controller/spot_balance/tests.rs index 1bece9f257..b57790f361 100644 --- a/programs/drift/src/controller/spot_balance/tests.rs +++ b/programs/drift/src/controller/spot_balance/tests.rs @@ -1743,12 +1743,18 @@ fn check_usdc_spot_market_twap() { ); let wa_res = - calculate_weighted_average(PRICE_PRECISION_I64, PRICE_PRECISION_I64, 0, ONE_HOUR).unwrap(); + calculate_weighted_average(PRICE_PRECISION_I64, PRICE_PRECISION_I64, 0, ONE_HOUR, None) + .unwrap(); assert_eq!(wa_res, PRICE_PRECISION_I64); - let wa_res2 = - calculate_weighted_average(PRICE_PRECISION_I64, PRICE_PRECISION_I64 + 1, 0, ONE_HOUR) - .unwrap(); + let wa_res2 = calculate_weighted_average( + PRICE_PRECISION_I64, + PRICE_PRECISION_I64 + 1, + 0, + ONE_HOUR, + None, + ) + .unwrap(); assert_eq!(wa_res2, PRICE_PRECISION_I64 + 1); assert_eq!( diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 34b2abe837..c34b8dda82 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -2446,13 +2446,9 @@ pub fn handle_update_perp_bid_ask_twap<'c: 'info, 'info>( msg!("skipping mark twap update for disabled amm prediction market"); return Ok(()); } - - msg!( - "before amm bid twap = {} ask twap = {} ts = {}", - perp_market.amm.last_bid_price_twap, - perp_market.amm.last_ask_price_twap, - perp_market.amm.last_mark_price_twap_ts - ); + let before_bid_price_twap = perp_market.amm.last_bid_price_twap; + let before_ask_price_twap = perp_market.amm.last_ask_price_twap; + let before_mark_twap_ts = perp_market.amm.last_mark_price_twap_ts; let sanitize_clamp_denominator = perp_market.get_sanitize_clamp_denominator()?; math::amm::update_mark_twap_crank( @@ -2465,12 +2461,33 @@ pub fn handle_update_perp_bid_ask_twap<'c: 'info, 'info>( )?; msg!( - "after amm bid twap = {} ask twap = {} ts = {}", + "after amm bid twap = {} -> {} + ask twap = {} -> {} + ts = {} -> {}", + before_bid_price_twap, perp_market.amm.last_bid_price_twap, + before_ask_price_twap, perp_market.amm.last_ask_price_twap, + before_mark_twap_ts, perp_market.amm.last_mark_price_twap_ts ); + if perp_market.amm.last_bid_price_twap == before_bid_price_twap + || perp_market.amm.last_ask_price_twap == before_ask_price_twap + { + validate!( + perp_market + .amm + .last_mark_price_twap_ts + .safe_sub(before_mark_twap_ts)? + >= 60 + || estimated_bid.unwrap_or(0) == before_bid_price_twap + || estimated_ask.unwrap_or(0) == before_ask_price_twap, + ErrorCode::CantUpdatePerpBidAskTwap, + "bid or ask twap unchanged from small ts delta update", + )?; + } + let funding_paused = state.funding_paused()? || perp_market.is_operation_paused(PerpOperation::UpdateFunding); controller::funding::update_funding_rate( diff --git a/programs/drift/src/math/amm.rs b/programs/drift/src/math/amm.rs index 525082dd9d..39c33e40b9 100644 --- a/programs/drift/src/math/amm.rs +++ b/programs/drift/src/math/amm.rs @@ -267,6 +267,12 @@ pub fn update_mark_twap( amm.last_bid_price_twap.cast()?, last_valid_trade_since_oracle_twap_update, from_start_valid, + Some( + amm.historical_oracle_data + .last_oracle_price_twap + .safe_sub(amm.last_bid_price_twap.cast()?)? + .signum(), + ), )?, calculate_weighted_average( amm.historical_oracle_data @@ -275,6 +281,12 @@ pub fn update_mark_twap( amm.last_ask_price_twap.cast()?, last_valid_trade_since_oracle_twap_update, from_start_valid, + Some( + amm.historical_oracle_data + .last_oracle_price_twap + .safe_sub(amm.last_ask_price_twap.cast()?)? + .signum(), + ), )?, ) } else { @@ -527,6 +539,7 @@ pub fn calculate_new_oracle_price_twap( oracle_price, since_last_valid, from_start_valid, + None, )? } else { oracle_price @@ -537,6 +550,7 @@ pub fn calculate_new_oracle_price_twap( last_oracle_twap.cast()?, since_last, from_start, + None, ) } diff --git a/programs/drift/src/math/amm/tests.rs b/programs/drift/src/math/amm/tests.rs index d8dfd37c72..da9e87e243 100644 --- a/programs/drift/src/math/amm/tests.rs +++ b/programs/drift/src/math/amm/tests.rs @@ -734,17 +734,17 @@ fn update_mark_twap_tests() { let new_bid_twap = amm.last_bid_price_twap; let new_ask_twap = amm.last_ask_price_twap; - assert_eq!(new_bid_twap, 39_989_389); - assert_eq!(new_ask_twap, 40_000_790); + assert_eq!(new_bid_twap, 39_989_386); + assert_eq!(new_ask_twap, 40_000_774); assert!(new_bid_twap < new_ask_twap); assert_eq!((new_bid_twap + new_ask_twap) / 2, new_mark_twap); assert_eq!(new_oracle_twap, 39_998_518); - assert_eq!(new_mark_twap, 39995089); + assert_eq!(new_mark_twap, 39995080); assert!((new_oracle_twap as u64) >= new_mark_twap); // funding in favor of maker assert_eq!(amm.oracle_std, 7240); - assert_eq!(amm.mark_std, 24467); + assert_eq!(amm.mark_std, 24457); } #[test] diff --git a/programs/drift/src/math/stats.rs b/programs/drift/src/math/stats.rs index 57210e4b12..d75d2428a9 100644 --- a/programs/drift/src/math/stats.rs +++ b/programs/drift/src/math/stats.rs @@ -23,6 +23,7 @@ pub fn calculate_weighted_average( data2: i64, weight1: i64, weight2: i64, + bias: Option, ) -> DriftResult { let denominator = weight1.safe_add(weight2)?.cast::()?; let prev_twap_99 = data1.cast::()?.safe_mul(weight1.cast()?)?; @@ -36,17 +37,19 @@ pub fn calculate_weighted_average( return Ok(data1); } - let bias: i64 = if weight2 > 1 { - if latest_price_01 < prev_twap_99 { - -1 - } else if latest_price_01 > prev_twap_99 { - 1 + let bias: i64 = bias.unwrap_or_else(|| { + if weight2 > 1 { + if latest_price_01 < prev_twap_99 { + -1 + } else if latest_price_01 > prev_twap_99 { + 1 + } else { + 0 + } } else { 0 } - } else { - 0 - }; + }); let twap = prev_twap_99 .safe_add(latest_price_01)? @@ -70,5 +73,5 @@ pub fn calculate_new_twap( let since_last = max(0_i64, current_ts.safe_sub(last_ts)?); let from_start = max(1_i64, period.safe_sub(since_last)?); - calculate_weighted_average(current_price, last_twap, since_last, from_start) + calculate_weighted_average(current_price, last_twap, since_last, from_start, None) } From d1dcc960caef4fe98725c982cc47e38c61b533fb Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:58:18 -0400 Subject: [PATCH 103/216] program: update-fee-tier-determine-fix5 (#1800) * program: update-fee-tier-determine-fix5 * update changelog --- CHANGELOG.md | 2 ++ programs/drift/src/math/fees.rs | 2 +- programs/drift/src/math/fees/tests.rs | 2 +- sdk/src/user.ts | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbc07e5e34..74a4589662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- program: correct fee tier 5 volume requirement ([#1800](https://github.com/drift-labs/protocol-v2/pull/1800)) + - program: fix small number mark-twap-integer-bias ([#1783](https://github.com/drift-labs/protocol-v2/pull/1783)) ### Breaking diff --git a/programs/drift/src/math/fees.rs b/programs/drift/src/math/fees.rs index 6e1dc0a472..da3cd3a356 100644 --- a/programs/drift/src/math/fees.rs +++ b/programs/drift/src/math/fees.rs @@ -441,7 +441,7 @@ fn determine_perp_fee_tier( ONE_MILLION_QUOTE * 2, FIVE_MILLION_QUOTE * 2, TEN_MILLION_QUOTE * 2, - FIFTY_MILLION_QUOTE * 2, + TEN_MILLION_QUOTE * 8, ONE_HUNDRED_MILLION_QUOTE * 2, ]; diff --git a/programs/drift/src/math/fees/tests.rs b/programs/drift/src/math/fees/tests.rs index 00b1ef639d..82188b62b9 100644 --- a/programs/drift/src/math/fees/tests.rs +++ b/programs/drift/src/math/fees/tests.rs @@ -1013,7 +1013,7 @@ mod calcuate_fee_tiers { assert_eq!(res.maker_rebate_numerator, 25); assert_eq!(res.maker_rebate_denominator, 1000000); - taker_stats.taker_volume_30d = 80_000_000 * QUOTE_PRECISION_U64; + taker_stats.taker_volume_30d = 70_000_000 * QUOTE_PRECISION_U64; let res: FeeTier = determine_user_fee_tier(&taker_stats, &fee_structure, &MarketType::Perp, false) diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 8a39e672a2..4f1c33cd16 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -3515,7 +3515,7 @@ export class User { new BN(2_000_000).mul(QUOTE_PRECISION), new BN(10_000_000).mul(QUOTE_PRECISION), new BN(20_000_000).mul(QUOTE_PRECISION), - new BN(100_000_000).mul(QUOTE_PRECISION), + new BN(80_000_000).mul(QUOTE_PRECISION), new BN(200_000_000).mul(QUOTE_PRECISION), ]; const stakeThresholds = [ From 8de9f6002d30c08c93bab2a437019cda09f15979 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 13 Aug 2025 17:00:29 -0400 Subject: [PATCH 104/216] program: update-mark-twap-crank-use-5min-basis (#1769) * program: update-mark-twap-crank-use-5min-basis * changelog --- CHANGELOG.md | 1 + programs/drift/src/math/amm.rs | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74a4589662..7b7bfd766f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - program: add new settle pnl invariants ([#1812](https://github.com/drift-labs/protocol-v2/pull/1812)) - program: add update_perp_market_pnl_pool ([#1810](https://github.com/drift-labs/protocol-v2/pull/1810)) +- program: update mark twap crank use 5min basis for bid/ask ([#1769](https://github.com/drift-labs/protocol-v2/pull/1769)) ### Fixes diff --git a/programs/drift/src/math/amm.rs b/programs/drift/src/math/amm.rs index 39c33e40b9..a18560e881 100644 --- a/programs/drift/src/math/amm.rs +++ b/programs/drift/src/math/amm.rs @@ -125,7 +125,13 @@ pub fn update_mark_twap_crank( // handle crossing bid/ask if best_bid_price > best_ask_price { - if best_bid_price >= oracle_price_data.price.cast()? { + let market_basis = amm + .last_mark_price_twap_5min + .cast::()? + .safe_sub(amm.historical_oracle_data.last_oracle_price_twap_5min)? + .clamp(-oracle_price_data.price/100, oracle_price_data.price/100); + + if best_bid_price >= oracle_price_data.price.safe_add(market_basis)?.cast()? { best_bid_price = best_ask_price; } else { best_ask_price = best_bid_price; From 59d836c74a38ef09b3763e2b61fbca566374ef4a Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 13 Aug 2025 17:02:32 -0400 Subject: [PATCH 105/216] program: update-min-margin-const-limit (#1802) * program: update-min-margin-const-limit * add CHANGELOG.md --- CHANGELOG.md | 2 +- programs/drift/src/math/constants.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b7bfd766f..6b226a2cd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - program: add new settle pnl invariants ([#1812](https://github.com/drift-labs/protocol-v2/pull/1812)) - program: add update_perp_market_pnl_pool ([#1810](https://github.com/drift-labs/protocol-v2/pull/1810)) +- program: increase min margin ratio invariant constant ([#1802](https://github.com/drift-labs/protocol-v2/pull/1802)) - program: update mark twap crank use 5min basis for bid/ask ([#1769](https://github.com/drift-labs/protocol-v2/pull/1769)) ### Fixes - program: correct fee tier 5 volume requirement ([#1800](https://github.com/drift-labs/protocol-v2/pull/1800)) - - program: fix small number mark-twap-integer-bias ([#1783](https://github.com/drift-labs/protocol-v2/pull/1783)) ### Breaking diff --git a/programs/drift/src/math/constants.rs b/programs/drift/src/math/constants.rs index 9ae6c0ca24..1c127315ae 100644 --- a/programs/drift/src/math/constants.rs +++ b/programs/drift/src/math/constants.rs @@ -186,7 +186,7 @@ pub const MAX_LIQUIDATION_SLIPPAGE_U128: u128 = 10_000; // expo = -2 pub const MAX_MARK_TWAP_DIVERGENCE: u128 = 500_000; // expo = -3 pub const MAX_MARGIN_RATIO: u32 = MARGIN_PRECISION; // 1x leverage -pub const MIN_MARGIN_RATIO: u32 = MARGIN_PRECISION / 50; // 50x leverage +pub const MIN_MARGIN_RATIO: u32 = 125; // 80x leverage pub const HIGH_LEVERAGE_MIN_MARGIN_RATIO: u32 = MARGIN_PRECISION / 200; // 200x leverage pub const MAX_BID_ASK_INVENTORY_SKEW_FACTOR: u64 = 10 * BID_ASK_SPREAD_PRECISION; From fd54c991cdaedceddd502caaa41252b8005dd6f7 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 21:03:24 +0000 Subject: [PATCH 106/216] sdk: release v2.134.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 5d3a8cab27..8b53e62490 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.5 \ No newline at end of file +2.134.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index f0727bc524..cefb690d5d 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.5", + "version": "2.134.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From b42727c62fc71e6e0506b198651ae42b8288fd1e Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 13 Aug 2025 17:36:43 -0400 Subject: [PATCH 107/216] program: rm-burn-lp-shares-invariant (#1816) * program: rm-burn-lp-shares-invariant * update changelog --- CHANGELOG.md | 1 + programs/drift/src/controller/lp.rs | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b226a2cd3..7dd76b6ee3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- program: remove burn lp shares invariant ([#1816](https://github.com/drift-labs/protocol-v2/pull/1816)) - program: correct fee tier 5 volume requirement ([#1800](https://github.com/drift-labs/protocol-v2/pull/1800)) - program: fix small number mark-twap-integer-bias ([#1783](https://github.com/drift-labs/protocol-v2/pull/1783)) diff --git a/programs/drift/src/controller/lp.rs b/programs/drift/src/controller/lp.rs index f179b111e2..9dc8e3b489 100644 --- a/programs/drift/src/controller/lp.rs +++ b/programs/drift/src/controller/lp.rs @@ -249,15 +249,6 @@ pub fn burn_lp_shares( .base_asset_amount_with_unsettled_lp .safe_add(position.remainder_base_asset_amount.cast()?)?; if shares_to_burn as u128 == market.amm.user_lp_shares && unsettled_remainder != 0 { - crate::validate!( - unsettled_remainder.unsigned_abs() <= market.amm.order_step_size as u128, - ErrorCode::UnableToBurnLPTokens, - "unsettled baa on final burn too big rel to stepsize {}: {} (remainder:{})", - market.amm.order_step_size, - market.amm.base_asset_amount_with_unsettled_lp, - position.remainder_base_asset_amount - )?; - // sub bc lps take the opposite side of the user position.remainder_base_asset_amount = position .remainder_base_asset_amount From 15543c37834ef5681f484aa356ab7fff3ee1f141 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 13 Aug 2025 17:58:21 -0400 Subject: [PATCH 108/216] fix test and cargo fmt --- programs/drift/src/math/amm.rs | 5 ++++- tests/highLeverageMode.ts | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/programs/drift/src/math/amm.rs b/programs/drift/src/math/amm.rs index a18560e881..0407f627cf 100644 --- a/programs/drift/src/math/amm.rs +++ b/programs/drift/src/math/amm.rs @@ -129,7 +129,10 @@ pub fn update_mark_twap_crank( .last_mark_price_twap_5min .cast::()? .safe_sub(amm.historical_oracle_data.last_oracle_price_twap_5min)? - .clamp(-oracle_price_data.price/100, oracle_price_data.price/100); + .clamp( + -oracle_price_data.price / 100, + oracle_price_data.price / 100, + ); if best_bid_price >= oracle_price_data.price.safe_add(market_basis)?.cast()? { best_bid_price = best_ask_price; diff --git a/tests/highLeverageMode.ts b/tests/highLeverageMode.ts index 148c41ce92..2cfbb4ef2b 100644 --- a/tests/highLeverageMode.ts +++ b/tests/highLeverageMode.ts @@ -221,7 +221,7 @@ describe('max leverage order params', () => { let leverage = driftClient.getUser().getLeverage().toNumber() / 10000; console.log('leverage', leverage); - assert(leverage === 99.8999); + assert(leverage === 99.9); await driftClient.cancelOrderByUserId(1); @@ -238,7 +238,7 @@ describe('max leverage order params', () => { leverage = driftClient.getUser().getLeverage().toNumber() / 10000; console.log('leverage', leverage); - assert(leverage === 99.8999); + assert(leverage === 99.9); await driftClient.cancelOrderByUserId(1); }); From c47e9e9320e27952d9d10d70efc5aa253119d4b3 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 13 Aug 2025 18:48:12 -0400 Subject: [PATCH 109/216] fix anchor tests --- tests/liquidityProvider.ts | 2 +- tests/maxLeverageOrderParams.ts | 4 ++-- tests/perpLpRiskMitigation.ts | 2 +- tests/settlePNLInvariant.ts | 9 +++------ tests/spotDepositWithdraw.ts | 2 +- tests/spotDepositWithdraw22.ts | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/liquidityProvider.ts b/tests/liquidityProvider.ts index c865579d36..d2a895d310 100644 --- a/tests/liquidityProvider.ts +++ b/tests/liquidityProvider.ts @@ -375,7 +375,7 @@ describe('liquidity providing', () => { const newInitMarginReq = driftClientUser.getInitialMarginRequirement(); console.log(initMarginReq.toString(), '->', newInitMarginReq.toString()); - assert(newInitMarginReq.eq(new BN(9284008))); // 8284008 + $1 + assert(newInitMarginReq.eq(new BN(9283999))); // 8284008 + $1 // ensure margin calcs didnt modify user position const _position = driftClientUser.getPerpPosition(0); diff --git a/tests/maxLeverageOrderParams.ts b/tests/maxLeverageOrderParams.ts index 4695105240..d095740f56 100644 --- a/tests/maxLeverageOrderParams.ts +++ b/tests/maxLeverageOrderParams.ts @@ -209,7 +209,7 @@ describe('max leverage order params', () => { ); let leverage = driftClient.getUser().getLeverage().toNumber() / 10000; - assert(leverage === 4.9949); + assert(leverage === 4.995); await driftClient.cancelOrderByUserId(1); @@ -224,7 +224,7 @@ describe('max leverage order params', () => { ); leverage = driftClient.getUser().getLeverage().toNumber() / 10000; - assert(leverage === 4.9949); + assert(leverage === 4.995); await driftClient.cancelOrderByUserId(1); }); diff --git a/tests/perpLpRiskMitigation.ts b/tests/perpLpRiskMitigation.ts index 12b8037f31..6b9a0a84c8 100644 --- a/tests/perpLpRiskMitigation.ts +++ b/tests/perpLpRiskMitigation.ts @@ -397,7 +397,7 @@ describe('lp risk mitigation', () => { 'driftClientUser.getFreeCollateral()=', driftClientUser.getFreeCollateral().toString() ); - assert(driftClientUser.getFreeCollateral().eq(new BN('4761073360'))); + assert(driftClientUser.getFreeCollateral().eq(new BN('4761073597'))); // some user goes long (lp should get more short) try { await adjustOraclePostSwap( diff --git a/tests/settlePNLInvariant.ts b/tests/settlePNLInvariant.ts index f51bbbefbd..9fb46e71e7 100644 --- a/tests/settlePNLInvariant.ts +++ b/tests/settlePNLInvariant.ts @@ -309,11 +309,8 @@ describe('spot deposit and withdraw', () => { }); it('Second User Deposit SOL', async () => { - [ - secondUserDriftClient, - secondUserDriftClientWSOLAccount, - secondUserDriftClientUSDCAccount, - ] = await createUserWithUSDCAndWSOLAccount( + [secondUserDriftClient, secondUserDriftClientWSOLAccount] = + await createUserWithUSDCAndWSOLAccount( bankrunContextWrapper, usdcMint, chProgram, @@ -336,7 +333,7 @@ describe('spot deposit and withdraw', () => { const spotMarket = await admin.getSpotMarketAccount(marketIndex); assert(spotMarket.depositBalance.eq(SPOT_MARKET_BALANCE_PRECISION)); console.log(spotMarket.historicalOracleData); - assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.gt(ZERO)); + assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.eq(ZERO)); assert( spotMarket.historicalOracleData.lastOraclePrice.eq( new BN(30 * PRICE_PRECISION.toNumber()) diff --git a/tests/spotDepositWithdraw.ts b/tests/spotDepositWithdraw.ts index 71b412b9b6..c24a2900d5 100644 --- a/tests/spotDepositWithdraw.ts +++ b/tests/spotDepositWithdraw.ts @@ -340,7 +340,7 @@ describe('spot deposit and withdraw', () => { const spotMarket = await admin.getSpotMarketAccount(marketIndex); assert(spotMarket.depositBalance.eq(SPOT_MARKET_BALANCE_PRECISION)); console.log(spotMarket.historicalOracleData); - assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.gt(ZERO)); + assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.eq(ZERO)); assert( spotMarket.historicalOracleData.lastOraclePrice.eq( new BN(30 * PRICE_PRECISION.toNumber()) diff --git a/tests/spotDepositWithdraw22.ts b/tests/spotDepositWithdraw22.ts index 5bcd5cf5f1..77f5e2bf09 100644 --- a/tests/spotDepositWithdraw22.ts +++ b/tests/spotDepositWithdraw22.ts @@ -352,7 +352,7 @@ describe('spot deposit and withdraw 22', () => { const spotMarket = await admin.getSpotMarketAccount(marketIndex); assert(spotMarket.depositBalance.eq(SPOT_MARKET_BALANCE_PRECISION)); console.log(spotMarket.historicalOracleData); - assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.gt(ZERO)); + assert(spotMarket.historicalOracleData.lastOraclePriceTwapTs.eq(ZERO)); assert( spotMarket.historicalOracleData.lastOraclePrice.eq( new BN(30 * PRICE_PRECISION.toNumber()) From 835c1d0ac771428d8bd2c166b899b1e659201401 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Wed, 13 Aug 2025 18:57:12 -0400 Subject: [PATCH 110/216] yarn prettify:fix --- tests/settlePNLInvariant.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/settlePNLInvariant.ts b/tests/settlePNLInvariant.ts index 9fb46e71e7..85cffe3914 100644 --- a/tests/settlePNLInvariant.ts +++ b/tests/settlePNLInvariant.ts @@ -311,16 +311,16 @@ describe('spot deposit and withdraw', () => { it('Second User Deposit SOL', async () => { [secondUserDriftClient, secondUserDriftClientWSOLAccount] = await createUserWithUSDCAndWSOLAccount( - bankrunContextWrapper, - usdcMint, - chProgram, - solAmount, - ZERO, - marketIndexes, - spotMarketIndexes, - oracleInfos, - bulkAccountLoader - ); + bankrunContextWrapper, + usdcMint, + chProgram, + solAmount, + ZERO, + marketIndexes, + spotMarketIndexes, + oracleInfos, + bulkAccountLoader + ); const marketIndex = 1; const txSig = await secondUserDriftClient.deposit( From f255610b7bba1ac0766312200ce419eeef0668fb Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 13 Aug 2025 16:13:17 -0700 Subject: [PATCH 111/216] reenable settle_pnl mode test --- programs/drift/src/state/settle_pnl_mode/tests.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/drift/src/state/settle_pnl_mode/tests.rs b/programs/drift/src/state/settle_pnl_mode/tests.rs index 58bb96a8e8..a53c2b5bed 100644 --- a/programs/drift/src/state/settle_pnl_mode/tests.rs +++ b/programs/drift/src/state/settle_pnl_mode/tests.rs @@ -9,10 +9,10 @@ mod test { assert_eq!(result, Err(ErrorCode::DefaultError)); } - // #[test] - // fn test_try_settle_returns_ok() { - // let mode = SettlePnlMode::TrySettle; - // let result = mode.result(ErrorCode::DefaultError, 0, "Try settle error"); - // assert_eq!(result, Ok(())); - // } + #[test] + fn test_try_settle_returns_ok() { + let mode = SettlePnlMode::TrySettle; + let result = mode.result(ErrorCode::DefaultError, 0, "Try settle error"); + assert_eq!(result, Ok(())); + } } From 34457c201f0f0a78e334b7d3e2e516c7dab70a3b Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 13 Aug 2025 16:22:01 -0700 Subject: [PATCH 112/216] v2.134.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 4 ++-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dd76b6ee3..ce9f8838ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +### Fixes + +### Breaking + +## [2.134.0] - 2025-08-13 + +### Features + - program: add new settle pnl invariants ([#1812](https://github.com/drift-labs/protocol-v2/pull/1812)) - program: add update_perp_market_pnl_pool ([#1810](https://github.com/drift-labs/protocol-v2/pull/1810)) - program: increase min margin ratio invariant constant ([#1802](https://github.com/drift-labs/protocol-v2/pull/1802)) diff --git a/Cargo.lock b/Cargo.lock index 23ba8c26d3..6bdb6a8f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.133.0" +version = "2.134.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 35e5d51bda..0ff0326832 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.133.0" +version = "2.134.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index cefb690d5d..74961e655c 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0-beta.6", + "version": "2.134.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 12654be9e1..1c14a231ae 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.133.0", + "version": "2.134.0", "name": "drift", "instructions": [ { @@ -16113,4 +16113,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} \ No newline at end of file +} From 4578fa1b4eee18094818bbda310fb937faff0366 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Aug 2025 23:30:14 +0000 Subject: [PATCH 113/216] sdk: release v2.135.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8b53e62490..576117cba1 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.134.0-beta.6 \ No newline at end of file +2.135.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 74961e655c..e3bca90ba8 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.134.0", + "version": "2.135.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 4e6dd2fa4a57ca7ce8e20c26e4b803c1d3ec70e2 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Mon, 18 Aug 2025 15:16:33 +0800 Subject: [PATCH 114/216] Merge pull request #1820 from drift-labs/chester/fix-zod --- bun.lock | 1075 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 5 +- sdk/bun.lock | 3 + sdk/package.json | 3 +- 4 files changed, 1083 insertions(+), 3 deletions(-) create mode 100644 bun.lock diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000000..7fa155ea36 --- /dev/null +++ b/bun.lock @@ -0,0 +1,1075 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "dependencies": { + "@ellipsis-labs/phoenix-sdk": "1.4.2", + "@pythnetwork/pyth-solana-receiver": "0.8.0", + "@switchboard-xyz/common": "3.0.14", + "@switchboard-xyz/on-demand": "2.4.1", + "anchor-bankrun": "0.3.0", + "chai-bn": "0.2.2", + "csvtojson": "2.0.10", + "dotenv": "16.4.5", + "json2csv": "5.0.7", + "nanoid": "3.3.4", + "rpc-websockets": "7.5.1", + "solana-bankrun": "0.3.0", + "zod": "^4.0.17", + "zstddec": "0.1.0", + }, + "devDependencies": { + "@coral-xyz/anchor": "0.29.0", + "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1", + "@project-serum/common": "0.0.1-beta.3", + "@project-serum/serum": "0.13.65", + "@pythnetwork/client": "2.21.0", + "@pythnetwork/price-service-client": "1.9.0", + "@solana/spl-token": "0.4.13", + "@solana/web3.js": "1.73.2", + "@types/bn.js": "5.1.6", + "@types/chai": "5.0.0", + "@types/mocha": "8.2.3", + "@typescript-eslint/eslint-plugin": "6.21.0", + "@typescript-eslint/parser": "6.21.0", + "chai": "4.4.1", + "eslint": "8.57.0", + "eslint-config-prettier": "8.3.0", + "eslint-plugin-prettier": "3.4.0", + "husky": "7.0.4", + "prettier": "3.0.1", + "typedoc": "0.23.23", + "typescript": "5.4.5", + }, + }, + }, + "packages": { + "@babel/runtime": ["@babel/runtime@7.28.3", "", {}, "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA=="], + + "@coral-xyz/anchor": ["@coral-xyz/anchor@0.29.0", "", { "dependencies": { "@coral-xyz/borsh": "^0.29.0", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA=="], + + "@coral-xyz/anchor-30": ["@coral-xyz/anchor@0.30.1", "", { "dependencies": { "@coral-xyz/anchor-errors": "^0.30.1", "@coral-xyz/borsh": "^0.30.1", "@noble/hashes": "^1.3.1", "@solana/web3.js": "^1.68.0", "bn.js": "^5.1.2", "bs58": "^4.0.1", "buffer-layout": "^1.2.2", "camelcase": "^6.3.0", "cross-fetch": "^3.1.5", "crypto-hash": "^1.3.0", "eventemitter3": "^4.0.7", "pako": "^2.0.3", "snake-case": "^3.0.4", "superstruct": "^0.15.4", "toml": "^3.0.0" } }, "sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ=="], + + "@coral-xyz/anchor-errors": ["@coral-xyz/anchor-errors@0.30.1", "", {}, "sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ=="], + + "@coral-xyz/borsh": ["@coral-xyz/borsh@0.29.0", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ=="], + + "@ellipsis-labs/phoenix-sdk": ["@ellipsis-labs/phoenix-sdk@1.4.2", "", { "dependencies": { "@metaplex-foundation/beet": "^0.7.1", "@metaplex-foundation/rustbin": "^0.3.1", "@metaplex-foundation/solita": "^0.12.2", "@solana/spl-token": "^0.3.7", "@types/node": "^18.11.13", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^5.0.0" } }, "sha512-7Rf2aWHZwuLX8jcrNSRUDf2aHuBnBzsDBN4GzClTdJYVGo4uQzf9ixju5J3apZ+xkQ6qvrEVYOXtogdgOhJFvw=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@2.1.4", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="], + + "@eslint/js": ["@eslint/js@8.57.0", "", {}, "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g=="], + + "@grpc/grpc-js": ["@grpc/grpc-js@1.13.4", "", { "dependencies": { "@grpc/proto-loader": "^0.7.13", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg=="], + + "@grpc/proto-loader": ["@grpc/proto-loader@0.7.15", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ=="], + + "@humanwhocodes/config-array": ["@humanwhocodes/config-array@0.11.14", "", { "dependencies": { "@humanwhocodes/object-schema": "^2.0.2", "debug": "^4.3.1", "minimatch": "^3.0.5" } }, "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/object-schema": ["@humanwhocodes/object-schema@2.0.3", "", {}, "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="], + + "@isaacs/ttlcache": ["@isaacs/ttlcache@1.4.1", "", {}, "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA=="], + + "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], + + "@metaplex-foundation/beet": ["@metaplex-foundation/beet@0.7.2", "", { "dependencies": { "ansicolors": "^0.3.2", "assert": "^2.1.0", "bn.js": "^5.2.0", "debug": "^4.3.3" } }, "sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg=="], + + "@metaplex-foundation/beet-solana": ["@metaplex-foundation/beet-solana@0.3.1", "", { "dependencies": { "@metaplex-foundation/beet": ">=0.1.0", "@solana/web3.js": "^1.56.2", "bs58": "^5.0.0", "debug": "^4.3.4" } }, "sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g=="], + + "@metaplex-foundation/rustbin": ["@metaplex-foundation/rustbin@0.3.5", "", { "dependencies": { "debug": "^4.3.3", "semver": "^7.3.7", "text-table": "^0.2.0", "toml": "^3.0.0" } }, "sha512-m0wkRBEQB/8krwMwKBvFugufZtYwMXiGHud2cTDAv+aGXK4M90y0Hx67/wpu+AqqoQfdV8VM9YezUOHKD+Z5kA=="], + + "@metaplex-foundation/solita": ["@metaplex-foundation/solita@0.12.2", "", { "dependencies": { "@metaplex-foundation/beet": "^0.4.0", "@metaplex-foundation/beet-solana": "^0.3.0", "@metaplex-foundation/rustbin": "^0.3.0", "@solana/web3.js": "^1.36.0", "camelcase": "^6.2.1", "debug": "^4.3.3", "js-sha256": "^0.9.0", "prettier": "^2.5.1", "snake-case": "^3.0.4", "spok": "^1.4.3" }, "bin": { "solita": "dist/src/cli/solita.js" } }, "sha512-oczMfE43NNHWweSqhXPTkQBUbap/aAiwjDQw8zLKNnd/J8sXr/0+rKcN5yJIEgcHeKRkp90eTqkmt2WepQc8yw=="], + + "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], + + "@noble/ed25519": ["@noble/ed25519@1.7.5", "", {}, "sha512-xuS0nwRMQBvSxDa7UxMb61xTiH3MxTgUfhyPUALVIe0FlOAz4sjELwyDRyUvqeEYfRSG9qNjFIycqLZppg4RSA=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@noble/secp256k1": ["@noble/secp256k1@1.7.2", "", {}, "sha512-/qzwYl5eFLH8OWIecQWM31qld2g1NfjgylK+TNhqtaUKP37Nm+Y+z30Fjhw0Ct8p9yCQEm2N3W/AckdIb3SMcQ=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@project-serum/anchor": ["@project-serum/anchor@0.11.1", "", { "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" } }, "sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA=="], + + "@project-serum/borsh": ["@project-serum/borsh@0.2.5", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.2.0" } }, "sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q=="], + + "@project-serum/common": ["@project-serum/common@0.0.1-beta.3", "", { "dependencies": { "@project-serum/serum": "^0.13.21", "bn.js": "^5.1.2", "superstruct": "0.8.3" }, "peerDependencies": { "@solana/web3.js": "^0.87.1" } }, "sha512-gnQE/eUydTtto5okCgLWj1M97R9RRPJqnhKklikYI7jP/pnNhDmngSXC/dmfzED2GXSJEIKNIlxVw1k+E2Aw3w=="], + + "@project-serum/serum": ["@project-serum/serum@0.13.65", "", { "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" } }, "sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + + "@pythnetwork/client": ["@pythnetwork/client@2.21.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@coral-xyz/borsh": "^0.28.0", "buffer": "^6.0.1" }, "peerDependencies": { "@solana/web3.js": "^1.30.2" } }, "sha512-jqUuPLuVKRNUsZfwLuvK/MwnJ3LIrIxBNoz43xt0fjvVuH5QyTlz51ek76CkeKfCbomGKe41Vq7bvn8aqWVOGA=="], + + "@pythnetwork/price-service-client": ["@pythnetwork/price-service-client@1.9.0", "", { "dependencies": { "@pythnetwork/price-service-sdk": "*", "@types/ws": "^8.5.3", "axios": "^1.5.1", "axios-retry": "^3.8.0", "isomorphic-ws": "^4.0.1", "ts-log": "^2.2.4", "ws": "^8.6.0" } }, "sha512-SLm3IFcfmy9iMqHeT4Ih6qMNZhJEefY14T9yTlpsH2D/FE5+BaGGnfcexUifVlfH6M7mwRC4hEFdNvZ6ebZjJg=="], + + "@pythnetwork/price-service-sdk": ["@pythnetwork/price-service-sdk@1.8.0", "", { "dependencies": { "bn.js": "^5.2.1" } }, "sha512-tFZ1thj3Zja06DzPIX2dEWSi7kIfIyqreoywvw5NQ3Z1pl5OJHQGMEhxt6Li3UCGSp2ooYZS9wl8/8XfrfrNSA=="], + + "@pythnetwork/pyth-solana-receiver": ["@pythnetwork/pyth-solana-receiver@0.8.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@noble/hashes": "^1.4.0", "@pythnetwork/price-service-sdk": ">=1.6.0", "@pythnetwork/solana-utils": "*", "@solana/web3.js": "^1.90.0" } }, "sha512-5lhLtggAqsiHtffTPM8vcKJmhBdxzidBmiNNUlqPyg9XmhZ4Z+roY0dfzluEoX5xer9rEA1ThsBpX0bG1DRIGA=="], + + "@pythnetwork/solana-utils": ["@pythnetwork/solana-utils@0.5.0", "", { "dependencies": { "@coral-xyz/anchor": "^0.29.0", "@solana/web3.js": "^1.90.0", "bs58": "^5.0.0", "jito-ts": "^3.0.1", "ts-log": "^2.2.7" } }, "sha512-6F99H/FiLNcleLlagBbM5YKPEp7QGo+bk5IUf+PPfEdk64sflRVq74M4rRghm+LpK4TuTbCA2Gh4aKJUZLG08Q=="], + + "@solana/buffer-layout": ["@solana/buffer-layout@4.0.1", "", { "dependencies": { "buffer": "~6.0.3" } }, "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA=="], + + "@solana/buffer-layout-utils": ["@solana/buffer-layout-utils@0.2.0", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/web3.js": "^1.32.0", "bigint-buffer": "^1.1.5", "bignumber.js": "^9.0.1" } }, "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g=="], + + "@solana/codecs": ["@solana/codecs@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/options": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ=="], + + "@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + + "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + + "@solana/codecs-numbers": ["@solana/codecs-numbers@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg=="], + + "@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], + + "@solana/errors": ["@solana/errors@2.3.0", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^14.0.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ=="], + + "@solana/options": ["@solana/options@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-data-structures": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/codecs-strings": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA=="], + + "@solana/spl-token": ["@solana/spl-token@0.4.13", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-group": "^0.0.7", "@solana/spl-token-metadata": "^0.1.6", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.95.5" } }, "sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w=="], + + "@solana/spl-token-group": ["@solana/spl-token-group@0.0.7", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug=="], + + "@solana/spl-token-metadata": ["@solana/spl-token-metadata@0.1.6", "", { "dependencies": { "@solana/codecs": "2.0.0-rc.1" }, "peerDependencies": { "@solana/web3.js": "^1.95.3" } }, "sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA=="], + + "@solana/web3.js": ["@solana/web3.js@1.73.2", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/ed25519": "^1.7.0", "@noble/hashes": "^1.1.2", "@noble/secp256k1": "^1.6.3", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.1", "fast-stable-stringify": "^1.0.0", "jayson": "^3.4.4", "node-fetch": "2", "rpc-websockets": "^7.5.0", "superstruct": "^0.14.2" } }, "sha512-9WACF8W4Nstj7xiDw3Oom22QmrhBh0VyZyZ7JvvG3gOxLWLlX3hvm5nPVJOGcCE/9fFavBbCUb5A6CIuvMGdoA=="], + + "@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="], + + "@switchboard-xyz/common": ["@switchboard-xyz/common@3.0.14", "", { "dependencies": { "@solana/web3.js": "^1.98.0", "axios": "^1.8.3", "big.js": "^6.2.2", "bn.js": "^5.2.1", "bs58": "^6.0.0", "buffer": "^6.0.3", "decimal.js": "^10.4.3", "js-sha256": "^0.11.0", "protobufjs": "^7.4.0", "yaml": "^2.6.1" } }, "sha512-LpxzEywO0DjPYIgPzQYkf32C7agwW4YRsPN6BcIvYrw0iJdDMtPZ3SQfIGHLSlD1fwvn2KLUYuGaKegeq4aBTw=="], + + "@switchboard-xyz/on-demand": ["@switchboard-xyz/on-demand@2.4.1", "", { "dependencies": { "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1", "@isaacs/ttlcache": "^1.4.1", "@switchboard-xyz/common": ">=3.0.0", "axios": "^1.8.3", "bs58": "^6.0.0", "buffer": "^6.0.3", "js-yaml": "^4.1.0" } }, "sha512-eSlBp+c8lxpcSgh0/2xK8OaLHPziTSZlcs8V96gZGdiCJz1KgWJRNE1qnIJDOwaGdFecZdwcmajfQRtLRLED3w=="], + + "@types/bn.js": ["@types/bn.js@5.1.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w=="], + + "@types/chai": ["@types/chai@5.0.0", "", {}, "sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA=="], + + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mocha": ["@types/mocha@8.2.3", "", {}, "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw=="], + + "@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@types/semver": ["@types/semver@7.7.0", "", {}, "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA=="], + + "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], + + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@6.21.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@6.21.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" } }, "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@6.21.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@6.21.0", "", {}, "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" } }, "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@6.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@6.21.0", "", { "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" } }, "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": { "JSONStream": "./bin.js" } }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "anchor-bankrun": ["anchor-bankrun@0.3.0", "", { "peerDependencies": { "@coral-xyz/anchor": "^0.28.0", "@solana/web3.js": "^1.78.4", "solana-bankrun": "^0.2.0" } }, "sha512-PYBW5fWX+iGicIS5MIM/omhk1tQPUc0ELAnI/IkLKQJ6d75De/CQRh8MF2bU/TgGyFi6zEel80wUe3uRol9RrQ=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], + + "assert": ["assert@2.1.0", "", { "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", "object-is": "^1.1.5", "object.assign": "^4.1.4", "util": "^0.12.5" } }, "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw=="], + + "assertion-error": ["assertion-error@1.1.0", "", {}, "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axios": ["axios@1.11.0", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA=="], + + "axios-retry": ["axios-retry@3.9.1", "", { "dependencies": { "@babel/runtime": "^7.15.4", "is-retry-allowed": "^2.2.0" } }, "sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base-x": ["base-x@3.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "big.js": ["big.js@6.2.2", "", {}, "sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ=="], + + "bigint-buffer": ["bigint-buffer@1.1.5", "", { "dependencies": { "bindings": "^1.3.0" } }, "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA=="], + + "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], + + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + + "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], + + "bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], + + "borsh": ["borsh@0.7.0", "", { "dependencies": { "bn.js": "^5.2.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" } }, "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA=="], + + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "buffer-layout": ["buffer-layout@1.2.2", "", {}, "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA=="], + + "bufferutil": ["bufferutil@4.0.9", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw=="], + + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + + "chai": ["chai@4.4.1", "", { "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.0.8" } }, "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g=="], + + "chai-bn": ["chai-bn@0.2.2", "", { "peerDependencies": { "bn.js": "^4.11.0", "chai": "^4.0.0" } }, "sha512-MzjelH0p8vWn65QKmEq/DLBG1Hle4WeyqT79ANhXZhn/UxRWO0OogkAxi5oGGtfzwU9bZR8mvbvYdoqNVWQwFg=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "check-error": ["check-error@1.0.3", "", { "dependencies": { "get-func-name": "^2.0.2" } }, "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@6.2.1", "", {}, "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "crypto-hash": ["crypto-hash@1.3.0", "", {}, "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg=="], + + "csvtojson": ["csvtojson@2.0.10", "", { "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.3", "strip-bom": "^2.0.0" }, "bin": { "csvtojson": "./bin/csvtojson" } }, "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + + "deep-eql": ["deep-eql@4.1.4", "", { "dependencies": { "type-detect": "^4.0.0" } }, "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "delay": ["delay@5.0.0", "", {}, "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], + + "doctrine": ["doctrine@3.0.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="], + + "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], + + "dotenv": ["dotenv@16.4.5", "", {}, "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es6-promise": ["es6-promise@4.2.8", "", {}, "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="], + + "es6-promisify": ["es6-promisify@5.0.0", "", { "dependencies": { "es6-promise": "^4.0.3" } }, "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@8.57.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.0", "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" } }, "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ=="], + + "eslint-config-prettier": ["eslint-config-prettier@8.3.0", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew=="], + + "eslint-plugin-prettier": ["eslint-plugin-prettier@3.4.0", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0" }, "peerDependencies": { "eslint": ">=5.0.0", "prettier": ">=1.13.0" } }, "sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw=="], + + "eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="], + + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + + "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-stable-stringify": ["fast-stable-stringify@1.0.0", "", {}, "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag=="], + + "fastestsmallesttextencoderdecoder": ["fastestsmallesttextencoderdecoder@1.0.22", "", {}, "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw=="], + + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], + + "file-entry-cache": ["file-entry-cache@6.0.1", "", { "dependencies": { "flat-cache": "^3.0.4" } }, "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="], + + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "find": ["find@0.3.0", "", { "dependencies": { "traverse-chain": "~0.1.0" } }, "sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw=="], + + "find-process": ["find-process@1.4.11", "", { "dependencies": { "chalk": "~4.1.2", "commander": "^12.1.0", "loglevel": "^1.9.2" }, "bin": { "find-process": "bin/find-process.js" } }, "sha512-mAOh9gGk9WZ4ip5UjV0o6Vb4SrfnAmtsFNzkMRH9HQiFXVQnDyQFrSHTK5UoG6E+KV+s+cIznbtwpfN41l2nFA=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@3.2.0", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="], + + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], + + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-func-name": ["get-func-name@2.0.2", "", {}, "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@13.24.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="], + + "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + + "husky": ["husky@7.0.4", "", { "bin": { "husky": "lib/bin.js" } }, "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "is-arguments": ["is-arguments@1.2.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-nan": ["is-nan@1.3.2", "", { "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" } }, "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-path-inside": ["is-path-inside@3.0.3", "", {}, "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-retry-allowed": ["is-retry-allowed@2.2.0", "", {}, "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-utf8": ["is-utf8@0.2.1", "", {}, "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "isomorphic-ws": ["isomorphic-ws@4.0.1", "", { "peerDependencies": { "ws": "*" } }, "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w=="], + + "jayson": ["jayson@3.7.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "JSONStream": "^1.3.5", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "lodash": "^4.17.20", "uuid": "^8.3.2", "ws": "^7.4.5" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ=="], + + "jito-ts": ["jito-ts@3.0.1", "", { "dependencies": { "@grpc/grpc-js": "^1.8.13", "@noble/ed25519": "^1.7.1", "@solana/web3.js": "~1.77.3", "agentkeepalive": "^4.3.0", "dotenv": "^16.0.3", "jayson": "^4.0.0", "node-fetch": "^2.6.7", "superstruct": "^1.0.3" } }, "sha512-TSofF7KqcwyaWGjPaSYC8RDoNBY1TPRNBHdrw24bdIi7mQ5bFEDdYK3D//llw/ml8YDvcZlgd644WxhjLTS9yg=="], + + "js-sha256": ["js-sha256@0.11.1", "", {}, "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + + "json2csv": ["json2csv@5.0.7", "", { "dependencies": { "commander": "^6.1.0", "jsonparse": "^1.3.1", "lodash.get": "^4.4.2" }, "bin": { "json2csv": "bin/json2csv.js" } }, "sha512-YRZbUnyaJZLZUJSRi2G/MqahCyRv9n/ds+4oIetjDF3jWQA7AG7iSeKTiZiCNqtMZM7HDyt0e/W6lEnoGEmMGA=="], + + "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], + + "lodash.get": ["lodash.get@4.4.2", "", {}, "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "loglevel": ["loglevel@1.9.2", "", {}, "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "loupe": ["loupe@2.3.7", "", { "dependencies": { "get-func-name": "^2.0.1" } }, "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA=="], + + "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], + + "lunr": ["lunr@2.3.9", "", {}, "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="], + + "marked": ["marked@4.3.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.4", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], + + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + + "object-is": ["object-is@1.1.6", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" } }, "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "pako": ["pako@2.1.0", "", {}, "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "pathval": ["pathval@1.1.1", "", {}, "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ=="], + + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "prettier": ["prettier@3.0.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ=="], + + "prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="], + + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "rpc-websockets": ["rpc-websockets@7.5.1", "", { "dependencies": { "@babel/runtime": "^7.17.2", "eventemitter3": "^4.0.7", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "shiki": ["shiki@0.11.1", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-oniguruma": "^1.6.1", "vscode-textmate": "^6.0.0" } }, "sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA=="], + + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + + "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], + + "solana-bankrun": ["solana-bankrun@0.3.0", "", { "dependencies": { "@solana/web3.js": "^1.68.0", "bs58": "^4.0.1" }, "optionalDependencies": { "solana-bankrun-darwin-arm64": "0.3.0", "solana-bankrun-darwin-universal": "0.3.0", "solana-bankrun-darwin-x64": "0.3.0", "solana-bankrun-linux-x64-gnu": "0.3.0", "solana-bankrun-linux-x64-musl": "0.3.0" } }, "sha512-YkH7sa8TB/AoRPzG17CXJtYsRIQHEkEqGLz1Vwc13taXhDBkjO7z6NI5JYw7n0ybRymDHwMYTc7sd+5J40TyVQ=="], + + "solana-bankrun-darwin-arm64": ["solana-bankrun-darwin-arm64@0.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+NbDncf0U6l3knuacRBiqpjZ2DSp+5lZaAU518gH7/x6qubbui/d000STaIBK+uNTPBS/AL/bCN+7PkXqmA3lA=="], + + "solana-bankrun-darwin-universal": ["solana-bankrun-darwin-universal@0.3.0", "", { "os": "darwin" }, "sha512-1/F0xdMa4qvc5o6z16FCCbZ5jbdvKvxpx5kyPcMWRiRPwyvi+zltMxciPAYMlg3wslQqGz88uFhrBEzq2eTumQ=="], + + "solana-bankrun-darwin-x64": ["solana-bankrun-darwin-x64@0.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U6CANjkmMl+lgNA7UH0GKs5V7LtVIUDzJBZefGGqLfqUNv3EjA/PrrToM0hAOWJgkxSwdz6zW+p5sw5FmnbXtg=="], + + "solana-bankrun-linux-x64-gnu": ["solana-bankrun-linux-x64-gnu@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-qJSkCFs0k2n4XtTnyxGMiZsuqO2TiqTYgWjQ+3mZhGNUAMys/Vq8bd7/SyBm6RR7EfVuRXRxZvh+F8oKZ77V4w=="], + + "solana-bankrun-linux-x64-musl": ["solana-bankrun-linux-x64-musl@0.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-xsS2CS2xb1Sw4ivNXM0gPz/qpW9BX0neSvt/pnok5L330Nu9xlTnKAY8FhzzqOP9P9sJlGRM787Y6d0yYwt6xQ=="], + + "spok": ["spok@1.5.5", "", { "dependencies": { "ansicolors": "~0.3.2", "find-process": "^1.4.7" } }, "sha512-IrJIXY54sCNFASyHPOY+jEirkiJ26JDqsGiI0Dvhwcnkl0PEWi1PSsrkYql0rzDw8LFVTcA7rdUCAJdE2HE+2Q=="], + + "stream-chain": ["stream-chain@2.2.5", "", {}, "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="], + + "stream-json": ["stream-json@1.9.1", "", { "dependencies": { "stream-chain": "^2.2.5" } }, "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@2.0.0", "", { "dependencies": { "is-utf8": "^0.2.0" } }, "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], + + "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], + + "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toml": ["toml@3.0.0", "", {}, "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w=="], + + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "traverse-chain": ["traverse-chain@0.1.0", "", {}, "sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg=="], + + "ts-api-utils": ["ts-api-utils@1.4.3", "", { "peerDependencies": { "typescript": ">=4.2.0" } }, "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw=="], + + "ts-log": ["ts-log@2.2.7", "", {}, "sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-detect": ["type-detect@4.1.0", "", {}, "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw=="], + + "type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], + + "typedoc": ["typedoc@0.23.23", "", { "dependencies": { "lunr": "^2.3.9", "marked": "^4.2.4", "minimatch": "^5.1.1", "shiki": "^0.11.1" }, "peerDependencies": { "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-cg1YQWj+/BU6wq74iott513U16fbrPCbyYs04PHZgvoKJIc6EY4xNobyDZh4KMfRGW8Yjv6wwIzQyoqopKOUGw=="], + + "typescript": ["typescript@5.4.5", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="], + + "undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="], + + "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], + + "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], + + "vscode-oniguruma": ["vscode-oniguruma@1.7.0", "", {}, "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA=="], + + "vscode-textmate": ["vscode-textmate@6.0.0", "", {}, "sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ=="], + + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "zod": ["zod@4.0.17", "", {}, "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ=="], + + "zstddec": ["zstddec@0.1.0", "", {}, "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="], + + "@coral-xyz/anchor-30/@coral-xyz/borsh": ["@coral-xyz/borsh@0.30.1", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ=="], + + "@ellipsis-labs/phoenix-sdk/@solana/spl-token": ["@solana/spl-token@0.3.11", "", { "dependencies": { "@solana/buffer-layout": "^4.0.0", "@solana/buffer-layout-utils": "^0.2.0", "@solana/spl-token-metadata": "^0.1.2", "buffer": "^6.0.3" }, "peerDependencies": { "@solana/web3.js": "^1.88.0" } }, "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ=="], + + "@ellipsis-labs/phoenix-sdk/@types/node": ["@types/node@18.19.123", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg=="], + + "@ellipsis-labs/phoenix-sdk/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@metaplex-foundation/beet-solana/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], + + "@metaplex-foundation/solita/@metaplex-foundation/beet": ["@metaplex-foundation/beet@0.4.0", "", { "dependencies": { "ansicolors": "^0.3.2", "bn.js": "^5.2.0", "debug": "^4.3.3" } }, "sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA=="], + + "@metaplex-foundation/solita/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@metaplex-foundation/solita/js-sha256": ["js-sha256@0.9.0", "", {}, "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="], + + "@metaplex-foundation/solita/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + + "@project-serum/anchor/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@project-serum/anchor/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], + + "@project-serum/anchor/js-sha256": ["js-sha256@0.9.0", "", {}, "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="], + + "@project-serum/common/superstruct": ["superstruct@0.8.3", "", { "dependencies": { "kind-of": "^6.0.2", "tiny-invariant": "^1.0.6" } }, "sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww=="], + + "@project-serum/serum/@solana/spl-token": ["@solana/spl-token@0.1.8", "", { "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" } }, "sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ=="], + + "@pythnetwork/client/@coral-xyz/borsh": ["@coral-xyz/borsh@0.28.0", "", { "dependencies": { "bn.js": "^5.1.2", "buffer-layout": "^1.2.0" }, "peerDependencies": { "@solana/web3.js": "^1.68.0" } }, "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@pythnetwork/solana-utils/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@pythnetwork/solana-utils/bs58": ["bs58@5.0.0", "", { "dependencies": { "base-x": "^4.0.0" } }, "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/codecs-data-structures/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/codecs-numbers/@solana/codecs-core": ["@solana/codecs-core@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw=="], + + "@solana/codecs-strings/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/errors/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "@solana/errors/commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], + + "@solana/options/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], + + "@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/web3.js/buffer": ["buffer@6.0.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ=="], + + "@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], + + "@switchboard-xyz/common/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@switchboard-xyz/common/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], + + "@switchboard-xyz/on-demand/bs58": ["bs58@6.0.0", "", { "dependencies": { "base-x": "^5.0.0" } }, "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "find-process/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "jito-ts/@solana/web3.js": ["@solana/web3.js@1.77.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.3.0", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", "node-fetch": "^2.6.7", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } }, "sha512-XdN0Lh4jdY7J8FYMyucxCwzn6Ga2Sr1DHDWRbqVzk7ZPmmpSPOVWHzO67X1cVT+jNi1D6gZi2tgjHgDPuj6e9Q=="], + + "jito-ts/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "jito-ts/superstruct": ["superstruct@1.0.4", "", {}, "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ=="], + + "solana-bankrun/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "typedoc/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + + "@ellipsis-labs/phoenix-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@ellipsis-labs/phoenix-sdk/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@metaplex-foundation/beet-solana/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@metaplex-foundation/solita/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@metaplex-foundation/solita/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@project-serum/anchor/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@project-serum/anchor/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@project-serum/anchor/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js": ["@solana/web3.js@1.98.4", "", { "dependencies": { "@babel/runtime": "^7.25.0", "@noble/curves": "^1.4.2", "@noble/hashes": "^1.4.0", "@solana/buffer-layout": "^4.0.1", "@solana/codecs-numbers": "^2.1.0", "agentkeepalive": "^4.5.0", "bn.js": "^5.2.1", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.1", "node-fetch": "^2.7.0", "rpc-websockets": "^9.0.2", "superstruct": "^2.0.2" } }, "sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw=="], + + "@project-serum/serum/@solana/spl-token/dotenv": ["dotenv@10.0.0", "", {}, "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@pythnetwork/solana-utils/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@pythnetwork/solana-utils/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@pythnetwork/solana-utils/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@pythnetwork/solana-utils/bs58/base-x": ["base-x@4.0.1", "", {}, "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@solana/buffer-layout-utils/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@solana/buffer-layout-utils/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@solana/codecs-core/@solana/errors/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/codecs-data-structures/@solana/errors/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/codecs-strings/@solana/errors/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/options/@solana/errors/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@switchboard-xyz/common/@solana/web3.js/bs58": ["bs58@4.0.1", "", { "dependencies": { "base-x": "^3.0.2" } }, "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@switchboard-xyz/common/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@switchboard-xyz/common/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@switchboard-xyz/common/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], + + "@switchboard-xyz/on-demand/bs58/base-x": ["base-x@5.0.1", "", {}, "sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "jito-ts/@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], + + "jito-ts/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "jito-ts/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "jito-ts/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "jito-ts/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "solana-bankrun/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "solana-bankrun/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "solana-bankrun/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "typedoc/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@metaplex-foundation/solita/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@project-serum/anchor/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@project-serum/anchor/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@project-serum/anchor/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@project-serum/anchor/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@project-serum/anchor/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson": ["jayson@4.2.0", "", { "dependencies": { "@types/connect": "^3.4.33", "@types/node": "^12.12.54", "@types/ws": "^7.4.4", "commander": "^2.20.3", "delay": "^5.0.0", "es6-promisify": "^5.0.0", "eyes": "^0.1.8", "isomorphic-ws": "^4.0.1", "json-stringify-safe": "^5.0.1", "stream-json": "^1.9.1", "uuid": "^8.3.2", "ws": "^7.5.10" }, "bin": { "jayson": "bin/jayson.js" } }, "sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/rpc-websockets": ["rpc-websockets@9.1.3", "", { "dependencies": { "@swc/helpers": "^0.5.11", "@types/uuid": "^8.3.4", "@types/ws": "^8.2.2", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "uuid": "^8.3.2", "ws": "^8.5.0" }, "optionalDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" } }, "sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/superstruct": ["superstruct@2.0.2", "", {}, "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@pythnetwork/solana-utils/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@solana/buffer-layout-utils/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@solana/codecs/@solana/codecs-numbers/@solana/errors/chalk": ["chalk@5.6.0", "", {}, "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ=="], + + "@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@switchboard-xyz/common/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "jito-ts/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "solana-bankrun/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "solana-bankrun/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "solana-bankrun/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "solana-bankrun/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "solana-bankrun/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@metaplex-foundation/beet-solana/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@metaplex-foundation/solita/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@project-serum/anchor/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + + "@pythnetwork/pyth-solana-receiver/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@pythnetwork/solana-utils/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@solana/buffer-layout-utils/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@switchboard-xyz/common/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "solana-bankrun/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + + "@project-serum/serum/@solana/spl-token/@solana/web3.js/jayson/@types/ws/@types/node": ["@types/node@24.3.0", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow=="], + } +} diff --git a/package.json b/package.json index 2d519c9f3c..9470e1191a 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ "dependencies": { "@ellipsis-labs/phoenix-sdk": "1.4.2", "@pythnetwork/pyth-solana-receiver": "0.8.0", - "@switchboard-xyz/on-demand": "2.4.1", "@switchboard-xyz/common": "3.0.14", + "@switchboard-xyz/on-demand": "2.4.1", "anchor-bankrun": "0.3.0", "chai-bn": "0.2.2", "csvtojson": "2.0.10", @@ -40,6 +40,7 @@ "nanoid": "3.3.4", "rpc-websockets": "7.5.1", "solana-bankrun": "0.3.0", + "zod": "4.0.17", "zstddec": "0.1.0" }, "scripts": { @@ -54,4 +55,4 @@ "engines": { "node": ">=12" } -} +} \ No newline at end of file diff --git a/sdk/bun.lock b/sdk/bun.lock index c77cb9180f..bb1e7df66d 100644 --- a/sdk/bun.lock +++ b/sdk/bun.lock @@ -29,6 +29,7 @@ "tweetnacl-util": "0.15.1", "uuid": "8.3.2", "yargs": "17.7.2", + "zod": "^4.0.17", "zstddec": "0.1.0", }, "devDependencies": { @@ -1138,6 +1139,8 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "zod": ["zod@4.0.17", "", {}, "sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ=="], + "zstddec": ["zstddec@0.1.0", "", {}, "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="], "@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], diff --git a/sdk/package.json b/sdk/package.json index e3bca90ba8..6cd085f477 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -63,6 +63,7 @@ "tweetnacl-util": "0.15.1", "uuid": "8.3.2", "yargs": "17.7.2", + "zod": "4.0.17", "zstddec": "0.1.0" }, "devDependencies": { @@ -97,4 +98,4 @@ "@solana/errors": "2.0.0-preview.4", "@solana/codecs-data-structures": "2.0.0-preview.4" } -} +} \ No newline at end of file From c716ba0540e5e060c9011fc30743a2bce6ea6b0e Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 07:21:27 +0000 Subject: [PATCH 115/216] sdk: release v2.135.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 4 ++-- sdk/yarn.lock | 5 +++++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 576117cba1..918d6bacec 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.0 \ No newline at end of file +2.135.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 6cd085f477..72779a8bed 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.0", + "version": "2.135.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", @@ -98,4 +98,4 @@ "@solana/errors": "2.0.0-preview.4", "@solana/codecs-data-structures": "2.0.0-preview.4" } -} \ No newline at end of file +} diff --git a/sdk/yarn.lock b/sdk/yarn.lock index dd6247d0f0..960207492a 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -4417,6 +4417,11 @@ zod@4.0.0-beta.20250505T195954: dependencies: "@zod/core" "0.11.6" +zod@4.0.17: + version "4.0.17" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.0.17.tgz#95931170715f73f7426c385c237b7477750d6c8d" + integrity sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ== + zstddec@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec" From e129aef4aeea9e0ea25536f105a3b1ebd5eea5f6 Mon Sep 17 00:00:00 2001 From: moosecat Date: Mon, 18 Aug 2025 09:13:16 -0700 Subject: [PATCH 116/216] mm oracle sdk change (#1806) * mm oracle sdk change * better conditional typing * DLOB bug fix * updated idl * rm getAmmBidAskPrice --- sdk/src/dlob/DLOB.ts | 162 ++++++++++++++++++++------------ sdk/src/dlob/DLOBSubscriber.ts | 2 +- sdk/src/dlob/orderBookLevels.ts | 14 +-- sdk/src/driftClient.ts | 30 +++--- sdk/src/idl/drift.json | 2 +- sdk/src/math/amm.ts | 94 ++++-------------- sdk/src/math/funding.ts | 22 ++++- sdk/src/math/market.ts | 26 ++--- sdk/src/math/orders.ts | 20 ++-- sdk/src/math/position.ts | 8 +- sdk/src/math/trade.ts | 52 ++++++---- sdk/src/oracles/types.ts | 11 +-- sdk/src/oracles/utils.ts | 11 +-- sdk/src/user.ts | 22 ++--- 14 files changed, 239 insertions(+), 237 deletions(-) diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 92fb572c22..5a3367912f 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -32,7 +32,7 @@ import { StateAccount, } from '../types'; import { isUserProtectedMaker } from '../math/userStatus'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { ProtectMakerParamsMap } from './types'; import { SlotSubscriber } from '../slot/SlotSubscriber'; import { UserMap } from '../userMap/userMap'; @@ -446,16 +446,20 @@ export class DLOB { return undefined; } - public findNodesToFill( + public findNodesToFill( marketIndex: number, fallbackBid: BN | undefined, fallbackAsk: BN | undefined, slot: number, ts: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, stateAccount: StateAccount, - marketAccount: PerpMarketAccount | SpotMarketAccount + marketAccount: T extends { spot: unknown } + ? SpotMarketAccount + : PerpMarketAccount ): NodeToFill[] { if (fillPaused(stateAccount, marketAccount)) { return []; @@ -572,11 +576,13 @@ export class DLOB { return Array.from(mergedNodesToFill.values()); } - public findRestingLimitOrderNodesToFill( + public findRestingLimitOrderNodesToFill( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, isAmmPaused: boolean, minAuctionDuration: number, makerRebateNumerator: number, @@ -656,11 +662,13 @@ export class DLOB { return nodesToFill; } - public findTakingNodesToFill( + public findTakingNodesToFill( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, isAmmPaused: boolean, minAuctionDuration: number, fallbackAsk: BN | undefined, @@ -782,17 +790,21 @@ export class DLOB { return nodesToFill; } - public findTakingNodesCrossingMakerNodes( + public findTakingNodesCrossingMakerNodes( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, takerNodeGenerator: Generator, makerNodeGeneratorFn: ( marketIndex: number, slot: number, marketType: MarketType, - oraclePriceData: OraclePriceData + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ) => Generator, doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean ): NodeToFill[] { @@ -878,10 +890,12 @@ export class DLOB { return nodesToFill; } - public findNodesCrossingFallbackLiquidity( - marketType: MarketType, + public findNodesCrossingFallbackLiquidity( + marketType: T, slot: number, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, nodeGenerator: Generator, doesCross: (nodePrice: BN | undefined) => boolean, minAuctionDuration: number @@ -997,11 +1011,13 @@ export class DLOB { return nodesToFill; } - *getTakingBids( + *getTakingBids( marketIndex: number, - marketType: MarketType, + marketType: T, slot: number, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { const marketTypeStr = getVariant(marketType) as MarketTypeStr; @@ -1032,11 +1048,13 @@ export class DLOB { ); } - *getTakingAsks( + *getTakingAsks( marketIndex: number, - marketType: MarketType, + marketType: T, slot: number, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { const marketTypeStr = getVariant(marketType) as MarketTypeStr; @@ -1138,11 +1156,13 @@ export class DLOB { } } - *getRestingLimitAsks( + *getRestingLimitAsks( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1180,11 +1200,13 @@ export class DLOB { ); } - *getRestingLimitBids( + *getRestingLimitBids( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1231,12 +1253,14 @@ export class DLOB { * @param oraclePriceData * @param filterFcn */ - *getAsks( + *getAsks( marketIndex: number, _fallbackAsk: BN | undefined, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1276,12 +1300,14 @@ export class DLOB { * @param oraclePriceData * @param filterFcn */ - *getBids( + *getBids( marketIndex: number, _fallbackBid: BN | undefined, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1313,11 +1339,13 @@ export class DLOB { ); } - findCrossingRestingLimitOrders( + findCrossingRestingLimitOrders( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ): NodeToFill[] { const nodesToFill = new Array(); @@ -1439,11 +1467,13 @@ export class DLOB { } } - public getBestAsk( + public getBestAsk( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ): BN | undefined { const bestAsk = this.getRestingLimitAsks( marketIndex, @@ -1458,11 +1488,13 @@ export class DLOB { return undefined; } - public getBestBid( + public getBestBid( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ): BN | undefined { const bestBid = this.getRestingLimitBids( marketIndex, @@ -1636,7 +1668,7 @@ export class DLOB { if (isVariant(marketType, 'perp')) { const slot = slotSubscriber.getSlot(); const oraclePriceData = - driftClient.getOracleDataForPerpMarket(marketIndex); + driftClient.getMMOracleDataForPerpMarket(marketIndex); const bestAsk = this.getBestAsk( marketIndex, @@ -1681,18 +1713,18 @@ export class DLOB { } else if (isVariant(marketType, 'spot')) { const slot = slotSubscriber.getSlot(); const oraclePriceData = - driftClient.getOracleDataForPerpMarket(marketIndex); + driftClient.getOracleDataForSpotMarket(marketIndex); const bestAsk = this.getBestAsk( marketIndex, slot, - marketType, + MarketType.SPOT, oraclePriceData ); const bestBid = this.getBestBid( marketIndex, slot, - marketType, + MarketType.SPOT, oraclePriceData ); const mid = bestAsk.add(bestBid).div(new BN(2)); @@ -1783,7 +1815,7 @@ export class DLOB { * @param depth how many levels of the order book to return * @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber} */ - public getL2({ + public getL2({ marketIndex, marketType, slot, @@ -1792,9 +1824,11 @@ export class DLOB { fallbackL2Generators = [], }: { marketIndex: number; - marketType: MarketType; + marketType: T; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; depth: number; fallbackL2Generators?: L2OrderBookGenerator[]; }): L2OrderBook { @@ -1853,16 +1887,18 @@ export class DLOB { * @param slot * @param oraclePriceData */ - public getL3({ + public getL3({ marketIndex, marketType, slot, oraclePriceData, }: { marketIndex: number; - marketType: MarketType; + marketType: T; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; }): L3OrderBook { const bids: L3Level[] = []; const asks: L3Level[] = []; @@ -1945,7 +1981,7 @@ export class DLOB { * @param param.oraclePriceData the oracle price data * @returns the estimated quote amount filled: QUOTE_PRECISION */ - public estimateFillWithExactBaseAmount({ + public estimateFillWithExactBaseAmount({ marketIndex, marketType, baseAmount, @@ -1954,11 +1990,13 @@ export class DLOB { oraclePriceData, }: { marketIndex: number; - marketType: MarketType; + marketType: T; baseAmount: BN; orderDirection: PositionDirection; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; }): BN { if (isVariant(orderDirection, 'long')) { return this.estimateFillExactBaseAmountInForSide( @@ -1977,7 +2015,7 @@ export class DLOB { } } - public getBestMakers({ + public getBestMakers({ marketIndex, marketType, direction, @@ -1986,10 +2024,12 @@ export class DLOB { numMakers, }: { marketIndex: number; - marketType: MarketType; + marketType: T; direction: PositionDirection; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; numMakers: number; }): PublicKey[] { const makers = new Map(); diff --git a/sdk/src/dlob/DLOBSubscriber.ts b/sdk/src/dlob/DLOBSubscriber.ts index 431302c011..03e5cf70bf 100644 --- a/sdk/src/dlob/DLOBSubscriber.ts +++ b/sdk/src/dlob/DLOBSubscriber.ts @@ -139,7 +139,7 @@ export class DLOBSubscriber { fallbackL2Generators = [ getVammL2Generator({ marketAccount: this.driftClient.getPerpMarketAccount(marketIndex), - oraclePriceData: + mmOraclePriceData: this.driftClient.getMMOracleDataForPerpMarket(marketIndex), numOrders: numVammOrders ?? depth, topOfBookQuoteAmounts: diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index 41022f4998..2e45976858 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -22,7 +22,7 @@ import { PositionDirection, SwapDirection, } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { PublicKey } from '@solana/web3.js'; import { standardizeBaseAssetAmount, standardizePrice } from '../math/orders'; @@ -178,18 +178,18 @@ export function createL2Levels( export function getVammL2Generator({ marketAccount, - oraclePriceData, + mmOraclePriceData, numOrders, now = new BN(Math.floor(Date.now() / 1000)), topOfBookQuoteAmounts = [], }: { marketAccount: PerpMarketAccount; - oraclePriceData: OraclePriceData; + mmOraclePriceData: MMOraclePriceData; numOrders: number; now?: BN; topOfBookQuoteAmounts?: BN[]; }): L2OrderBookGenerator { - const updatedAmm = calculateUpdatedAMM(marketAccount.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(marketAccount.amm, mmOraclePriceData); const paused = isOperationPaused( marketAccount.pausedOperations, PerpOperation.AMM_FILL @@ -209,7 +209,7 @@ export function getVammL2Generator({ const [bidReserves, askReserves] = calculateSpreadReserves( updatedAmm, - oraclePriceData, + mmOraclePriceData, now, isVariant(marketAccount.contractType, 'prediction') ); @@ -218,7 +218,7 @@ export function getVammL2Generator({ const commonOpts = { numOrders, numBaseOrders, - oraclePriceData, + mmOraclePriceData, orderTickSize: marketAccount.amm.orderTickSize, orderStepSize: marketAccount.amm.orderStepSize, pegMultiplier: updatedAmm.pegMultiplier, @@ -253,7 +253,7 @@ export function getVammL2Generator({ const raw = commonOpts.topOfBookQuoteAmounts[count] .mul(AMM_TO_QUOTE_PRECISION_RATIO) .mul(PRICE_PRECISION) - .div(commonOpts.oraclePriceData.price); + .div(commonOpts.mmOraclePriceData.price); baseSwap = standardizeBaseAssetAmount(raw, commonOpts.orderStepSize); const remaining = openLiquidity.abs().sub(topSize); if (remaining.lt(baseSwap)) baseSwap = remaining; diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index fa5e441574..02ccb1b0d9 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -134,7 +134,7 @@ import { import { findDirectionToClose, positionIsAvailable } from './math/position'; import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance'; import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName'; -import { OraclePriceData } from './oracles/types'; +import { MMOraclePriceData, OraclePriceData } from './oracles/types'; import { DriftClientConfig } from './driftClientConfig'; import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; @@ -8516,20 +8516,16 @@ export class DriftClient { } public getOracleDataForPerpMarket(marketIndex: number): OraclePriceData { - const perpMarket = this.getPerpMarketAccount(marketIndex); - const isMMOracleActive = !perpMarket.amm.mmOracleSlot.eq(ZERO); - return { - ...this.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket( - marketIndex - ).data, - isMMOracleActive, - }; + return this.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket( + marketIndex + ).data; } - public getMMOracleDataForPerpMarket(marketIndex: number): OraclePriceData { + public getMMOracleDataForPerpMarket(marketIndex: number): MMOraclePriceData { const perpMarket = this.getPerpMarketAccount(marketIndex); const oracleData = this.getOracleDataForPerpMarket(marketIndex); const stateAccountAndSlot = this.accountSubscriber.getStateAccountAndSlot(); + const isMMOracleActive = !perpMarket.amm.mmOracleSlot.eq(ZERO); const pctDiff = perpMarket.amm.mmOraclePrice .sub(oracleData.price) .abs() @@ -8546,20 +8542,18 @@ export class DriftClient { perpMarket.amm.mmOracleSlot < oracleData.slot || pctDiff.gt(PERCENTAGE_PRECISION.divn(100)) // 1% threshold ) { - return { ...oracleData, fetchedWithMMOracle: true }; + return { ...oracleData, isMMOracleActive }; } else { - const conf = getOracleConfidenceFromMMOracleData({ - mmOraclePrice: perpMarket.amm.mmOraclePrice, - mmOracleSlot: perpMarket.amm.mmOracleSlot, - oraclePriceData: oracleData, - }); + const conf = getOracleConfidenceFromMMOracleData( + perpMarket.amm.mmOraclePrice, + oracleData + ); return { price: perpMarket.amm.mmOraclePrice, slot: perpMarket.amm.mmOracleSlot, confidence: conf, hasSufficientNumberOfDataPoints: true, - fetchedWithMMOracle: true, - isMMOracleActive: oracleData.isMMOracleActive, + isMMOracleActive, }; } } diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 1c14a231ae..26279b8a11 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -16113,4 +16113,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} +} \ No newline at end of file diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index cf46acfd73..f86ea9444f 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -26,7 +26,7 @@ import { assert } from '../assert/assert'; import { squareRootBN, sigNum, clampBN } from './utils'; import { standardizeBaseAssetAmount } from './orders'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { calculateRepegCost, calculateAdjustKCost, @@ -52,14 +52,14 @@ export function calculatePegFromTargetPrice( export function calculateOptimalPegAndBudget( amm: AMM, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): [BN, BN, BN, boolean] { const reservePriceBefore = calculatePrice( amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier ); - const targetPrice = oraclePriceData.price; + const targetPrice = mmOraclePriceData.price; const newPeg = calculatePegFromTargetPrice( targetPrice, amm.baseAssetReserve, @@ -113,13 +113,13 @@ export function calculateOptimalPegAndBudget( export function calculateNewAmm( amm: AMM, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): [BN, BN, BN, BN] { let pKNumer = new BN(1); let pKDenom = new BN(1); const [targetPrice, _newPeg, budget, _checkLowerBound] = - calculateOptimalPegAndBudget(amm, oraclePriceData); + calculateOptimalPegAndBudget(amm, mmOraclePriceData); let prePegCost = calculateRepegCost(amm, _newPeg); let newPeg = _newPeg; @@ -155,15 +155,15 @@ export function calculateNewAmm( export function calculateUpdatedAMM( amm: AMM, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): AMM { - if (amm.curveUpdateIntensity == 0 || oraclePriceData === undefined) { + if (amm.curveUpdateIntensity == 0 || mmOraclePriceData === undefined) { return amm; } const newAmm = Object.assign({}, amm); const [prepegCost, pKNumer, pKDenom, newPeg] = calculateNewAmm( amm, - oraclePriceData + mmOraclePriceData ); newAmm.baseAssetReserve = newAmm.baseAssetReserve.mul(pKNumer).div(pKDenom); @@ -196,21 +196,13 @@ export function calculateUpdatedAMM( export function calculateUpdatedAMMSpreadReserves( amm: AMM, direction: PositionDirection, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, isPrediction = false ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } { - if ( - !oraclePriceData?.fetchedWithMMOracle && - oraclePriceData?.isMMOracleActive - ) { - console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate updated AMM in calculateUpdatedAMMSpreadReserves' - ); - } - const newAmm = calculateUpdatedAMM(amm, oraclePriceData); + const newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); const [shortReserves, longReserves] = calculateSpreadReserves( newAmm, - oraclePriceData, + mmOraclePriceData, undefined, isPrediction ); @@ -229,68 +221,22 @@ export function calculateUpdatedAMMSpreadReserves( return result; } -export function calculateAMMBidAskPrice( - amm: AMM, - oraclePriceData: OraclePriceData, - withUpdate = true, - isPrediction = false -): [BN, BN] { - if ( - !oraclePriceData?.fetchedWithMMOracle && - oraclePriceData?.isMMOracleActive - ) { - console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing in calculateAMMBidAskPrice' - ); - } - let newAmm: AMM; - if (withUpdate) { - newAmm = calculateUpdatedAMM(amm, oraclePriceData); - } else { - newAmm = amm; - } - - const [bidReserves, askReserves] = calculateSpreadReserves( - newAmm, - oraclePriceData, - undefined, - isPrediction - ); - - const askPrice = calculatePrice( - askReserves.baseAssetReserve, - askReserves.quoteAssetReserve, - newAmm.pegMultiplier - ); - - const bidPrice = calculatePrice( - bidReserves.baseAssetReserve, - bidReserves.quoteAssetReserve, - newAmm.pegMultiplier - ); - - return [bidPrice, askPrice]; -} - -/** - * @deprecated Use calculateAMMBidAskPrice instead - */ export function calculateBidAskPrice( amm: AMM, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, withUpdate = true, isPrediction = false ): [BN, BN] { let newAmm: AMM; if (withUpdate) { - newAmm = calculateUpdatedAMM(amm, oraclePriceData); + newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); } else { newAmm = amm; } const [bidReserves, askReserves] = calculateSpreadReserves( newAmm, - oraclePriceData, + mmOraclePriceData, undefined, isPrediction ); @@ -991,7 +937,7 @@ export function getQuoteAssetReservePredictionMarketBounds( export function calculateSpreadReserves( amm: AMM, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, now?: BN, isPrediction = false ) { @@ -1085,7 +1031,7 @@ export function calculateSpreadReserves( let [longSpread, shortSpread] = calculateSpread( amm, - oraclePriceData, + mmOraclePriceData, now, reservePrice ); @@ -1095,9 +1041,9 @@ export function calculateSpreadReserves( amm.curveUpdateIntensity > 100; if (doReferencePricOffsetSmooth) { - if (oraclePriceData.slot !== amm.lastUpdateSlot) { + if (mmOraclePriceData.slot !== amm.lastUpdateSlot) { const slotsPassed = - oraclePriceData.slot.toNumber() - amm.lastUpdateSlot.toNumber(); + mmOraclePriceData.slot.toNumber() - amm.lastUpdateSlot.toNumber(); const fullOffsetDelta = referencePriceOffset - amm.referencePriceOffset; const raw = Math.trunc( Math.min(Math.abs(fullOffsetDelta), slotsPassed * 1000) / 10 @@ -1212,7 +1158,7 @@ export function calculateMaxBaseAssetAmountToTrade( amm: AMM, limit_price: BN, direction: PositionDirection, - oraclePriceData?: OraclePriceData, + mmOraclePriceData?: MMOraclePriceData, now?: BN, isPrediction = false ): [BN, PositionDirection] { @@ -1227,7 +1173,7 @@ export function calculateMaxBaseAssetAmountToTrade( const newBaseAssetReserve = squareRootBN(newBaseAssetReserveSquared); const [shortSpreadReserves, longSpreadReserves] = calculateSpreadReserves( amm, - oraclePriceData, + mmOraclePriceData, now, isPrediction ); diff --git a/sdk/src/math/funding.ts b/sdk/src/math/funding.ts index 6b08c56106..281721404c 100644 --- a/sdk/src/math/funding.ts +++ b/sdk/src/math/funding.ts @@ -9,7 +9,7 @@ import { } from '../constants/numericConstants'; import { BigNum } from '../factory/bigNum'; import { PerpMarketAccount, isVariant } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { calculateBidAskPrice } from './amm'; import { calculateLiveOracleTwap } from './oracles'; import { clampBN } from './utils'; @@ -20,7 +20,7 @@ import { function calculateLiveMarkTwap( market: PerpMarketAccount, - oraclePriceData?: OraclePriceData, + mmOraclePriceData?: MMOraclePriceData, markPrice?: BN, now?: BN, period = new BN(3600) @@ -37,7 +37,7 @@ function calculateLiveMarkTwap( ); if (!markPrice) { - const [bid, ask] = calculateBidAskPrice(market.amm, oraclePriceData); + const [bid, ask] = calculateBidAskPrice(market.amm, mmOraclePriceData); markPrice = bid.add(ask).div(new BN(2)); } @@ -122,6 +122,7 @@ function shrinkStaleTwaps( */ export function calculateAllEstimatedFundingRate( market: PerpMarketAccount, + mmOraclePriceData?: MMOraclePriceData, oraclePriceData?: OraclePriceData, markPrice?: BN, now?: BN @@ -136,7 +137,7 @@ export function calculateAllEstimatedFundingRate( // calculate real-time mark and oracle twap const liveMarkTwap = calculateLiveMarkTwap( market, - oraclePriceData, + mmOraclePriceData, markPrice, now, market.amm.fundingPeriod @@ -265,6 +266,7 @@ const getFundingRatePct = (rawFundingRate: BN) => { */ export function calculateFormattedLiveFundingRate( market: PerpMarketAccount, + mmOraclePriceData: MMOraclePriceData, oraclePriceData: OraclePriceData, period: 'hour' | 'year' ): { @@ -278,6 +280,7 @@ export function calculateFormattedLiveFundingRate( const [_markTwapLive, _oracleTwapLive, longFundingRate, shortFundingRate] = calculateLongShortFundingRateAndLiveTwaps( market, + mmOraclePriceData, oraclePriceData, undefined, nowBN @@ -349,12 +352,14 @@ function getMaxPriceDivergenceForFundingRate( */ export function calculateLongShortFundingRate( market: PerpMarketAccount, + mmOraclePriceData?: MMOraclePriceData, oraclePriceData?: OraclePriceData, markPrice?: BN, now?: BN ): [BN, BN] { const [_1, _2, _, cappedAltEst, interpEst] = calculateAllEstimatedFundingRate( market, + mmOraclePriceData, oraclePriceData, markPrice, now @@ -380,12 +385,19 @@ export function calculateLongShortFundingRate( */ export function calculateLongShortFundingRateAndLiveTwaps( market: PerpMarketAccount, + mmOraclePriceData?: MMOraclePriceData, oraclePriceData?: OraclePriceData, markPrice?: BN, now?: BN ): [BN, BN, BN, BN] { const [markTwapLive, oracleTwapLive, _2, cappedAltEst, interpEst] = - calculateAllEstimatedFundingRate(market, oraclePriceData, markPrice, now); + calculateAllEstimatedFundingRate( + market, + mmOraclePriceData, + oraclePriceData, + markPrice, + now + ); if ( market.amm.baseAssetAmountLong.gt(market.amm.baseAssetAmountShort.abs()) diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index 78f95048ae..4ef1bb6752 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -20,7 +20,7 @@ import { calculateSizeDiscountAssetWeight, calculateSizePremiumLiabilityWeight, } from './margin'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { BASE_PRECISION, MARGIN_PRECISION, @@ -43,9 +43,9 @@ import { assert } from '../assert/assert'; */ export function calculateReservePrice( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { - const newAmm = calculateUpdatedAMM(market.amm, oraclePriceData); + const newAmm = calculateUpdatedAMM(market.amm, mmOraclePriceData); return calculatePrice( newAmm.baseAssetReserve, newAmm.quoteAssetReserve, @@ -61,13 +61,13 @@ export function calculateReservePrice( */ export function calculateBidPrice( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.SHORT, - oraclePriceData + mmOraclePriceData ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); @@ -81,13 +81,13 @@ export function calculateBidPrice( */ export function calculateAskPrice( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.LONG, - oraclePriceData + mmOraclePriceData ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); @@ -117,10 +117,10 @@ export function calculateNewMarketAfterTrade( export function calculateOracleReserveSpread( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { - const reservePrice = calculateReservePrice(market, oraclePriceData); - return calculateOracleSpread(reservePrice, oraclePriceData); + const reservePrice = calculateReservePrice(market, mmOraclePriceData); + return calculateOracleSpread(reservePrice, mmOraclePriceData); } export function calculateOracleSpread( @@ -298,7 +298,7 @@ export function calculateNetUserPnlImbalance( export function calculateAvailablePerpLiquidity( market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, dlob: DLOB, slot: number ): { bids: BN; asks: BN } { @@ -315,7 +315,7 @@ export function calculateAvailablePerpLiquidity( market.marketIndex, slot, MarketType.PERP, - oraclePriceData + mmOraclePriceData )) { bids = bids.add( bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled) @@ -326,7 +326,7 @@ export function calculateAvailablePerpLiquidity( market.marketIndex, slot, MarketType.PERP, - oraclePriceData + mmOraclePriceData )) { asks = asks.add( ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled) diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index 7be65e58ce..5789f1c224 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -10,7 +10,7 @@ import { } from '../types'; import { ZERO, TWO, ONE } from '../constants/numericConstants'; import { BN } from '@coral-xyz/anchor'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { getAuctionPrice, isAuctionComplete, @@ -153,7 +153,7 @@ export function standardizePrice( export function getLimitPrice( order: Order, - oraclePriceData: OraclePriceData, + oraclePriceData: OraclePriceData | MMOraclePriceData, slot: number, fallbackPrice?: BN, protectedMakerParams?: ProtectedMakerParams @@ -232,7 +232,7 @@ export function hasAuctionPrice(order: Order, slot: number): boolean { export function isFillableByVAMM( order: Order, market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, slot: number, ts: number, minAuctionDuration: number @@ -242,7 +242,7 @@ export function isFillableByVAMM( calculateBaseAssetAmountForAmmToFulfill( order, market, - oraclePriceData, + mmOraclePriceData, slot ).gte(market.amm.minOrderSize)) || isOrderExpired(order, ts) @@ -252,23 +252,23 @@ export function isFillableByVAMM( export function calculateBaseAssetAmountForAmmToFulfill( order: Order, market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, slot: number ): BN { if (mustBeTriggered(order) && !isTriggered(order)) { return ZERO; } - const limitPrice = getLimitPrice(order, oraclePriceData, slot); + const limitPrice = getLimitPrice(order, mmOraclePriceData, slot); let baseAssetAmount; - const updatedAMM = calculateUpdatedAMM(market.amm, oraclePriceData); + const updatedAMM = calculateUpdatedAMM(market.amm, mmOraclePriceData); if (limitPrice !== undefined) { baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice( order, updatedAMM, limitPrice, - oraclePriceData + mmOraclePriceData ); } else { baseAssetAmount = order.baseAssetAmount.sub(order.baseAssetAmountFilled); @@ -286,7 +286,7 @@ export function calculateBaseAssetAmountToFillUpToLimitPrice( order: Order, amm: AMM, limitPrice: BN, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { const adjustedLimitPrice = isVariant(order.direction, 'long') ? limitPrice.sub(amm.orderTickSize) @@ -296,7 +296,7 @@ export function calculateBaseAssetAmountToFillUpToLimitPrice( amm, adjustedLimitPrice, order.direction, - oraclePriceData + mmOraclePriceData ); const baseAssetAmount = standardizeBaseAssetAmount( diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index c64b86d73f..b3f8a30fac 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -8,7 +8,7 @@ import { ONE, ZERO, } from '../constants/numericConstants'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { PerpMarketAccount, PositionDirection, @@ -35,7 +35,7 @@ import { calculateNetUserPnlImbalance } from './market'; export function calculateBaseAssetValue( market: PerpMarketAccount, userPosition: PerpPosition, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, useSpread = true, skipUpdate = false ): BN { @@ -52,7 +52,7 @@ export function calculateBaseAssetValue( calculateUpdatedAMMSpreadReserves( market.amm, directionToClose, - oraclePriceData + mmOraclePriceData ); prepegAmm = { baseAssetReserve, @@ -61,7 +61,7 @@ export function calculateBaseAssetValue( pegMultiplier: newPeg, }; } else { - prepegAmm = calculateUpdatedAMM(market.amm, oraclePriceData); + prepegAmm = calculateUpdatedAMM(market.amm, mmOraclePriceData); } } else { prepegAmm = market.amm; diff --git a/sdk/src/math/trade.ts b/sdk/src/math/trade.ts index aeb9272ed7..bd83d9e317 100644 --- a/sdk/src/math/trade.ts +++ b/sdk/src/math/trade.ts @@ -31,7 +31,7 @@ import { } from './amm'; import { squareRootBN } from './utils'; import { isVariant } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { DLOB } from '../dlob/DLOB'; import { PublicKey } from '@solana/web3.js'; import { Orderbook } from '@project-serum/serum'; @@ -77,19 +77,19 @@ export function calculateTradeSlippage( amount: BN, market: PerpMarketAccount, inputAssetType: AssetType = 'quote', - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, useSpread = true ): [BN, BN, BN, BN] { let oldPrice: BN; if (useSpread && market.amm.baseSpread > 0) { if (isVariant(direction, 'long')) { - oldPrice = calculateAskPrice(market, oraclePriceData); + oldPrice = calculateAskPrice(market, mmOraclePriceData); } else { - oldPrice = calculateBidPrice(market, oraclePriceData); + oldPrice = calculateBidPrice(market, mmOraclePriceData); } } else { - oldPrice = calculateReservePrice(market, oraclePriceData); + oldPrice = calculateReservePrice(market, mmOraclePriceData); } if (amount.eq(ZERO)) { return [ZERO, ZERO, oldPrice, oldPrice]; @@ -100,7 +100,7 @@ export function calculateTradeSlippage( amount, market, inputAssetType, - oraclePriceData, + mmOraclePriceData, useSpread ); @@ -112,7 +112,11 @@ export function calculateTradeSlippage( let amm: Parameters[0]; if (useSpread && market.amm.baseSpread > 0) { const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData + ); amm = { baseAssetReserve, quoteAssetReserve, @@ -165,7 +169,7 @@ export function calculateTradeAcquiredAmounts( amount: BN, market: PerpMarketAccount, inputAssetType: AssetType = 'quote', - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, useSpread = true ): [BN, BN, BN] { if (amount.eq(ZERO)) { @@ -177,7 +181,11 @@ export function calculateTradeAcquiredAmounts( let amm: Parameters[0]; if (useSpread && market.amm.baseSpread > 0) { const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData + ); amm = { baseAssetReserve, quoteAssetReserve, @@ -227,16 +235,16 @@ export function calculateTargetPriceTrade( targetPrice: BN, pct: BN = MAXPCT, outputAssetType: AssetType = 'quote', - oraclePriceData?: OraclePriceData, + mmOraclePriceData?: MMOraclePriceData, useSpread = true ): [PositionDirection, BN, BN, BN] { assert(market.amm.baseAssetReserve.gt(ZERO)); assert(targetPrice.gt(ZERO)); assert(pct.lte(MAXPCT) && pct.gt(ZERO)); - const reservePriceBefore = calculateReservePrice(market, oraclePriceData); - const bidPriceBefore = calculateBidPrice(market, oraclePriceData); - const askPriceBefore = calculateAskPrice(market, oraclePriceData); + const reservePriceBefore = calculateReservePrice(market, mmOraclePriceData); + const bidPriceBefore = calculateBidPrice(market, mmOraclePriceData); + const askPriceBefore = calculateAskPrice(market, mmOraclePriceData); let direction; if (targetPrice.gt(reservePriceBefore)) { @@ -261,7 +269,11 @@ export function calculateTargetPriceTrade( if (useSpread && market.amm.baseSpread > 0) { const { baseAssetReserve, quoteAssetReserve, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData + ); baseAssetReserveBefore = baseAssetReserve; quoteAssetReserveBefore = quoteAssetReserve; peg = newPeg; @@ -387,7 +399,7 @@ export function calculateEstimatedPerpEntryPrice( amount: BN, direction: PositionDirection, market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, dlob: DLOB, slot: number, usersToSkip = new Map() @@ -413,12 +425,12 @@ export function calculateEstimatedPerpEntryPrice( const takerIsLong = isVariant(direction, 'long'); const limitOrders = dlob[ takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids' - ](market.marketIndex, slot, MarketType.PERP, oraclePriceData); + ](market.marketIndex, slot, MarketType.PERP, mmOraclePriceData); const swapDirection = getSwapDirection(assetType, direction); const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves(market.amm, direction, mmOraclePriceData); const amm = { baseAssetReserve, quoteAssetReserve, @@ -464,7 +476,7 @@ export function calculateEstimatedPerpEntryPrice( let limitOrder = limitOrders.next().value; if (limitOrder) { - const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot); + const limitOrderPrice = limitOrder.getPrice(mmOraclePriceData, slot); bestPrice = takerIsLong ? BN.min(limitOrderPrice, bestPrice) : BN.max(limitOrderPrice, bestPrice); @@ -477,7 +489,7 @@ export function calculateEstimatedPerpEntryPrice( !cumulativeBaseFilled.eq(amount) && (ammLiquidity.gt(ZERO) || limitOrder) ) { - const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot); + const limitOrderPrice = limitOrder?.getPrice(mmOraclePriceData, slot); let maxAmmFill: BN; if (limitOrderPrice) { @@ -561,7 +573,7 @@ export function calculateEstimatedPerpEntryPrice( !cumulativeQuoteFilled.eq(amount) && (ammLiquidity.gt(ZERO) || limitOrder) ) { - const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot); + const limitOrderPrice = limitOrder?.getPrice(mmOraclePriceData, slot); let maxAmmFill: BN; if (limitOrderPrice) { diff --git a/sdk/src/oracles/types.ts b/sdk/src/oracles/types.ts index 5ffbc25744..4c015be0da 100644 --- a/sdk/src/oracles/types.ts +++ b/sdk/src/oracles/types.ts @@ -2,10 +2,11 @@ import { BN } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; import { OracleSource } from '../types'; -export type MMOraclePriceData = { - mmOraclePrice: BN; - mmOracleSlot: BN; - oraclePriceData: OraclePriceData; +export type MMOraclePriceData = Omit< + OraclePriceData, + 'twap' | 'twapConfidence' | 'maxPrice' +> & { + isMMOracleActive: boolean; }; export type OraclePriceData = { @@ -16,8 +17,6 @@ export type OraclePriceData = { twap?: BN; twapConfidence?: BN; maxPrice?: BN; // pre-launch markets only - fetchedWithMMOracle?: boolean; - isMMOracleActive?: boolean; }; export type OracleInfo = { diff --git a/sdk/src/oracles/utils.ts b/sdk/src/oracles/utils.ts index f26e498487..1995490599 100644 --- a/sdk/src/oracles/utils.ts +++ b/sdk/src/oracles/utils.ts @@ -1,11 +1,10 @@ import { BN } from '@coral-xyz/anchor'; -import { MMOraclePriceData } from './types'; +import { OraclePriceData } from './types'; export function getOracleConfidenceFromMMOracleData( - mmOracleData: MMOraclePriceData + mmOraclePrice: BN, + oraclePriceData: OraclePriceData ): BN { - const mmOracleDiffPremium = mmOracleData.mmOraclePrice - .sub(mmOracleData.oraclePriceData.price) - .abs(); - return mmOracleData.oraclePriceData.confidence.add(mmOracleDiffPremium); + const mmOracleDiffPremium = mmOraclePrice.sub(oraclePriceData.price).abs(); + return oraclePriceData.confidence.add(mmOracleDiffPremium); } diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 4f1c33cd16..208fae7803 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -90,7 +90,7 @@ import { calculateMarginUSDCRequiredForTrade, calculateWorstCaseBaseAssetAmount, } from './math/margin'; -import { OraclePriceData } from './oracles/types'; +import { MMOraclePriceData, OraclePriceData } from './oracles/types'; import { UserConfig } from './userConfig'; import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber'; import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber'; @@ -671,7 +671,7 @@ export class User { )[0]; const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex); - const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex); + const oraclePriceData = this.getMMOracleDataForPerpMarket(marketIndex); const worstCaseBaseAssetAmount = perpPosition ? calculateWorstCaseBaseAssetAmount( perpPosition, @@ -843,7 +843,7 @@ export class User { const market = this.driftClient.getPerpMarketAccount( perpPosition.marketIndex ); - const oraclePriceData = this.getOracleDataForPerpMarket( + const oraclePriceData = this.getMMOracleDataForPerpMarket( market.marketIndex ); @@ -1031,7 +1031,7 @@ export class User { } for (const perpPosition of this.getActivePerpPositions()) { - const oraclePriceData = this.getOracleDataForPerpMarket( + const oraclePriceData = this.getMMOracleDataForPerpMarket( perpPosition.marketIndex ); @@ -1447,7 +1447,7 @@ export class User { )[0]; } - let valuationPrice = this.getOracleDataForPerpMarket( + let valuationPrice = this.getMMOracleDataForPerpMarket( market.marketIndex ).price; @@ -1679,7 +1679,7 @@ export class User { const entryPrice = calculateEntryPrice(position); - const oraclePriceData = this.getOracleDataForPerpMarket( + const oraclePriceData = this.getMMOracleDataForPerpMarket( position.marketIndex ); @@ -2660,7 +2660,7 @@ export class User { ? true : targetSide === currentPositionSide; - const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); + const oracleData = this.getMMOracleDataForPerpMarket(targetMarketIndex); const marketAccount = this.driftClient.getPerpMarketAccount(targetMarketIndex); @@ -3426,7 +3426,7 @@ export class User { this.getEmptyPosition(targetMarketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex); - const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); + const oracleData = this.getMMOracleDataForPerpMarket(targetMarketIndex); let { // eslint-disable-next-line prefer-const @@ -4106,7 +4106,7 @@ export class User { !!marginCategory )[0] || this.getEmptyPosition(marketToIgnore); - const oracleData = this.getOracleDataForPerpMarket(marketToIgnore); + const oracleData = this.getMMOracleDataForPerpMarket(marketToIgnore); let currentPerpPositionValueUSDC = ZERO; if (currentPerpPosition) { @@ -4124,8 +4124,8 @@ export class User { ).sub(currentPerpPositionValueUSDC); } - private getOracleDataForPerpMarket(marketIndex: number): OraclePriceData { - return this.driftClient.getOracleDataForPerpMarket(marketIndex); + private getMMOracleDataForPerpMarket(marketIndex: number): MMOraclePriceData { + return this.driftClient.getMMOracleDataForPerpMarket(marketIndex); } private getOracleDataForSpotMarket(marketIndex: number): OraclePriceData { From c6101842f424161a83b9cb01b20cd900de8b4621 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:19:53 +0000 Subject: [PATCH 117/216] sdk: release v2.135.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 918d6bacec..34b222ae08 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.1 \ No newline at end of file +2.135.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 72779a8bed..61c116f606 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.1", + "version": "2.135.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 6b25d244aa3847e5fadf811a7ca3ac012d7e1bce Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 18 Aug 2025 14:28:36 -0400 Subject: [PATCH 118/216] sdk: fix isHighLeverageMode --- sdk/src/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 208fae7803..5119815236 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -2102,7 +2102,7 @@ export class User { public isHighLeverageMode(marginCategory: MarginCategory): boolean { return ( isVariant(this.getUserAccount().marginMode, 'highLeverage') || - (isVariant(marginCategory, 'maintenance') && + (marginCategory === 'Maintenance' && isVariant(this.getUserAccount().marginMode, 'highLeverageMaintenance')) ); } From b0662f8f1b51bb6d460962aea534ca7447556585 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 18:33:33 +0000 Subject: [PATCH 119/216] sdk: release v2.135.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 34b222ae08..2d878e156e 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.2 \ No newline at end of file +2.135.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 61c116f606..14246942b6 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.2", + "version": "2.135.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From cc88d503a92c616cba2b7be7408ccbb74f82b3dd Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 19 Aug 2025 23:26:37 +0800 Subject: [PATCH 120/216] refactor(sdk): add update delegate ix method, ovrride authority for settle multiple pnl (#1822) --- sdk/src/driftClient.ts | 27 +++++++++++++++++++++++++-- sdk/src/tx/txHandler.ts | 4 ++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 02ccb1b0d9..94c0455bce 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -1587,6 +1587,26 @@ export class DriftClient { return txSig; } + public async getUpdateUserDelegateIx( + delegate: PublicKey, + overrides: { + subAccountId?: number, + userAccountPublicKey?: PublicKey, + authority?: PublicKey, + } + ): Promise { + const subAccountId = overrides.subAccountId ?? this.activeSubAccountId; + const userAccountPublicKey = overrides.userAccountPublicKey ?? await this.getUserAccountPublicKey(); + const authority = overrides.authority ?? this.wallet.publicKey; + + return await this.program.instruction.updateUserDelegate(subAccountId, delegate, { + accounts: { + user: userAccountPublicKey, + authority, + }, + }); + } + public async updateUserDelegate( delegate: PublicKey, subAccountId = 0 @@ -7406,7 +7426,10 @@ export class DriftClient { settleeUserAccountPublicKey: PublicKey, settleeUserAccount: UserAccount, marketIndexes: number[], - mode: SettlePnlMode + mode: SettlePnlMode, + overrides?: { + authority?: PublicKey; + } ): Promise { const remainingAccounts = this.getRemainingAccounts({ userAccounts: [settleeUserAccount], @@ -7420,7 +7443,7 @@ export class DriftClient { { accounts: { state: await this.getStatePublicKey(), - authority: this.wallet.publicKey, + authority: overrides?.authority ?? this.wallet.publicKey, user: settleeUserAccountPublicKey, spotMarketVault: this.getQuoteSpotMarketAccount().vault, }, diff --git a/sdk/src/tx/txHandler.ts b/sdk/src/tx/txHandler.ts index 09471867e8..bd9b2b702e 100644 --- a/sdk/src/tx/txHandler.ts +++ b/sdk/src/tx/txHandler.ts @@ -133,6 +133,10 @@ export class TxHandler { this.preSignedCb = props.opts?.preSignedCb; } + public getWallet() { + return this.wallet; + } + private addHashAndExpiryToLookup( hashAndExpiry: BlockhashWithExpiryBlockHeight ) { From bd479bec8d0084184d4243f1d19e3318ad54155f Mon Sep 17 00:00:00 2001 From: moosecat Date: Tue, 19 Aug 2025 13:46:49 -0700 Subject: [PATCH 121/216] mm oracle sdk additions (#1824) * strict typing for more MM oracle contact points * add comments to auction.ts * prettify --- sdk/src/dlob/DLOB.ts | 6 +++--- sdk/src/dlob/DLOBNode.ts | 16 ++++++++++++---- sdk/src/driftClient.ts | 25 +++++++++++++++---------- sdk/src/math/auction.ts | 19 +++++++++++++++++++ sdk/src/math/orders.ts | 5 +++-- sdk/src/user.ts | 12 ++++++++---- 6 files changed, 60 insertions(+), 23 deletions(-) diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 5a3367912f..70d788ee00 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -1096,15 +1096,15 @@ export class DLOB { } } - protected *getBestNode( + protected *getBestNode( generatorList: Array>, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends 'spot' ? OraclePriceData : MMOraclePriceData, slot: number, compareFcn: ( bestDLOBNode: DLOBNode, currentDLOBNode: DLOBNode, slot: number, - oraclePriceData: OraclePriceData + oraclePriceData: T extends 'spot' ? OraclePriceData : MMOraclePriceData ) => boolean, filterFcn?: DLOBFilterFcn ): Generator { diff --git a/sdk/src/dlob/DLOBNode.ts b/sdk/src/dlob/DLOBNode.ts index a789583ce1..5907f7498f 100644 --- a/sdk/src/dlob/DLOBNode.ts +++ b/sdk/src/dlob/DLOBNode.ts @@ -5,8 +5,13 @@ import { ZERO, } from '../constants/numericConstants'; import { getLimitPrice } from '../math/orders'; -import { isVariant, Order, ProtectedMakerParams } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { + isVariant, + MarketTypeStr, + Order, + ProtectedMakerParams, +} from '../types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { convertToNumber } from '../math/conversion'; import { getOrderSignature } from './NodeList'; @@ -81,8 +86,11 @@ export abstract class OrderNode implements DLOBNode { return msg; } - getPrice(oraclePriceData: OraclePriceData, slot: number): BN { - return getLimitPrice( + getPrice( + oraclePriceData: T extends 'spot' ? OraclePriceData : MMOraclePriceData, + slot: number + ): BN { + return getLimitPrice( this.order, oraclePriceData, slot, diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 94c0455bce..d1fafa3d05 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -1590,21 +1590,26 @@ export class DriftClient { public async getUpdateUserDelegateIx( delegate: PublicKey, overrides: { - subAccountId?: number, - userAccountPublicKey?: PublicKey, - authority?: PublicKey, + subAccountId?: number; + userAccountPublicKey?: PublicKey; + authority?: PublicKey; } ): Promise { const subAccountId = overrides.subAccountId ?? this.activeSubAccountId; - const userAccountPublicKey = overrides.userAccountPublicKey ?? await this.getUserAccountPublicKey(); + const userAccountPublicKey = + overrides.userAccountPublicKey ?? (await this.getUserAccountPublicKey()); const authority = overrides.authority ?? this.wallet.publicKey; - return await this.program.instruction.updateUserDelegate(subAccountId, delegate, { - accounts: { - user: userAccountPublicKey, - authority, - }, - }); + return await this.program.instruction.updateUserDelegate( + subAccountId, + delegate, + { + accounts: { + user: userAccountPublicKey, + authority, + }, + } + ); } public async updateUserDelegate( diff --git a/sdk/src/math/auction.ts b/sdk/src/math/auction.ts index d0daf315be..f0b1b72119 100644 --- a/sdk/src/math/auction.ts +++ b/sdk/src/math/auction.ts @@ -29,6 +29,13 @@ export function isFallbackAvailableLiquiditySource( return new BN(slot).sub(order.slot).gt(new BN(minAuctionDuration)); } +/** + * + * @param order + * @param slot + * @param oraclePrice Use MMOraclePriceData source for perp orders, OraclePriceData for spot + * @returns BN + */ export function getAuctionPrice( order: Order, slot: number, @@ -92,6 +99,13 @@ export function getAuctionPriceForFixedAuction(order: Order, slot: number): BN { return price; } +/** + * + * @param order + * @param slot + * @param oraclePrice Use MMOraclePriceData source for perp orders, OraclePriceData for spot + * @returns + */ export function getAuctionPriceForOracleOffsetAuction( order: Order, slot: number, @@ -186,6 +200,11 @@ export function deriveOracleAuctionParams({ }; } +/** + * + * @param params Use OraclePriceData.price for oraclePrice param + * @returns + */ export function getTriggerAuctionStartPrice(params: { perpMarket: PerpMarketAccount; direction: PositionDirection; diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index 5789f1c224..0ef192717e 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -7,6 +7,7 @@ import { Order, PositionDirection, ProtectedMakerParams, + MarketTypeStr, } from '../types'; import { ZERO, TWO, ONE } from '../constants/numericConstants'; import { BN } from '@coral-xyz/anchor'; @@ -151,9 +152,9 @@ export function standardizePrice( } } -export function getLimitPrice( +export function getLimitPrice( order: Order, - oraclePriceData: OraclePriceData | MMOraclePriceData, + oraclePriceData: T extends 'spot' ? OraclePriceData : MMOraclePriceData, slot: number, fallbackPrice?: BN, protectedMakerParams?: ProtectedMakerParams diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 5119815236..0c027fa8bb 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -671,7 +671,7 @@ export class User { )[0]; const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex); - const oraclePriceData = this.getMMOracleDataForPerpMarket(marketIndex); + const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex); const worstCaseBaseAssetAmount = perpPosition ? calculateWorstCaseBaseAssetAmount( perpPosition, @@ -1447,7 +1447,7 @@ export class User { )[0]; } - let valuationPrice = this.getMMOracleDataForPerpMarket( + let valuationPrice = this.getOracleDataForPerpMarket( market.marketIndex ).price; @@ -3426,7 +3426,7 @@ export class User { this.getEmptyPosition(targetMarketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex); - const oracleData = this.getMMOracleDataForPerpMarket(targetMarketIndex); + const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); let { // eslint-disable-next-line prefer-const @@ -4106,7 +4106,7 @@ export class User { !!marginCategory )[0] || this.getEmptyPosition(marketToIgnore); - const oracleData = this.getMMOracleDataForPerpMarket(marketToIgnore); + const oracleData = this.getOracleDataForPerpMarket(marketToIgnore); let currentPerpPositionValueUSDC = ZERO; if (currentPerpPosition) { @@ -4128,6 +4128,10 @@ export class User { return this.driftClient.getMMOracleDataForPerpMarket(marketIndex); } + private getOracleDataForPerpMarket(marketIndex: number): OraclePriceData { + return this.driftClient.getOracleDataForPerpMarket(marketIndex); + } + private getOracleDataForSpotMarket(marketIndex: number): OraclePriceData { return this.driftClient.getOracleDataForSpotMarket(marketIndex); } From 6310e6487afd509a7adaa890ddfaaa622b3f7f4f Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:51:43 +0000 Subject: [PATCH 122/216] sdk: release v2.135.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 2d878e156e..c4f23ef3b8 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.3 \ No newline at end of file +2.135.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 14246942b6..0e72cc8ebe 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.3", + "version": "2.135.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From daf235a302a52cd59b5e2ba402888dfb3ad3eeac Mon Sep 17 00:00:00 2001 From: moosecat Date: Wed, 20 Aug 2025 09:35:45 -0700 Subject: [PATCH 123/216] add strict typing for getPrice and new auction trigger function (#1826) * add strict typing for getPrice and new auction trigger function * refactor getTriggerAuctionStartAndExecutionPrice --- sdk/src/dlob/DLOBNode.ts | 5 ++++- sdk/src/math/auction.ts | 36 ++++++++++++++++++++++++++++++++++++ yarn.lock | 5 +++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/sdk/src/dlob/DLOBNode.ts b/sdk/src/dlob/DLOBNode.ts index 5907f7498f..dfce8abf5d 100644 --- a/sdk/src/dlob/DLOBNode.ts +++ b/sdk/src/dlob/DLOBNode.ts @@ -16,7 +16,10 @@ import { convertToNumber } from '../math/conversion'; import { getOrderSignature } from './NodeList'; export interface DLOBNode { - getPrice(oraclePriceData: OraclePriceData, slot: number): BN; + getPrice( + oraclePriceData: T extends 'spot' ? OraclePriceData : MMOraclePriceData, + slot: number + ): BN; isVammNode(): boolean; order: Order | undefined; isBaseFilled(): boolean; diff --git a/sdk/src/math/auction.ts b/sdk/src/math/auction.ts index f0b1b72119..5a58f60054 100644 --- a/sdk/src/math/auction.ts +++ b/sdk/src/math/auction.ts @@ -289,3 +289,39 @@ export function getTriggerAuctionStartPrice(params: { return auctionStartPrice; } + +/** + * + * @param params Use OraclePriceData.price for oraclePrice param and MMOraclePriceData.price for mmOraclePrice + * @returns + */ +export function getTriggerAuctionStartAndExecutionPrice(params: { + perpMarket: PerpMarketAccount; + direction: PositionDirection; + oraclePrice: BN; + mmOraclePrice: BN; + limitPrice?: BN; +}): { startPrice: BN; executionPrice: BN } { + const { perpMarket, direction, oraclePrice, limitPrice, mmOraclePrice } = + params; + + const startPrice = getTriggerAuctionStartPrice({ + perpMarket, + direction, + oraclePrice, + limitPrice, + }); + + const offsetPlusBuffer = startPrice.sub(oraclePrice); + let executionPrice = mmOraclePrice.add(offsetPlusBuffer); + + if (limitPrice) { + if (isVariant(direction, 'long')) { + executionPrice = BN.min(executionPrice, limitPrice); + } else { + executionPrice = BN.max(executionPrice, limitPrice); + } + } + + return { startPrice, executionPrice }; +} diff --git a/yarn.lock b/yarn.lock index af30a18826..41c9b1a9d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2867,6 +2867,11 @@ zod@4.0.0-beta.20250505T195954: dependencies: "@zod/core" "0.11.6" +zod@4.0.17: + version "4.0.17" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.0.17.tgz#95931170715f73f7426c385c237b7477750d6c8d" + integrity sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ== + zstddec@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec" From a547ea8737b11457da316bb79987fbf11c2f2574 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:40:46 +0000 Subject: [PATCH 124/216] sdk: release v2.135.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index c4f23ef3b8..2cd39d8486 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.4 \ No newline at end of file +2.135.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 0e72cc8ebe..d02df2b99d 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.4", + "version": "2.135.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From a3853c16839465391f5e225e603320d2d76cc88a Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 20 Aug 2025 15:29:32 -0700 Subject: [PATCH 125/216] sdk: handle unfillable reduce only orders (#1790) * sdk: handle unfillable reduce only orders * fix dlob tests build errors * fix some test build errors --- sdk/package.json | 3 +- sdk/src/dlob/DLOB.ts | 61 +++++++++++++++++- sdk/src/types.ts | 1 + sdk/tests/amm/test.ts | 130 ++++++++++++++++++++++++-------------- sdk/tests/dlob/helpers.ts | 14 ++-- sdk/tests/dlob/test.ts | 6 +- 6 files changed, 160 insertions(+), 55 deletions(-) diff --git a/sdk/package.json b/sdk/package.json index d02df2b99d..fa421abb92 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -15,10 +15,11 @@ "build": "yarn clean && tsc -p tsconfig.json && tsc -p tsconfig.browser.json && node scripts/postbuild.js", "build:browser": "yarn clean && tsc -p tsconfig.json && tsc -p tsconfig.browser.json && node scripts/postbuild.js --force-env browser", "clean": "rm -rf lib", - "test": "mocha -r ts-node/register tests/**/*.ts", + "test": "mocha -r ts-node/register tests/**/*.ts --ignore 'tests/dlob/**/*.ts'", "test:inspect": "mocha --inspect-brk -r ts-node/register tests/**/*.ts", "test:bignum": "mocha -r ts-node/register tests/bn/**/*.ts", "test:ci": "mocha -r ts-node/register tests/ci/**/*.ts", + "test:dlob": "mocha -r ts-node/register tests/dlob/**/*.ts", "patch-and-pub": "npm version patch --force && npm publish", "prettify": "prettier --check './src/***/*.ts'", "prettify:fix": "prettier --write './{src,tests}/***/*.ts'", diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 70d788ee00..15c2ec72f2 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -508,10 +508,23 @@ export class DLOB { new BN(slot) ); + const stepSize = isVariant(marketType, 'perp') + ? (marketAccount as PerpMarketAccount).amm.orderStepSize + : (marketAccount as SpotMarketAccount).orderStepSize; + + const cancelReduceOnlyNodesToFill = + this.findUnfillableReduceOnlyOrdersToCancel( + marketIndex, + marketType, + stepSize + ); + return this.mergeNodesToFill( restingLimitOrderNodesToFill, takingOrderNodesToFill - ).concat(expiredNodesToFill); + ) + .concat(expiredNodesToFill) + .concat(cancelReduceOnlyNodesToFill); } getMakerRebate( @@ -1011,6 +1024,52 @@ export class DLOB { return nodesToFill; } + public findUnfillableReduceOnlyOrdersToCancel( + marketIndex: number, + marketType: MarketType, + stepSize: BN + ): NodeToFill[] { + const nodesToFill = new Array(); + + const marketTypeStr = getVariant(marketType) as MarketTypeStr; + const nodeLists = this.orderLists.get(marketTypeStr).get(marketIndex); + + if (!nodeLists) { + return nodesToFill; + } + + const generators = [ + nodeLists.takingLimit.bid.getGenerator(), + nodeLists.restingLimit.bid.getGenerator(), + nodeLists.floatingLimit.bid.getGenerator(), + nodeLists.market.bid.getGenerator(), + nodeLists.signedMsg.bid.getGenerator(), + nodeLists.takingLimit.ask.getGenerator(), + nodeLists.restingLimit.ask.getGenerator(), + nodeLists.floatingLimit.ask.getGenerator(), + nodeLists.market.ask.getGenerator(), + nodeLists.signedMsg.ask.getGenerator(), + nodeLists.trigger.above.getGenerator(), + nodeLists.trigger.below.getGenerator(), + ]; + + for (const generator of generators) { + for (const node of generator) { + if (!node.order.reduceOnly) { + continue; + } + + if (node.baseAssetAmount.lt(stepSize)) { + nodesToFill.push({ + node, + makerNodes: [], + }); + } + } + } + + return nodesToFill; + } *getTakingBids( marketIndex: number, marketType: T, diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 06a174f426..2d6a34d114 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -196,6 +196,7 @@ export class OrderBitFlag { static readonly SignedMessage = 1; static readonly OracleTriggerMarket = 2; static readonly SafeTriggerOrder = 4; + static readonly NewTriggerReduceOnly = 8; } export class OrderAction { diff --git a/sdk/tests/amm/test.ts b/sdk/tests/amm/test.ts index fc8821c1a8..ab849c57d6 100644 --- a/sdk/tests/amm/test.ts +++ b/sdk/tests/amm/test.ts @@ -15,7 +15,6 @@ import { calculateInventoryScale, calculateAllEstimatedFundingRate, calculateLongShortFundingRateAndLiveTwaps, - OraclePriceData, getVammL2Generator, BASE_PRECISION, PerpMarketAccount, @@ -32,6 +31,7 @@ import { isOracleValid, OracleGuardRails, getNewOracleConfPct, + MMOraclePriceData, // calculateReservePrice, } from '../../src'; import { mockPerpMarkets } from '../dlob/helpers'; @@ -278,7 +278,8 @@ describe('AMM Tests', () => { oracleStd, longIntensity, shortIntensity, - volume24H + volume24H, + 0 ); const l1 = spreads[0]; const s1 = spreads[1]; @@ -305,6 +306,7 @@ describe('AMM Tests', () => { longIntensity, shortIntensity, volume24H, + 0, true ); // console.log(terms1); @@ -337,6 +339,7 @@ describe('AMM Tests', () => { new BN(12358265776), new BN(72230366233), new BN(432067603632), + 0, true ); @@ -369,6 +372,7 @@ describe('AMM Tests', () => { new BN(12358265776), new BN(72230366233), new BN(432067603632), + 0, true ); @@ -402,6 +406,7 @@ describe('AMM Tests', () => { new BN(12358265776), new BN(72230366233), new BN(432067603632), + 0, true ); @@ -436,6 +441,7 @@ describe('AMM Tests', () => { new BN(768323534), new BN(243875031), new BN(130017761029), + 0, true ); @@ -608,6 +614,7 @@ describe('AMM Tests', () => { new BN(suiExample.amm.longIntensityVolume), new BN(suiExample.amm.shortIntensityVolume), new BN(suiExample.amm.volume24H), + 0, true ); @@ -648,6 +655,7 @@ describe('AMM Tests', () => { new BN(suiExample.amm.longIntensityVolume), new BN(suiExample.amm.shortIntensityVolume), new BN(suiExample.amm.volume24H), + 0, true ); console.log(termsSuiExampleMod1); @@ -676,6 +684,7 @@ describe('AMM Tests', () => { new BN(suiExample.amm.longIntensityVolume), new BN(suiExample.amm.shortIntensityVolume), new BN(suiExample.amm.volume24H), + 0, true ); @@ -713,7 +722,8 @@ describe('AMM Tests', () => { slot: new BN(68 + 1), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; const reserves = calculateSpreadReserves(mockAmm, oraclePriceData, now); assert(reserves[0].baseAssetReserve.eq(new BN('1000000000'))); @@ -924,7 +934,8 @@ describe('AMM Tests', () => { slot: new BN(68 + 1), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; const reserves = calculateSpreadReserves(mockAmm, oraclePriceData, now); assert(reserves[0].baseAssetReserve.eq(new BN('1000000000'))); @@ -1142,7 +1153,8 @@ describe('AMM Tests', () => { slot: new BN(slot), confidence: new BN(1000), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockAmm.oracleStd = new BN(0.18 * PRICE_PRECISION.toNumber()); mockAmm.fundingPeriod = new BN(3600); mockAmm.historicalOracleData.lastOraclePriceTwap = oraclePriceData.price @@ -1206,7 +1218,8 @@ describe('AMM Tests', () => { slot: new BN(slot), confidence: new BN(13.553 * PRICE_PRECISION.toNumber() * 0.021), hasSufficientNumberOfDataPoints: true, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot ) @@ -1221,7 +1234,8 @@ describe('AMM Tests', () => { slot: new BN(slot), confidence: new BN(1), hasSufficientNumberOfDataPoints: false, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot ) @@ -1236,7 +1250,8 @@ describe('AMM Tests', () => { slot: new BN(slot), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot ) @@ -1251,7 +1266,8 @@ describe('AMM Tests', () => { slot: new BN(slot), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot + 100 ) @@ -1266,7 +1282,8 @@ describe('AMM Tests', () => { slot: new BN(slot + 100), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot ) @@ -1281,7 +1298,8 @@ describe('AMM Tests', () => { slot: new BN(slot + 5), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot ) @@ -1296,7 +1314,8 @@ describe('AMM Tests', () => { slot: new BN(slot + 5), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }, + isMMOracleActive: true, + } as MMOraclePriceData, oracleGuardRails, slot ) @@ -1314,11 +1333,12 @@ describe('AMM Tests', () => { mockMarket1.amm.lastFundingRateTs = new BN(1688860817); const currentMarkPrice = new BN(1.9843 * PRICE_PRECISION.toNumber()); // trading at a premium - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(1.9535 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, + isMMOracleActive: true, }; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 1.9535 * PRICE_PRECISION.toNumber() @@ -1352,7 +1372,8 @@ describe('AMM Tests', () => { _interpEst, ] = calculateAllEstimatedFundingRate( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1360,7 +1381,8 @@ describe('AMM Tests', () => { const [markTwapLive, oracleTwapLive, est1, est2] = calculateLongShortFundingRateAndLiveTwaps( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1387,11 +1409,12 @@ describe('AMM Tests', () => { mockMarket1.amm.lastFundingRateTs = new BN(1688864415); const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(1.224 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, + isMMOracleActive: true, }; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 1.9535 * PRICE_PRECISION.toNumber() @@ -1425,7 +1448,8 @@ describe('AMM Tests', () => { _interpEst, ] = calculateAllEstimatedFundingRate( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1440,7 +1464,8 @@ describe('AMM Tests', () => { const [markTwapLive, oracleTwapLive, est1, est2] = calculateLongShortFundingRateAndLiveTwaps( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1476,12 +1501,13 @@ describe('AMM Tests', () => { mockMarket1.amm.lastFundingRateTs = new BN(1688864415); const currentMarkPrice = new BN(1.2242 * PRICE_PRECISION.toNumber()); // trading at a premium - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(1.924 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 1.9535 * PRICE_PRECISION.toNumber() ); @@ -1515,7 +1541,8 @@ describe('AMM Tests', () => { _interpEst, ] = calculateAllEstimatedFundingRate( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1530,7 +1557,8 @@ describe('AMM Tests', () => { let [markTwapLive, oracleTwapLive, est1, est2] = calculateLongShortFundingRateAndLiveTwaps( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1559,7 +1587,8 @@ describe('AMM Tests', () => { [markTwapLive, oracleTwapLive, est1, est2] = calculateLongShortFundingRateAndLiveTwaps( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1588,7 +1617,8 @@ describe('AMM Tests', () => { [markTwapLive, oracleTwapLive, est1, est2] = calculateLongShortFundingRateAndLiveTwaps( mockMarket1, - oraclePriceData, + mmOraclePriceData, + undefined, currentMarkPrice, now ); @@ -1630,17 +1660,18 @@ describe('AMM Tests', () => { const now = new BN(1688881915); - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(18.624 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 18.5535 * PRICE_PRECISION.toNumber() ); - const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, mmOraclePriceData); const [openBids, openAsks] = calculateMarketOpenBidAsk( updatedAmm.baseAssetReserve, @@ -1651,7 +1682,7 @@ describe('AMM Tests', () => { const generator = getVammL2Generator({ marketAccount: mockMarket1, - oraclePriceData, + mmOraclePriceData, numOrders: 10, now, topOfBookQuoteAmounts: [], @@ -1706,17 +1737,18 @@ describe('AMM Tests', () => { const now = new BN(1688881915); - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(18.624 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 18.5535 * PRICE_PRECISION.toNumber() ); - const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, mmOraclePriceData); const [openBids, openAsks] = calculateMarketOpenBidAsk( updatedAmm.baseAssetReserve, @@ -1727,7 +1759,7 @@ describe('AMM Tests', () => { const generator = getVammL2Generator({ marketAccount: mockMarket1, - oraclePriceData, + mmOraclePriceData, numOrders: 10, now, topOfBookQuoteAmounts: [], @@ -1781,17 +1813,18 @@ describe('AMM Tests', () => { const now = new BN(1688881915); - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(18.624 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 18.5535 * PRICE_PRECISION.toNumber() ); - const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, mmOraclePriceData); const [openBids, openAsks] = calculateMarketOpenBidAsk( updatedAmm.baseAssetReserve, @@ -1804,7 +1837,7 @@ describe('AMM Tests', () => { const generator = getVammL2Generator({ marketAccount: mockMarket1, - oraclePriceData, + mmOraclePriceData, numOrders: 10, now, topOfBookQuoteAmounts: [ @@ -1862,17 +1895,18 @@ describe('AMM Tests', () => { const now = new BN(1688881915); - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(18.624 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 18.5535 * PRICE_PRECISION.toNumber() ); - const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, mmOraclePriceData); const [openBids, openAsks] = calculateMarketOpenBidAsk( updatedAmm.baseAssetReserve, @@ -1885,7 +1919,7 @@ describe('AMM Tests', () => { const generator = getVammL2Generator({ marketAccount: mockMarket1, - oraclePriceData, + mmOraclePriceData, numOrders: 10, now, topOfBookQuoteAmounts: [ @@ -1946,17 +1980,18 @@ describe('AMM Tests', () => { const now = new BN(1688881915); - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(18.624 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, + isMMOracleActive: true, }; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 18.5535 * PRICE_PRECISION.toNumber() ); - const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, mmOraclePriceData); const [openBids, openAsks] = calculateMarketOpenBidAsk( updatedAmm.baseAssetReserve, @@ -1969,7 +2004,7 @@ describe('AMM Tests', () => { const generator = getVammL2Generator({ marketAccount: mockMarket1, - oraclePriceData, + mmOraclePriceData, numOrders: 10, now, topOfBookQuoteAmounts: [ @@ -2030,17 +2065,18 @@ describe('AMM Tests', () => { const now = new BN(1688881915); - const oraclePriceData: OraclePriceData = { + const mmOraclePriceData: MMOraclePriceData = { price: new BN(18.624 * PRICE_PRECISION.toNumber()), slot: new BN(0), confidence: new BN(1), hasSufficientNumberOfDataPoints: true, - }; + isMMOracleActive: true, + } as MMOraclePriceData; mockMarket1.amm.historicalOracleData.lastOraclePrice = new BN( 18.5535 * PRICE_PRECISION.toNumber() ); - const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(mockMarket1.amm, mmOraclePriceData); const [openBids, openAsks] = calculateMarketOpenBidAsk( updatedAmm.baseAssetReserve, @@ -2051,7 +2087,7 @@ describe('AMM Tests', () => { const generator = getVammL2Generator({ marketAccount: mockMarket1, - oraclePriceData, + mmOraclePriceData, numOrders: 10, now, topOfBookQuoteAmounts: [], diff --git a/sdk/tests/dlob/helpers.ts b/sdk/tests/dlob/helpers.ts index 78ab90387c..d1b68abe8c 100644 --- a/sdk/tests/dlob/helpers.ts +++ b/sdk/tests/dlob/helpers.ts @@ -26,6 +26,7 @@ import { PRICE_PRECISION, DataAndSlot, } from '../../src'; +import { EventEmitter } from 'events'; export const mockPerpPosition: PerpPosition = { baseAssetAmount: new BN(0), @@ -62,7 +63,6 @@ export const mockAMM: AMM = { lastMarkPriceTwap: new BN(0), lastMarkPriceTwap5Min: new BN(0), lastMarkPriceTwapTs: new BN(0), - totalFeeEarnedPerLp: new BN(0), historicalOracleData: { lastOraclePrice: new BN(0), lastOracleConf: new BN(0), @@ -130,13 +130,10 @@ export const mockAMM: AMM = { markStd: new BN(0), oracleStd: new BN(0), - longIntensityCount: 0, longIntensityVolume: new BN(0), - shortIntensityCount: 0, shortIntensityVolume: new BN(0), volume24H: new BN(0), minOrderSize: new BN(0), - maxPositionSize: new BN(0), bidBaseAssetReserve: new BN(0), bidQuoteAssetReserve: new BN(0), @@ -150,11 +147,16 @@ export const mockAMM: AMM = { takerSpeedBumpOverride: 0, ammSpreadAdjustment: 0, ammInventorySpreadAdjustment: 0, + mmOracleSequenceId: new BN(0), + mmOraclePrice: new BN(0), + mmOracleSlot: new BN(0), + lastFundingOracleTwap: new BN(0), }; export const mockPerpMarkets: Array = [ { status: MarketStatus.INITIALIZED, + lastFillPrice: new BN(0), name: [], contractType: ContractType.PERPETUAL, contractTier: ContractTier.A, @@ -201,6 +203,7 @@ export const mockPerpMarkets: Array = [ }, { status: MarketStatus.INITIALIZED, + lastFillPrice: new BN(0), contractTier: ContractTier.A, nextFundingRateRecordId: new BN(0), nextCurveRecordId: new BN(0), @@ -247,6 +250,7 @@ export const mockPerpMarkets: Array = [ }, { status: MarketStatus.INITIALIZED, + lastFillPrice: new BN(0), contractTier: ContractTier.A, nextFundingRateRecordId: new BN(0), nextCurveRecordId: new BN(0), @@ -574,6 +578,7 @@ export const mockSpotMarkets: Array = [ export const mockStateAccount: StateAccount = { admin: PublicKey.default, + featureBitFlags: 0, defaultMarketOrderTimeInForce: 0, defaultSpotAuctionDuration: 0, discountMint: PublicKey.default, @@ -651,6 +656,7 @@ export const mockStateAccount: StateAccount = { }; export class MockUserMap implements UserMapInterface { + eventEmitter: EventEmitter = new EventEmitter(); private userMap = new Map(); private userAccountToAuthority = new Map(); private driftClient: DriftClient; diff --git a/sdk/tests/dlob/test.ts b/sdk/tests/dlob/test.ts index 75d50db1cc..eea4a13b51 100644 --- a/sdk/tests/dlob/test.ts +++ b/sdk/tests/dlob/test.ts @@ -99,7 +99,8 @@ function insertOrderToDLOB( }, userAccount.toString(), slot.toNumber(), - false + false, + baseAssetAmount ); } @@ -153,7 +154,8 @@ function insertTriggerOrderToDLOB( }, userAccount.toString(), slot.toNumber(), - false + false, + baseAssetAmount ); } From 0a45d88fb8d2cf62d2181a28b095ec16c0c2d879 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 22:33:57 +0000 Subject: [PATCH 126/216] sdk: release v2.135.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 2cd39d8486..8bc4190751 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.5 \ No newline at end of file +2.135.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index fa421abb92..a65596dc3d 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.5", + "version": "2.135.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 5b981cb48c93defbe0847af527c77446a2ab9726 Mon Sep 17 00:00:00 2001 From: moosecat Date: Thu, 21 Aug 2025 08:24:07 -0700 Subject: [PATCH 127/216] ref price offset amm math fix (#1828) * ref price offset amm math fix * add latest slot optional var to callers of update amm spread --- sdk/src/dlob/orderBookLevels.ts | 5 +++- sdk/src/math/amm.ts | 53 ++++++++++++++++++--------------- sdk/src/math/market.ts | 14 ++++++--- sdk/src/math/position.ts | 7 +++-- sdk/src/math/trade.ts | 29 +++++++++++++----- 5 files changed, 70 insertions(+), 38 deletions(-) diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index 2e45976858..67528a51bb 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -182,12 +182,14 @@ export function getVammL2Generator({ numOrders, now = new BN(Math.floor(Date.now() / 1000)), topOfBookQuoteAmounts = [], + latestSlot, }: { marketAccount: PerpMarketAccount; mmOraclePriceData: MMOraclePriceData; numOrders: number; now?: BN; topOfBookQuoteAmounts?: BN[]; + latestSlot?: BN; }): L2OrderBookGenerator { const updatedAmm = calculateUpdatedAMM(marketAccount.amm, mmOraclePriceData); const paused = isOperationPaused( @@ -211,7 +213,8 @@ export function getVammL2Generator({ updatedAmm, mmOraclePriceData, now, - isVariant(marketAccount.contractType, 'prediction') + isVariant(marketAccount.contractType, 'prediction'), + latestSlot ); const numBaseOrders = Math.max(1, numOrders - topOfBookQuoteAmounts.length); diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index f86ea9444f..7b41b4a783 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -197,14 +197,16 @@ export function calculateUpdatedAMMSpreadReserves( amm: AMM, direction: PositionDirection, mmOraclePriceData: MMOraclePriceData, - isPrediction = false + isPrediction = false, + latestSlot?: BN ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } { const newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); const [shortReserves, longReserves] = calculateSpreadReserves( newAmm, mmOraclePriceData, undefined, - isPrediction + isPrediction, + latestSlot ); const dirReserves = isVariant(direction, 'long') @@ -225,7 +227,8 @@ export function calculateBidAskPrice( amm: AMM, mmOraclePriceData: MMOraclePriceData, withUpdate = true, - isPrediction = false + isPrediction = false, + latestSlot?: BN ): [BN, BN] { let newAmm: AMM; if (withUpdate) { @@ -238,7 +241,8 @@ export function calculateBidAskPrice( newAmm, mmOraclePriceData, undefined, - isPrediction + isPrediction, + latestSlot ); const askPrice = calculatePrice( @@ -939,7 +943,8 @@ export function calculateSpreadReserves( amm: AMM, mmOraclePriceData: MMOraclePriceData, now?: BN, - isPrediction = false + isPrediction = false, + latestSlot?: BN ) { function calculateSpreadReserve( spread: number, @@ -1041,28 +1046,28 @@ export function calculateSpreadReserves( amm.curveUpdateIntensity > 100; if (doReferencePricOffsetSmooth) { - if (mmOraclePriceData.slot !== amm.lastUpdateSlot) { - const slotsPassed = - mmOraclePriceData.slot.toNumber() - amm.lastUpdateSlot.toNumber(); - const fullOffsetDelta = referencePriceOffset - amm.referencePriceOffset; - const raw = Math.trunc( - Math.min(Math.abs(fullOffsetDelta), slotsPassed * 1000) / 10 - ); - const maxAllowed = - Math.abs(amm.referencePriceOffset) || Math.abs(referencePriceOffset); + const slotsPassed = + latestSlot != null + ? BN.max(latestSlot.sub(amm.lastUpdateSlot), ZERO).toNumber() + : 0; + const fullOffsetDelta = referencePriceOffset - amm.referencePriceOffset; + const raw = Math.trunc( + Math.min(Math.abs(fullOffsetDelta), slotsPassed * 1000) / 10 + ); + const maxAllowed = + Math.abs(amm.referencePriceOffset) || Math.abs(referencePriceOffset); - const magnitude = Math.min(Math.max(raw, 10), maxAllowed); - const referencePriceDelta = Math.sign(fullOffsetDelta) * magnitude; + const magnitude = Math.min(Math.max(raw, 10), maxAllowed); + const referencePriceDelta = Math.sign(fullOffsetDelta) * magnitude; - referencePriceOffset = amm.referencePriceOffset + referencePriceDelta; + referencePriceOffset = amm.referencePriceOffset + referencePriceDelta; - if (referencePriceDelta < 0) { - longSpread += Math.abs(referencePriceDelta); - shortSpread += Math.abs(referencePriceOffset); - } else { - shortSpread += Math.abs(referencePriceDelta); - longSpread += Math.abs(referencePriceOffset); - } + if (referencePriceDelta < 0) { + longSpread += Math.abs(referencePriceDelta); + shortSpread += Math.abs(referencePriceOffset); + } else { + shortSpread += Math.abs(referencePriceDelta); + longSpread += Math.abs(referencePriceOffset); } } diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index 4ef1bb6752..6dd3697de6 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -61,13 +61,16 @@ export function calculateReservePrice( */ export function calculateBidPrice( market: PerpMarketAccount, - mmOraclePriceData: MMOraclePriceData + mmOraclePriceData: MMOraclePriceData, + latestSlot?: BN ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.SHORT, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); @@ -81,13 +84,16 @@ export function calculateBidPrice( */ export function calculateAskPrice( market: PerpMarketAccount, - mmOraclePriceData: MMOraclePriceData + mmOraclePriceData: MMOraclePriceData, + latestSlot?: BN ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.LONG, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index b3f8a30fac..3db5007a20 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -37,7 +37,8 @@ export function calculateBaseAssetValue( userPosition: PerpPosition, mmOraclePriceData: MMOraclePriceData, useSpread = true, - skipUpdate = false + skipUpdate = false, + latestSlot?: BN ): BN { if (userPosition.baseAssetAmount.eq(ZERO)) { return ZERO; @@ -52,7 +53,9 @@ export function calculateBaseAssetValue( calculateUpdatedAMMSpreadReserves( market.amm, directionToClose, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); prepegAmm = { baseAssetReserve, diff --git a/sdk/src/math/trade.ts b/sdk/src/math/trade.ts index bd83d9e317..2b0dec0f58 100644 --- a/sdk/src/math/trade.ts +++ b/sdk/src/math/trade.ts @@ -78,7 +78,8 @@ export function calculateTradeSlippage( market: PerpMarketAccount, inputAssetType: AssetType = 'quote', mmOraclePriceData: MMOraclePriceData, - useSpread = true + useSpread = true, + latestSlot?: BN ): [BN, BN, BN, BN] { let oldPrice: BN; @@ -115,7 +116,9 @@ export function calculateTradeSlippage( calculateUpdatedAMMSpreadReserves( market.amm, direction, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); amm = { baseAssetReserve, @@ -170,7 +173,8 @@ export function calculateTradeAcquiredAmounts( market: PerpMarketAccount, inputAssetType: AssetType = 'quote', mmOraclePriceData: MMOraclePriceData, - useSpread = true + useSpread = true, + latestSlot?: BN ): [BN, BN, BN] { if (amount.eq(ZERO)) { return [ZERO, ZERO, ZERO]; @@ -184,7 +188,9 @@ export function calculateTradeAcquiredAmounts( calculateUpdatedAMMSpreadReserves( market.amm, direction, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); amm = { baseAssetReserve, @@ -236,7 +242,8 @@ export function calculateTargetPriceTrade( pct: BN = MAXPCT, outputAssetType: AssetType = 'quote', mmOraclePriceData?: MMOraclePriceData, - useSpread = true + useSpread = true, + latestSlot?: BN ): [PositionDirection, BN, BN, BN] { assert(market.amm.baseAssetReserve.gt(ZERO)); assert(targetPrice.gt(ZERO)); @@ -272,7 +279,9 @@ export function calculateTargetPriceTrade( calculateUpdatedAMMSpreadReserves( market.amm, direction, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); baseAssetReserveBefore = baseAssetReserve; quoteAssetReserveBefore = quoteAssetReserve; @@ -430,7 +439,13 @@ export function calculateEstimatedPerpEntryPrice( const swapDirection = getSwapDirection(assetType, direction); const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, mmOraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData, + undefined, + new BN(slot) + ); const amm = { baseAssetReserve, quoteAssetReserve, From 1f211572f51238c0c3571f142b2bd4bf51e0f135 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 15:30:33 +0000 Subject: [PATCH 128/216] sdk: release v2.135.0-beta.7 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8bc4190751..72927021d4 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.6 \ No newline at end of file +2.135.0-beta.7 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index a65596dc3d..f929abb924 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.6", + "version": "2.135.0-beta.7", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 44c605889f79dcb0fee86f69996518144fa4f021 Mon Sep 17 00:00:00 2001 From: moosecat Date: Thu, 21 Aug 2025 09:56:01 -0700 Subject: [PATCH 129/216] latest slot as argument to getL2 (#1829) * latest slot as argument to getL2 * add comment * update BN import --- sdk/src/dlob/DLOBSubscriber.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/src/dlob/DLOBSubscriber.ts b/sdk/src/dlob/DLOBSubscriber.ts index 03e5cf70bf..30074ebf04 100644 --- a/sdk/src/dlob/DLOBSubscriber.ts +++ b/sdk/src/dlob/DLOBSubscriber.ts @@ -19,6 +19,7 @@ import { L3OrderBook, } from './orderBookLevels'; import { getProtectedMakerParamsMap } from '../math/protectedMakerParams'; +import { BN } from '@coral-xyz/anchor'; export class DLOBSubscriber { driftClient: DriftClient; @@ -82,6 +83,7 @@ export class DLOBSubscriber { * @param depth Number of orders to include in the order book. Defaults to 10. * @param includeVamm Whether to include the VAMM orders in the order book. Defaults to false. If true, creates vAMM generator {@link getVammL2Generator} and adds it to fallbackL2Generators. * @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber} + * @param latestSlot Latest slot observed via slot subscriber or similar for accuarate vamm quotes (if including the vAMM). */ public getL2({ marketName, @@ -91,6 +93,7 @@ export class DLOBSubscriber { includeVamm = false, numVammOrders, fallbackL2Generators = [], + latestSlot, }: { marketName?: string; marketIndex?: number; @@ -99,6 +102,7 @@ export class DLOBSubscriber { includeVamm?: boolean; numVammOrders?: number; fallbackL2Generators?: L2OrderBookGenerator[]; + latestSlot?: BN; }): L2OrderBook { if (marketName) { const derivedMarketInfo = @@ -146,6 +150,7 @@ export class DLOBSubscriber { marketIndex < 3 ? MAJORS_TOP_OF_BOOK_QUOTE_AMOUNTS : DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS, + latestSlot, }), ]; } From 2a2f68faf6a2b214e093835442c1788acf4aca14 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 17:01:05 +0000 Subject: [PATCH 130/216] sdk: release v2.135.0-beta.8 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 72927021d4..a5ff6467f7 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.7 \ No newline at end of file +2.135.0-beta.8 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index f929abb924..a8983ac7e8 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.7", + "version": "2.135.0-beta.8", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 38119a4dd472e7e16cb8238c356f62c1a4fa5f4e Mon Sep 17 00:00:00 2001 From: lil perp Date: Fri, 22 Aug 2025 13:42:45 -0400 Subject: [PATCH 131/216] program: trigger price use 5min mark price (#1830) * program: trigger price use 5min mark price * cargo fmt -- --------- Co-authored-by: 0xbigz <83473873+0xbigz@users.noreply.github.com> --- programs/drift/src/controller/orders.rs | 10 +++++++++- programs/drift/src/state/perp_market.rs | 6 +++++- programs/drift/src/state/perp_market/tests.rs | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 84c2010c66..eb8e7d122f 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -3058,7 +3058,15 @@ pub fn trigger_order( let trigger_price = perp_market.get_trigger_price(oracle_price, now, state.use_median_trigger_price())?; let can_trigger = order_satisfies_trigger_condition(&user.orders[order_index], trigger_price)?; - validate!(can_trigger, ErrorCode::OrderDidNotSatisfyTriggerCondition)?; + + validate!( + can_trigger, + ErrorCode::OrderDidNotSatisfyTriggerCondition, + "Order did not satisfy trigger condition. trigger_price: {} oracle_price: {} trigger_condition: {:?}", + trigger_price, + &user.orders[order_index].trigger_price, + &user.orders[order_index].trigger_condition + )?; let (_, worst_case_liability_value_before) = user .get_perp_position(market_index)? diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 32559f30ad..398ee7d970 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -766,7 +766,7 @@ impl PerpMarket { let last_fill_price = self.last_fill_price; - let mark_price_5min_twap = self.amm.last_mark_price_twap; + let mark_price_5min_twap = self.amm.last_mark_price_twap_5min; let last_oracle_price_twap_5min = self.amm.historical_oracle_data.last_oracle_price_twap_5min; @@ -781,6 +781,10 @@ impl PerpMarket { let oracle_plus_funding_basis = oracle_price.safe_add(last_funding_basis)?.cast::()?; let median_price = if last_fill_price > 0 { + println!( + "last_fill_price: {} oracle_plus_funding_basis: {} oracle_plus_basis_5min: {}", + last_fill_price, oracle_plus_funding_basis, oracle_plus_basis_5min + ); let mut prices = [ last_fill_price, oracle_plus_funding_basis, diff --git a/programs/drift/src/state/perp_market/tests.rs b/programs/drift/src/state/perp_market/tests.rs index 89d1974d08..46de2248b1 100644 --- a/programs/drift/src/state/perp_market/tests.rs +++ b/programs/drift/src/state/perp_market/tests.rs @@ -234,7 +234,7 @@ mod get_trigger_price { .get_trigger_price(oracle_price, now, true) .unwrap(); - assert_eq!(trigger_price, 109147085925); + assert_eq!(trigger_price, 109144736794); } #[test] From 9e2739be71893ef603d8fdf2bf2d7c2cda7368ab Mon Sep 17 00:00:00 2001 From: wphan Date: Fri, 22 Aug 2025 12:36:21 -0700 Subject: [PATCH 132/216] v2.135.0 --- CHANGELOG.md | 10 ++++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 4 ++-- 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9f8838ba..b80d006464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking +## [2.135.0] - 2025-08-22 + +### Features + +### Fixes + +- program: trigger price use 5min mark price ([#1830](https://github.com/drift-labs/protocol-v2/pull/1830)) + +### Breaking + ## [2.134.0] - 2025-08-13 ### Features diff --git a/Cargo.lock b/Cargo.lock index 6bdb6a8f0a..0848eca3b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.134.0" +version = "2.135.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 0ff0326832..f3646a3f54 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.134.0" +version = "2.135.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index a8983ac7e8..d28427ac3b 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0-beta.8", + "version": "2.135.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 26279b8a11..c9eeec1e4d 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.134.0", + "version": "2.135.0", "name": "drift", "instructions": [ { @@ -16113,4 +16113,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} \ No newline at end of file +} From e4e582087ef9882872cf49ff82f9196a5ad20a9b Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Aug 2025 19:45:24 +0000 Subject: [PATCH 133/216] sdk: release v2.136.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index a5ff6467f7..d3c8614111 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.135.0-beta.8 \ No newline at end of file +2.136.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index d28427ac3b..596bef9d8b 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.135.0", + "version": "2.136.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 9b355510ee1f706e34dfe73302e9163150bcddf0 Mon Sep 17 00:00:00 2001 From: LukasDeco Date: Mon, 25 Aug 2025 23:04:32 -0600 Subject: [PATCH 134/216] lukas/websocket improvements (#1807) * feat: initial implementation for users and markets WS improvements * lukas/gill websocket sub (#1781) * websockets gill temp * feat: feature parity between gill version ws acct sub and reg one + optional passing into driftClient * fix: post rebase bugs and cleanup * chore: websocket account subscriber export * feat: logging string update on ws acct v2 * rm: useless logging * chore: cleanup ws subscriber v2 docs * chore: specific name on custom ws acct sub param * fix: post rebase again cleanup * fix: prettier fixed * feat: initial implementation for users and markets WS improvements * feat: polling check on websocket acct subscriber v2 + naming * fix: lint * fix: non-hanging WS subscription async loop handling * fix: bugs with program ws subs hanging on asynciter * fix: goofy self imports * feat: initial batch fetching temp * temp: sub second WS subscribe time * fix: ws program account subscriber v2 bugs and optimizations * feat: chunk stuff account requests * feat: more subscribe optimizations ws driftclient sub v2 * chore: cleanup ws sub v2 logs * feat: conditional check on using ws account subscriber + unused * fix: bad import * chore: add export of WebSocketProgramAccountSubscriberV2 * fix: unneeded drift idl export messing up common build * fix: consolidate rpc ws subscriptions for oracles * feat: docs for ws v2 and cleanup * chore: more docs on ws acct susbcriber v2 * feat: PR feedback round 2 * fix: default timeout for ws v2 susbcribers * feat: PR feedback on resubOpts and simplify logic * fix: prettier --- .../README_WebSocketAccountSubscriberV2.md | 41 + sdk/src/accounts/types.ts | 3 + .../accounts/webSocketAccountSubscriberV2.ts | 285 ++++- ...webSocketDriftClientAccountSubscriberV2.ts | 745 +++++++++++++ .../webSocketProgramAccountSubscriberV2.ts | 596 ----------- .../webSocketProgramAccountsSubscriberV2.ts | 995 ++++++++++++++++++ .../websocketProgramUserAccountSubscriber.ts | 94 ++ sdk/src/driftClient.ts | 20 +- sdk/src/driftClientConfig.ts | 23 +- sdk/src/index.ts | 4 + sdk/src/memcmp.ts | 17 + sdk/src/oracles/oracleId.ts | 34 + sdk/src/user.ts | 30 +- sdk/src/userConfig.ts | 3 + 14 files changed, 2228 insertions(+), 662 deletions(-) create mode 100644 sdk/src/accounts/webSocketDriftClientAccountSubscriberV2.ts delete mode 100644 sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts create mode 100644 sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts create mode 100644 sdk/src/accounts/websocketProgramUserAccountSubscriber.ts diff --git a/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md b/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md index f53ebc1337..dad9d861ce 100644 --- a/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md +++ b/sdk/src/accounts/README_WebSocketAccountSubscriberV2.md @@ -31,6 +31,46 @@ await subscriber.subscribe((data) => { await subscriber.unsubscribe(); ``` +### Polling Instead of Resubscribing + +For accounts that rarely update (like long-tail markets), you can use polling instead of resubscribing to reduce resource usage: + +```typescript +const resubOpts = { + resubTimeoutMs: 30000, // 30 seconds + logResubMessages: true, + usePollingInsteadOfResub: true, // Enable polling mode + pollingIntervalMs: 30000, // Poll every 30 seconds (optional, defaults to 30000) +}; + +const subscriber = new WebSocketAccountSubscriberV2( + 'perpMarket', // account name + program, + marketPublicKey, + undefined, // decodeBuffer + resubOpts +); +``` + +**How it works:** +1. Initially subscribes to WebSocket updates +2. If no WebSocket data is received for `resubTimeoutMs` (30s), switches to websocket+polling mode if `usePollingInsteadOfResub` is specified true, else just resubscribes(unsub, sub). +3. Polls every `pollingIntervalMs` (alongside websocket connection) to check for updates by: + - Storing current account buffer state + - Fetching latest account data + - Comparing buffers to detect any missed updates +4. If polling detects new data (indicating missed WebSocket events): + - Immediately stops polling + - Resubscribes to WebSocket to restore real-time updates + - This helps recover from degraded WebSocket connections +5. If a WebSocket event is received while polling: + - Polling is automatically stopped + - System continues with normal WebSocket updates +6. This approach provides: + - Efficient handling of rarely-updated accounts + - Automatic recovery from WebSocket connection issues + - Seamless fallback between polling and WebSocket modes + ## Implementation Details ### Gill Integration @@ -52,3 +92,4 @@ const { rpc, rpcSubscriptions } = createSolanaClient({ 3. **Address Handling**: Converts `PublicKey` to gill's `Address` type for compatibility 4. **Response Formatting**: Converts gill responses to match the expected `AccountInfo` format 5. **Abort Signal**: Utilizes AbortSignal nodejs/web class to shutdown websocket connection synchronously +6. **Polling Mode**: Optional polling mechanism for accounts that rarely update diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index 26bb9cfb49..98ab7133fb 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -202,6 +202,9 @@ export type DataAndSlot = { export type ResubOpts = { resubTimeoutMs?: number; logResubMessages?: boolean; + // New options for polling-based resubscription + usePollingInsteadOfResub?: boolean; + pollingIntervalMs?: number; }; export interface UserStatsAccountEvents { diff --git a/sdk/src/accounts/webSocketAccountSubscriberV2.ts b/sdk/src/accounts/webSocketAccountSubscriberV2.ts index 9068f1b156..cb8a955135 100644 --- a/sdk/src/accounts/webSocketAccountSubscriberV2.ts +++ b/sdk/src/accounts/webSocketAccountSubscriberV2.ts @@ -8,16 +8,67 @@ import { AnchorProvider, Program } from '@coral-xyz/anchor'; import { capitalize } from './utils'; import { AccountInfoBase, - AccountInfoWithBase58EncodedData, AccountInfoWithBase64EncodedData, + AccountInfoWithBase58EncodedData, createSolanaClient, isAddress, + Rpc, + RpcSubscriptions, + SolanaRpcSubscriptionsApi, type Address, type Commitment, } from 'gill'; import { PublicKey } from '@solana/web3.js'; import bs58 from 'bs58'; +/** + * WebSocketAccountSubscriberV2 + * + * High-level overview + * - WebSocket-first subscriber for a single Solana account with optional + * polling safeguards when the WS feed goes quiet. + * - Emits decoded updates via `onChange` and maintains the latest + * `{buffer, slot}` and decoded `{data, slot}` internally. + * + * Why polling if this is a WebSocket subscriber? + * - Under real-world conditions, WS notifications can stall or get dropped. + * - When `resubOpts.resubTimeoutMs` elapses without WS data, you can either: + * - resubscribe to the WS stream (default), or + * - enable `resubOpts.usePollingInsteadOfResub` to start polling this single + * account via RPC to check for missed changes. + * - Polling compares the fetched buffer to the last known buffer. If different + * at an equal-or-later slot, it indicates a missed update and we resubscribe + * to WS to restore a clean stream. + * + * Initial fetch (on subscribe) + * - On `subscribe()`, we do a one-time RPC `fetch()` to seed internal state and + * emit the latest account state, ensuring consumers start from ground truth + * even before WS events arrive. + * + * Continuous polling (opt-in) + * - If `usePollingInsteadOfResub` is set, the inactivity timeout triggers a + * polling loop that periodically `fetch()`es the account and checks for + * changes. On change, polling stops and we resubscribe to WS. + * - If not set (default), the inactivity timeout immediately triggers a WS + * resubscription (no polling loop). + * + * Account focus + * - This class tracks exactly one account — the one passed to the constructor — + * which is by definition the account the consumer cares about. The extra + * logic is narrowly scoped to this account to minimize overhead. + * + * Tuning knobs + * - `resubOpts.resubTimeoutMs`: WS inactivity threshold before fallback. + * - `resubOpts.usePollingInsteadOfResub`: toggle polling vs immediate resub. + * - `resubOpts.pollingIntervalMs`: polling cadence (default 30s). + * - `resubOpts.logResubMessages`: verbose logs for diagnostics. + * - `commitment`: WS/RPC commitment used for reads and notifications. + * - `decodeBufferFn`: optional custom decode; defaults to Anchor coder. + * + * Implementation notes + * - Uses `gill` for both WS (`rpcSubscriptions`) and RPC (`rpc`) to match the + * program provider’s RPC endpoint. Handles base58/base64 encoded data. + */ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { dataAndSlot?: DataAndSlot; bufferAndSlot?: BufferAndSlot; @@ -29,12 +80,13 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { onChange: (data: T) => void; listenerId?: number; - resubOpts?: ResubOpts; + resubOpts: ResubOpts; commitment?: Commitment; isUnsubscribing = false; timeoutId?: ReturnType; + pollingTimeoutId?: ReturnType; receivingData: boolean; @@ -45,21 +97,40 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { >['rpcSubscriptions']; private abortController?: AbortController; + /** + * Create a single-account WebSocket subscriber with optional polling fallback. + * + * @param accountName Name of the Anchor account type (used for default decode). + * @param program Anchor `Program` used for decoding and provider access. + * @param accountPublicKey Public key of the account to track. + * @param decodeBuffer Optional custom decode function; if omitted, uses + * program coder to decode `accountName`. + * @param resubOpts Resubscription/polling options. See class docs. + * @param commitment Commitment for WS and RPC operations. + * @param rpcSubscriptions Optional override/injection for testing. + * @param rpc Optional override/injection for testing. + */ public constructor( accountName: string, program: Program, accountPublicKey: PublicKey, decodeBuffer?: (buffer: Buffer) => T, resubOpts?: ResubOpts, - commitment?: Commitment + commitment?: Commitment, + rpcSubscriptions?: RpcSubscriptions & string, + rpc?: Rpc ) { this.accountName = accountName; this.logAccountName = `${accountName}-${accountPublicKey.toBase58()}-ws-acct-subscriber-v2`; this.program = program; this.accountPublicKey = accountPublicKey; this.decodeBufferFn = decodeBuffer; - this.resubOpts = resubOpts; - if (this.resubOpts?.resubTimeoutMs < 1000) { + this.resubOpts = resubOpts ?? { + resubTimeoutMs: 30000, + usePollingInsteadOfResub: true, + logResubMessages: false, + }; + if (this.resubOpts.resubTimeoutMs < 1000) { console.log( `resubTimeoutMs should be at least 1000ms to avoid spamming resub ${this.logAccountName}` ); @@ -81,31 +152,67 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { ((this.program.provider as AnchorProvider).opts.commitment as Commitment); // Initialize gill client using the same RPC URL as the program provider - const rpcUrl = (this.program.provider as AnchorProvider).connection - .rpcEndpoint; - const { rpc, rpcSubscriptions } = createSolanaClient({ - urlOrMoniker: rpcUrl, - }); - this.rpc = rpc; - this.rpcSubscriptions = rpcSubscriptions; + + this.rpc = rpc + ? rpc + : (() => { + const rpcUrl = (this.program.provider as AnchorProvider).connection + .rpcEndpoint; + const { rpc } = createSolanaClient({ + urlOrMoniker: rpcUrl, + }); + return rpc; + })(); + this.rpcSubscriptions = rpcSubscriptions + ? rpcSubscriptions + : (() => { + const rpcUrl = (this.program.provider as AnchorProvider).connection + .rpcEndpoint; + const { rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: rpcUrl, + }); + return rpcSubscriptions; + })(); } - private async handleNotificationLoop(subscription: AsyncIterable) { + private async handleNotificationLoop( + subscriptionPromise: Promise> + ) { + const subscription = await subscriptionPromise; for await (const notification of subscription) { - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - clearTimeout(this.timeoutId); - this.handleRpcResponse(notification.context, notification.value); - this.setTimeout(); - } else { - this.handleRpcResponse(notification.context, notification.value); + // If we're currently polling and receive a WebSocket event, stop polling + if (this.pollingTimeoutId) { + if (this.resubOpts.logResubMessages) { + console.log( + `[${this.logAccountName}] Received WebSocket event while polling, stopping polling` + ); + } + this.stopPolling(); } + + this.receivingData = true; + clearTimeout(this.timeoutId); + this.handleRpcResponse(notification.context, notification.value); + this.setTimeout(); } } async subscribe(onChange: (data: T) => void): Promise { + /** + * Start the WebSocket subscription and (optionally) setup inactivity + * fallback. + * + * Flow + * - If we do not have initial state, perform a one-time `fetch()` to seed + * internal buffers and emit current data. + * - Subscribe to account notifications via WS. + * - If `resubOpts.resubTimeoutMs` is set, schedule an inactivity timeout. + * When it fires: + * - if `usePollingInsteadOfResub` is true, start polling loop; + * - otherwise, resubscribe to WS immediately. + */ if (this.listenerId != null || this.isUnsubscribing) { - if (this.resubOpts?.logResubMessages) { + if (this.resubOpts.logResubMessages) { console.log( `[${this.logAccountName}] Subscribe returning early - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` ); @@ -124,7 +231,7 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { this.listenerId = Math.random(); // Unique ID for logging purposes - if (this.resubOpts?.resubTimeoutMs) { + if (this.resubOpts.resubTimeoutMs) { this.receivingData = true; this.setTimeout(); } @@ -132,7 +239,7 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { // Subscribe to account changes using gill's rpcSubscriptions const pubkey = this.accountPublicKey.toBase58(); if (isAddress(pubkey)) { - const subscription = await this.rpcSubscriptions + const subscriptionPromise = this.rpcSubscriptions .accountNotifications(pubkey, { commitment: this.commitment, encoding: 'base64', @@ -141,8 +248,10 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { abortSignal: abortController.signal, }); - // Start notification loop without awaiting - this.handleNotificationLoop(subscription); + // Start notification loop with the subscription promise + this.handleNotificationLoop(subscriptionPromise); + } else { + throw new Error('Invalid account public key'); } } @@ -159,23 +268,37 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { } protected setTimeout(): void { + /** + * Schedule inactivity handling. If WS is quiet for + * `resubOpts.resubTimeoutMs` and `receivingData` is true, trigger either + * a polling loop or a resubscribe depending on options. + */ if (!this.onChange) { throw new Error('onChange callback function must be set'); } - this.timeoutId = setTimeout( - async () => { - if (this.isUnsubscribing) { - // If we are in the process of unsubscribing, do not attempt to resubscribe - if (this.resubOpts?.logResubMessages) { + this.timeoutId = setTimeout(async () => { + if (this.isUnsubscribing) { + // If we are in the process of unsubscribing, do not attempt to resubscribe + if (this.resubOpts.logResubMessages) { + console.log( + `[${this.logAccountName}] Timeout fired but isUnsubscribing=true, skipping resubscribe` + ); + } + return; + } + + if (this.receivingData) { + if (this.resubOpts.usePollingInsteadOfResub) { + // Use polling instead of resubscribing + if (this.resubOpts.logResubMessages) { console.log( - `[${this.logAccountName}] Timeout fired but isUnsubscribing=true, skipping resubscribe` + `[${this.logAccountName}] No ws data in ${this.resubOpts.resubTimeoutMs}ms, starting polling - listenerId=${this.listenerId}` ); } - return; - } - - if (this.receivingData) { - if (this.resubOpts?.logResubMessages) { + this.startPolling(); + } else { + // Original resubscribe behavior + if (this.resubOpts.logResubMessages) { console.log( `No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` ); @@ -183,23 +306,93 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { await this.unsubscribe(true); this.receivingData = false; await this.subscribe(this.onChange); - if (this.resubOpts?.logResubMessages) { + if (this.resubOpts.logResubMessages) { console.log( `[${this.logAccountName}] Resubscribe completed - receivingData=${this.receivingData}, listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}` ); } + } + } else { + if (this.resubOpts.logResubMessages) { + console.log( + `[${this.logAccountName}] Timeout fired but receivingData=false, skipping resubscribe` + ); + } + } + }, this.resubOpts.resubTimeoutMs); + } + + /** + * Start the polling loop (single-account). + * - Periodically calls `fetch()` and compares buffers to detect changes. + * - On detected change, stops polling and resubscribes to WS. + */ + private startPolling(): void { + const pollingInterval = this.resubOpts.pollingIntervalMs || 30000; // Default to 30s + + const poll = async () => { + if (this.isUnsubscribing) { + return; + } + + try { + // Store current data and buffer before polling + const currentBuffer = this.bufferAndSlot?.buffer; + + // Fetch latest account data + await this.fetch(); + + // Check if we got new data by comparing buffers + const newBuffer = this.bufferAndSlot?.buffer; + const hasNewData = + newBuffer && (!currentBuffer || !newBuffer.equals(currentBuffer)); + + if (hasNewData) { + // New data received, stop polling and resubscribe to websocket + if (this.resubOpts.logResubMessages) { + console.log( + `[${this.logAccountName}] Polling detected account data change, resubscribing to websocket` + ); + } + await this.unsubscribe(true); + this.receivingData = false; + await this.subscribe(this.onChange); } else { - if (this.resubOpts?.logResubMessages) { + // No new data, continue polling + if (this.resubOpts.logResubMessages) { console.log( - `[${this.logAccountName}] Timeout fired but receivingData=false, skipping resubscribe` + `[${this.logAccountName}] Polling found no account changes, continuing to poll every ${pollingInterval}ms` ); } + this.pollingTimeoutId = setTimeout(poll, pollingInterval); + } + } catch (error) { + if (this.resubOpts.logResubMessages) { + console.error( + `[${this.logAccountName}] Error during polling:`, + error + ); } - }, - this.resubOpts?.resubTimeoutMs - ); + // On error, continue polling + this.pollingTimeoutId = setTimeout(poll, pollingInterval); + } + }; + + // Start polling immediately + poll(); } + private stopPolling(): void { + if (this.pollingTimeoutId) { + clearTimeout(this.pollingTimeoutId); + this.pollingTimeoutId = undefined; + } + } + + /** + * Fetch the current account state via RPC and process it through the same + * decoding and update pathway as WS notifications. + */ async fetch(): Promise { // Use gill's rpc for fetching account info const accountAddress = this.accountPublicKey.toBase58() as Address; @@ -294,6 +487,11 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { } unsubscribe(onResub = false): Promise { + /** + * Stop timers, polling, and WS subscription. + * - When called during a resubscribe (`onResub=true`), we preserve + * `resubOpts.resubTimeoutMs` for the restarted subscription. + */ if (!onResub && this.resubOpts) { this.resubOpts.resubTimeoutMs = undefined; } @@ -301,6 +499,9 @@ export class WebSocketAccountSubscriberV2 implements AccountSubscriber { clearTimeout(this.timeoutId); this.timeoutId = undefined; + // Stop polling if active + this.stopPolling(); + // Abort the WebSocket subscription if (this.abortController) { this.abortController.abort('unsubscribing'); diff --git a/sdk/src/accounts/webSocketDriftClientAccountSubscriberV2.ts b/sdk/src/accounts/webSocketDriftClientAccountSubscriberV2.ts new file mode 100644 index 0000000000..0a37b53d81 --- /dev/null +++ b/sdk/src/accounts/webSocketDriftClientAccountSubscriberV2.ts @@ -0,0 +1,745 @@ +import { + AccountSubscriber, + DataAndSlot, + DelistedMarketSetting, + DriftClientAccountEvents, + DriftClientAccountSubscriber, + NotSubscribedError, + ResubOpts, +} from './types'; +import { + isVariant, + PerpMarketAccount, + SpotMarketAccount, + StateAccount, +} from '../types'; +import { Program } from '@coral-xyz/anchor'; +import StrictEventEmitter from 'strict-event-emitter-types'; +import { EventEmitter } from 'events'; +import { + getDriftStateAccountPublicKey, + getPerpMarketPublicKey, + getSpotMarketPublicKey, +} from '../addresses/pda'; +import { Context, PublicKey } from '@solana/web3.js'; +import { + Commitment, + SolanaRpcSubscriptionsApi, + Rpc, + RpcSubscriptions, + createSolanaClient, +} from 'gill'; +import { OracleInfo, OraclePriceData } from '../oracles/types'; +import { OracleClientCache } from '../oracles/oracleClientCache'; +import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient'; +import { findAllMarketAndOracles } from '../config'; +import { findDelistedPerpMarketsAndOracles } from './utils'; +import { + getOracleId, + getPublicKeyAndSourceFromOracleId, +} from '../oracles/oracleId'; +import { OracleSource } from '../types'; +import { + getPerpMarketAccountsFilter, + getSpotMarketAccountsFilter, +} from '../memcmp'; +import { WebSocketProgramAccountsSubscriberV2 } from './webSocketProgramAccountsSubscriberV2'; +import { WebSocketAccountSubscriberV2 } from './webSocketAccountSubscriberV2'; +const ORACLE_DEFAULT_ID = getOracleId( + PublicKey.default, + OracleSource.QUOTE_ASSET +); + +export class WebSocketDriftClientAccountSubscriberV2 + implements DriftClientAccountSubscriber +{ + isSubscribed: boolean; + program: Program; + commitment?: Commitment; + perpMarketIndexes: number[]; + spotMarketIndexes: number[]; + oracleInfos: OracleInfo[]; + oracleClientCache = new OracleClientCache(); + + resubOpts?: ResubOpts; + shouldFindAllMarketsAndOracles: boolean; + skipInitialData: boolean = true; + + eventEmitter: StrictEventEmitter; + stateAccountSubscriber?: WebSocketAccountSubscriberV2; + perpMarketAllAccountsSubscriber: WebSocketProgramAccountsSubscriberV2; + perpMarketAccountLatestData = new Map< + number, + DataAndSlot + >(); + spotMarketAllAccountsSubscriber: WebSocketProgramAccountsSubscriberV2; + spotMarketAccountLatestData = new Map< + number, + DataAndSlot + >(); + perpOracleMap = new Map(); + perpOracleStringMap = new Map(); + spotOracleMap = new Map(); + spotOracleStringMap = new Map(); + oracleSubscribers = new Map>(); + delistedMarketSetting: DelistedMarketSetting; + + initialPerpMarketAccountData: Map; + initialSpotMarketAccountData: Map; + initialOraclePriceData: Map; + + protected isSubscribing = false; + protected subscriptionPromise: Promise; + protected subscriptionPromiseResolver: (val: boolean) => void; + + private rpc: Rpc; + private rpcSubscriptions: RpcSubscriptions & + string; + + public constructor( + program: Program, + perpMarketIndexes: number[], + spotMarketIndexes: number[], + oracleInfos: OracleInfo[], + shouldFindAllMarketsAndOracles: boolean, + delistedMarketSetting: DelistedMarketSetting, + resubOpts?: ResubOpts, + commitment?: Commitment, + skipInitialData?: boolean + ) { + this.isSubscribed = false; + this.program = program; + this.eventEmitter = new EventEmitter(); + this.perpMarketIndexes = perpMarketIndexes; + this.spotMarketIndexes = spotMarketIndexes; + this.oracleInfos = oracleInfos; + this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles; + this.delistedMarketSetting = delistedMarketSetting; + this.resubOpts = resubOpts; + this.commitment = commitment; + this.skipInitialData = skipInitialData ?? false; + + const { rpc, rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: this.program.provider.connection.rpcEndpoint, + }); + this.rpc = rpc; + this.rpcSubscriptions = rpcSubscriptions; + } + + public async subscribe(): Promise { + try { + const startTime = performance.now(); + if (this.isSubscribed) { + console.log( + `[PROFILING] WebSocketDriftClientAccountSubscriberV2.subscribe() skipped - already subscribed` + ); + return true; + } + + if (this.isSubscribing) { + console.log( + `[PROFILING] WebSocketDriftClientAccountSubscriberV2.subscribe() waiting for existing subscription` + ); + return await this.subscriptionPromise; + } + + this.isSubscribing = true; + + // Initialize subscriptionPromiseResolver to a no-op function + this.subscriptionPromiseResolver = () => {}; + + this.subscriptionPromise = new Promise((res) => { + this.subscriptionPromiseResolver = res; + }); + + const [perpMarketAccountPubkeys, spotMarketAccountPubkeys] = + await Promise.all([ + Promise.all( + this.perpMarketIndexes.map((marketIndex) => + getPerpMarketPublicKey(this.program.programId, marketIndex) + ) + ), + Promise.all( + this.spotMarketIndexes.map((marketIndex) => + getSpotMarketPublicKey(this.program.programId, marketIndex) + ) + ), + ]); + + // Profile findAllMarketsAndOracles if needed + let findAllMarketsDuration = 0; + if (this.shouldFindAllMarketsAndOracles) { + const findAllMarketsStartTime = performance.now(); + const { + perpMarketIndexes, + perpMarketAccounts, + spotMarketIndexes, + spotMarketAccounts, + oracleInfos, + } = await findAllMarketAndOracles(this.program); + this.perpMarketIndexes = perpMarketIndexes; + this.spotMarketIndexes = spotMarketIndexes; + this.oracleInfos = oracleInfos; + // front run and set the initial data here to save extra gma call in set initial data + this.initialPerpMarketAccountData = new Map( + perpMarketAccounts.map((market) => [market.marketIndex, market]) + ); + this.initialSpotMarketAccountData = new Map( + spotMarketAccounts.map((market) => [market.marketIndex, market]) + ); + const findAllMarketsEndTime = performance.now(); + findAllMarketsDuration = + findAllMarketsEndTime - findAllMarketsStartTime; + console.log( + `[PROFILING] findAllMarketAndOracles completed in ${findAllMarketsDuration.toFixed( + 2 + )}ms (${perpMarketAccounts.length} perp markets, ${ + spotMarketAccounts.length + } spot markets)` + ); + } + + // Create subscribers + this.perpMarketAllAccountsSubscriber = + new WebSocketProgramAccountsSubscriberV2( + 'PerpMarketAccountsSubscriber', + 'PerpMarket', + this.program, + this.program.account.perpMarket.coder.accounts.decodeUnchecked.bind( + this.program.account.perpMarket.coder.accounts + ), + { + filters: [getPerpMarketAccountsFilter()], + commitment: this.commitment, + }, + this.resubOpts, + perpMarketAccountPubkeys // because we pass these in, it will monitor these accounts and fetch them right away + ); + + this.spotMarketAllAccountsSubscriber = + new WebSocketProgramAccountsSubscriberV2( + 'SpotMarketAccountsSubscriber', + 'SpotMarket', + this.program, + this.program.account.spotMarket.coder.accounts.decodeUnchecked.bind( + this.program.account.spotMarket.coder.accounts + ), + { + filters: [getSpotMarketAccountsFilter()], + commitment: this.commitment, + }, + this.resubOpts, + spotMarketAccountPubkeys // because we pass these in, it will monitor these accounts and fetch them right away + ); + + // Run all subscriptions in parallel + await Promise.all([ + // Perp market subscription + this.perpMarketAllAccountsSubscriber.subscribe( + ( + _accountId: PublicKey, + data: PerpMarketAccount, + context: Context, + _buffer: Buffer + ) => { + if ( + this.delistedMarketSetting !== DelistedMarketSetting.Subscribe && + isVariant(data.status, 'delisted') + ) { + return; + } + this.perpMarketAccountLatestData.set(data.marketIndex, { + data, + slot: context.slot, + }); + this.eventEmitter.emit('perpMarketAccountUpdate', data); + this.eventEmitter.emit('update'); + } + ), + // Spot market subscription + this.spotMarketAllAccountsSubscriber.subscribe( + ( + _accountId: PublicKey, + data: SpotMarketAccount, + context: Context, + _buffer: Buffer + ) => { + if ( + this.delistedMarketSetting !== DelistedMarketSetting.Subscribe && + isVariant(data.status, 'delisted') + ) { + return; + } + this.spotMarketAccountLatestData.set(data.marketIndex, { + data, + slot: context.slot, + }); + this.eventEmitter.emit('spotMarketAccountUpdate', data); + this.eventEmitter.emit('update'); + } + ), + // State account subscription + (async () => { + const statePublicKey = await getDriftStateAccountPublicKey( + this.program.programId + ); + this.stateAccountSubscriber = new WebSocketAccountSubscriberV2( + 'state', + this.program, + statePublicKey, + undefined, + undefined, + this.commitment as Commitment, + this.rpcSubscriptions, + this.rpc + ); + await Promise.all([ + this.stateAccountSubscriber.fetch(), + this.stateAccountSubscriber.subscribe((data: StateAccount) => { + this.eventEmitter.emit('stateAccountUpdate', data); + this.eventEmitter.emit('update'); + }), + ]); + })(), + (async () => { + await this.setInitialData(); + const subscribeToOraclesStartTime = performance.now(); + await this.subscribeToOracles(); + const subscribeToOraclesEndTime = performance.now(); + const duration = + subscribeToOraclesEndTime - subscribeToOraclesStartTime; + return duration; + })(), + ]); + + // const initialPerpMarketDataFromLatestData = new Map( + // Array.from(this.perpMarketAccountLatestData.values()).map((data) => [ + // data.data.marketIndex, + // data.data, + // ]) + // ); + // const initialSpotMarketDataFromLatestData = new Map( + // Array.from(this.spotMarketAccountLatestData.values()).map((data) => [ + // data.data.marketIndex, + // data.data, + // ]) + // ); + // this.initialPerpMarketAccountData = initialPerpMarketDataFromLatestData; + // this.initialSpotMarketAccountData = initialSpotMarketDataFromLatestData; + + await this.handleDelistedMarketOracles(); + + await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]); + + this.eventEmitter.emit('update'); + // delete initial data + this.removeInitialData(); + + const totalDuration = performance.now() - startTime; + console.log( + `[PROFILING] WebSocketDriftClientAccountSubscriberV2.subscribe() completed in ${totalDuration.toFixed( + 2 + )}ms` + ); + + // Resolve the subscription promise + this.isSubscribed = true; + this.isSubscribing = false; + // Before calling subscriptionPromiseResolver, check if it's defined + if (this.subscriptionPromiseResolver) { + this.subscriptionPromiseResolver(true); + } + + return true; + } catch (error) { + console.error('Subscription failed:', error); + this.isSubscribing = false; + this.subscriptionPromiseResolver(false); + return false; + } + } + + chunks = (array: readonly T[], size: number): T[][] => { + const result: T[][] = []; + for (let i = 0; i < array.length; i += size) { + result.push(array.slice(i, i + size)); + } + return result; + }; + + public async fetch(): Promise { + await this.setInitialData(); + } + + /** + * This is a no-op method that always returns true. + * Unlike the previous implementation, we don't need to manually subscribe to individual perp markets + * because we automatically receive updates for all program account changes via a single websocket subscription. + * This means any new perp markets will automatically be included without explicit subscription. + * @param marketIndex The perp market index to add (unused) + * @returns Promise that resolves to true + */ + public addPerpMarket(_marketIndex: number): Promise { + return Promise.resolve(true); + } + + /** + * This is a no-op method that always returns true. + * Unlike the previous implementation, we don't need to manually subscribe to individual spot markets + * because we automatically receive updates for all program account changes via a single websocket subscription. + * This means any new spot markets will automatically be included without explicit subscription. + * @param marketIndex The spot market index to add (unused) + * @returns Promise that resolves to true + */ + public addSpotMarket(_marketIndex: number): Promise { + return Promise.resolve(true); + } + + // TODO: need more options to skip loading perp market and spot market data. Because of how we fetch within the program account subscribers, I am commenting this all out + async setInitialData(): Promise { + const connection = this.program.provider.connection; + // Profile oracle initial data setup + const oracleSetupStartTime = performance.now(); + const oracleAccountPubkeyChunks = this.chunks( + this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey), + 100 + ); + const oracleAccountInfos = ( + await Promise.all( + oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) => + connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk) + ) + ) + ).flat(); + this.initialOraclePriceData = new Map( + this.oracleInfos.reduce((result, oracleInfo, i) => { + if (!oracleAccountInfos[i]) { + return result; + } + + const oracleClient = this.oracleClientCache.get( + oracleInfo.source, + connection, + this.program + ); + + const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer( + oracleAccountInfos[i].data + ); + + result.push([ + getOracleId(oracleInfo.publicKey, oracleInfo.source), + oraclePriceData, + ]); + return result; + }, []) + ); + const oracleSetupEndTime = performance.now(); + const oracleSetupDuration = oracleSetupEndTime - oracleSetupStartTime; + if (this.resubOpts?.logResubMessages) { + console.log( + `[PROFILING] Oracle initial data setup completed in ${oracleSetupDuration.toFixed( + 2 + )}ms (${this.initialOraclePriceData.size} oracles)` + ); + } + + // emit initial oracle price data + Array.from(this.initialOraclePriceData.entries()).forEach( + ([oracleId, oraclePriceData]) => { + const { publicKey, source } = + getPublicKeyAndSourceFromOracleId(oracleId); + this.eventEmitter.emit( + 'oraclePriceUpdate', + publicKey, + source, + oraclePriceData + ); + } + ); + this.eventEmitter.emit('update'); + } + + removeInitialData() { + this.initialPerpMarketAccountData = new Map(); + this.initialSpotMarketAccountData = new Map(); + this.initialOraclePriceData = new Map(); + } + + async subscribeToOracles(): Promise { + const startTime = performance.now(); + + // Filter out default oracles and duplicates to avoid unnecessary subscriptions + const validOracleInfos = this.oracleInfos.filter( + (oracleInfo) => + !this.oracleSubscribers.has( + getOracleId(oracleInfo.publicKey, oracleInfo.source) + ) + ); + + await Promise.all( + validOracleInfos.map((oracleInfo) => this.subscribeToOracle(oracleInfo)) + ); + + const totalDuration = performance.now() - startTime; + console.log( + `[PROFILING] subscribeToOracles() completed in ${totalDuration.toFixed( + 2 + )}ms` + ); + + return true; + } + + async subscribeToOracle(oracleInfo: OracleInfo): Promise { + try { + const oracleId = getOracleId(oracleInfo.publicKey, oracleInfo.source); + + const client = this.oracleClientCache.get( + oracleInfo.source, + this.program.provider.connection, + this.program + ); + const accountSubscriber = + new WebSocketAccountSubscriberV2( + 'oracle', + this.program, + oracleInfo.publicKey, + (buffer: Buffer) => { + return client.getOraclePriceDataFromBuffer(buffer); + }, + this.resubOpts, + this.commitment, + this.rpcSubscriptions, + this.rpc + ); + const initialOraclePriceData = this.initialOraclePriceData?.get(oracleId); + if (initialOraclePriceData) { + accountSubscriber.setData(initialOraclePriceData); + } + await accountSubscriber.subscribe((data: OraclePriceData) => { + this.eventEmitter.emit( + 'oraclePriceUpdate', + oracleInfo.publicKey, + oracleInfo.source, + data + ); + this.eventEmitter.emit('update'); + }); + + this.oracleSubscribers.set(oracleId, accountSubscriber); + + return true; + } catch (error) { + console.error( + `Failed to subscribe to oracle ${oracleInfo.publicKey.toString()}:`, + error + ); + return false; + } + } + + async unsubscribeFromMarketAccounts(): Promise { + await this.perpMarketAllAccountsSubscriber.unsubscribe(); + } + + async unsubscribeFromSpotMarketAccounts(): Promise { + await this.spotMarketAllAccountsSubscriber.unsubscribe(); + } + + async unsubscribeFromOracles(): Promise { + await Promise.all( + Array.from(this.oracleSubscribers.values()).map((accountSubscriber) => + accountSubscriber.unsubscribe() + ) + ); + } + + public async unsubscribe(): Promise { + if (!this.isSubscribed) { + return; + } + + if (this.subscriptionPromise) { + await this.subscriptionPromise; + } + await Promise.all([ + this.stateAccountSubscriber?.unsubscribe(), + this.unsubscribeFromMarketAccounts(), + this.unsubscribeFromSpotMarketAccounts(), + this.unsubscribeFromOracles(), + ]); + + this.isSubscribed = false; + this.isSubscribing = false; + this.subscriptionPromiseResolver = () => {}; + } + + async addOracle(oracleInfo: OracleInfo): Promise { + const oracleId = getOracleId(oracleInfo.publicKey, oracleInfo.source); + if (this.oracleSubscribers.has(oracleId)) { + return true; + } + + if (oracleInfo.publicKey.equals(PublicKey.default)) { + return true; + } + + return this.subscribeToOracle(oracleInfo); + } + + async setPerpOracleMap() { + const perpMarkets = this.getMarketAccountsAndSlots(); + const addOraclePromises = []; + for (const perpMarket of perpMarkets) { + if (!perpMarket || !perpMarket.data) { + continue; + } + const perpMarketAccount = perpMarket.data; + const perpMarketIndex = perpMarketAccount.marketIndex; + const oracle = perpMarketAccount.amm.oracle; + const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource); + if (!this.oracleSubscribers.has(oracleId)) { + addOraclePromises.push( + this.addOracle({ + publicKey: oracle, + source: perpMarket.data.amm.oracleSource, + }) + ); + } + this.perpOracleMap.set(perpMarketIndex, oracle); + this.perpOracleStringMap.set(perpMarketIndex, oracleId); + } + await Promise.all(addOraclePromises); + } + + async setSpotOracleMap() { + const spotMarkets = this.getSpotMarketAccountsAndSlots(); + const addOraclePromises = []; + for (const spotMarket of spotMarkets) { + if (!spotMarket || !spotMarket.data) { + continue; + } + const spotMarketAccount = spotMarket.data; + const spotMarketIndex = spotMarketAccount.marketIndex; + const oracle = spotMarketAccount.oracle; + const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource); + if (!this.oracleSubscribers.has(oracleId)) { + addOraclePromises.push( + this.addOracle({ + publicKey: oracle, + source: spotMarketAccount.oracleSource, + }) + ); + } + this.spotOracleMap.set(spotMarketIndex, oracle); + this.spotOracleStringMap.set(spotMarketIndex, oracleId); + } + await Promise.all(addOraclePromises); + } + + async handleDelistedMarketOracles(): Promise { + if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) { + return; + } + + const { oracles } = findDelistedPerpMarketsAndOracles( + this.getMarketAccountsAndSlots(), + this.getSpotMarketAccountsAndSlots() + ); + + for (const oracle of oracles) { + const oracleId = getOracleId(oracle.publicKey, oracle.source); + if (this.oracleSubscribers.has(oracleId)) { + await this.oracleSubscribers.get(oracleId).unsubscribe(); + if (this.delistedMarketSetting === DelistedMarketSetting.Discard) { + this.oracleSubscribers.delete(oracleId); + } + } + } + } + + assertIsSubscribed(): void { + if (!this.isSubscribed) { + throw new NotSubscribedError( + 'You must call `subscribe` before using this function' + ); + } + } + + public getStateAccountAndSlot(): DataAndSlot { + this.assertIsSubscribed(); + return this.stateAccountSubscriber.dataAndSlot; + } + + public getMarketAccountAndSlot( + marketIndex: number + ): DataAndSlot | undefined { + this.assertIsSubscribed(); + return this.perpMarketAccountLatestData.get(marketIndex); + } + + public getMarketAccountsAndSlots(): DataAndSlot[] { + return Array.from(this.perpMarketAccountLatestData.values()); + } + + public getSpotMarketAccountAndSlot( + marketIndex: number + ): DataAndSlot | undefined { + this.assertIsSubscribed(); + return this.spotMarketAccountLatestData.get(marketIndex); + } + + public getSpotMarketAccountsAndSlots(): DataAndSlot[] { + return Array.from(this.spotMarketAccountLatestData.values()); + } + + public getOraclePriceDataAndSlot( + oracleId: string + ): DataAndSlot | undefined { + this.assertIsSubscribed(); + if (oracleId === ORACLE_DEFAULT_ID) { + return { + data: QUOTE_ORACLE_PRICE_DATA, + slot: 0, + }; + } + return this.oracleSubscribers.get(oracleId)?.dataAndSlot; + } + + public getOraclePriceDataAndSlotForPerpMarket( + marketIndex: number + ): DataAndSlot | undefined { + const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex); + const oracle = this.perpOracleMap.get(marketIndex); + const oracleId = this.perpOracleStringMap.get(marketIndex); + if (!perpMarketAccount || !oracleId) { + return undefined; + } + + if (!perpMarketAccount.data.amm.oracle.equals(oracle)) { + // If the oracle has changed, we need to update the oracle map in background + this.setPerpOracleMap(); + } + + return this.getOraclePriceDataAndSlot(oracleId); + } + + public getOraclePriceDataAndSlotForSpotMarket( + marketIndex: number + ): DataAndSlot | undefined { + const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex); + const oracle = this.spotOracleMap.get(marketIndex); + const oracleId = this.spotOracleStringMap.get(marketIndex); + if (!spotMarketAccount || !oracleId) { + return undefined; + } + + if (!spotMarketAccount.data.oracle.equals(oracle)) { + // If the oracle has changed, we need to update the oracle map in background + this.setSpotOracleMap(); + } + + return this.getOraclePriceDataAndSlot(oracleId); + } +} diff --git a/sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts b/sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts deleted file mode 100644 index 1176b86589..0000000000 --- a/sdk/src/accounts/webSocketProgramAccountSubscriberV2.ts +++ /dev/null @@ -1,596 +0,0 @@ -import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types'; -import { AnchorProvider, Program } from '@coral-xyz/anchor'; -import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; -import { - AccountInfoBase, - AccountInfoWithBase58EncodedData, - AccountInfoWithBase64EncodedData, - createSolanaClient, - isAddress, - type Address, - type Commitment as GillCommitment, -} from 'gill'; -import bs58 from 'bs58'; - -export class WebSocketProgramAccountSubscriberV2 - implements ProgramAccountSubscriber -{ - subscriptionName: string; - accountDiscriminator: string; - bufferAndSlot?: BufferAndSlot; - bufferAndSlotMap: Map = new Map(); - program: Program; - decodeBuffer: (accountName: string, ix: Buffer) => T; - onChange: ( - accountId: PublicKey, - data: T, - context: Context, - buffer: Buffer - ) => void; - listenerId?: number; - resubOpts?: ResubOpts; - isUnsubscribing = false; - timeoutId?: ReturnType; - options: { filters: MemcmpFilter[]; commitment?: Commitment }; - - receivingData = false; - - // Gill client components - private rpc: ReturnType['rpc']; - private rpcSubscriptions: ReturnType< - typeof createSolanaClient - >['rpcSubscriptions']; - private abortController?: AbortController; - - // Polling logic for specific accounts - private accountsToMonitor: Set = new Set(); - private pollingIntervalMs: number = 30000; // 30 seconds - private pollingTimeouts: Map> = - new Map(); - private lastWsNotificationTime: Map = new Map(); // Track last WS notification time per account - private accountsCurrentlyPolling: Set = new Set(); // Track which accounts are being polled - private batchPollingTimeout?: ReturnType; // Single timeout for batch polling - - public constructor( - subscriptionName: string, - accountDiscriminator: string, - program: Program, - decodeBufferFn: (accountName: string, ix: Buffer) => T, - options: { filters: MemcmpFilter[]; commitment?: Commitment } = { - filters: [], - }, - resubOpts?: ResubOpts, - accountsToMonitor?: PublicKey[] // Optional list of accounts to poll - ) { - this.subscriptionName = subscriptionName; - this.accountDiscriminator = accountDiscriminator; - this.program = program; - this.decodeBuffer = decodeBufferFn; - this.resubOpts = resubOpts; - if (this.resubOpts?.resubTimeoutMs < 1000) { - console.log( - 'resubTimeoutMs should be at least 1000ms to avoid spamming resub' - ); - } - this.options = options; - this.receivingData = false; - - // Initialize accounts to monitor - if (accountsToMonitor) { - accountsToMonitor.forEach((account) => { - this.accountsToMonitor.add(account.toBase58()); - }); - } - - // Initialize gill client using the same RPC URL as the program provider - const rpcUrl = (this.program.provider as AnchorProvider).connection - .rpcEndpoint; - const { rpc, rpcSubscriptions } = createSolanaClient({ - urlOrMoniker: rpcUrl, - }); - this.rpc = rpc; - this.rpcSubscriptions = rpcSubscriptions; - } - - async subscribe( - onChange: ( - accountId: PublicKey, - data: T, - context: Context, - buffer: Buffer - ) => void - ): Promise { - if (this.listenerId != null || this.isUnsubscribing) { - return; - } - - this.onChange = onChange; - - // Create abort controller for proper cleanup - const abortController = new AbortController(); - this.abortController = abortController; - - // Subscribe to program account changes using gill's rpcSubscriptions - const programId = this.program.programId.toBase58(); - if (isAddress(programId)) { - const subscription = await this.rpcSubscriptions - .programNotifications(programId, { - commitment: this.options.commitment as GillCommitment, - encoding: 'base64', - filters: this.options.filters.map((filter) => ({ - memcmp: { - offset: BigInt(filter.memcmp.offset), - bytes: filter.memcmp.bytes as any, - encoding: 'base64' as const, - }, - })), - }) - .subscribe({ - abortSignal: abortController.signal, - }); - - for await (const notification of subscription) { - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - clearTimeout(this.timeoutId); - this.handleRpcResponse( - notification.context, - notification.value.account - ); - this.setTimeout(); - } else { - this.handleRpcResponse( - notification.context, - notification.value.account - ); - } - } - } - - this.listenerId = Math.random(); // Unique ID for logging purposes - - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - this.setTimeout(); - } - - // Start monitoring for accounts that may need polling if no WS event is received - this.startMonitoringForAccounts(); - } - - protected setTimeout(): void { - if (!this.onChange) { - throw new Error('onChange callback function must be set'); - } - this.timeoutId = setTimeout( - async () => { - if (this.isUnsubscribing) { - // If we are in the process of unsubscribing, do not attempt to resubscribe - return; - } - - if (this.receivingData) { - if (this.resubOpts?.logResubMessages) { - console.log( - `No ws data from ${this.subscriptionName} in ${this.resubOpts?.resubTimeoutMs}ms, resubscribing` - ); - } - await this.unsubscribe(true); - this.receivingData = false; - await this.subscribe(this.onChange); - } - }, - this.resubOpts?.resubTimeoutMs - ); - } - - handleRpcResponse( - context: { slot: bigint }, - accountInfo?: AccountInfoBase & - (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData) - ): void { - const newSlot = Number(context.slot); - let newBuffer: Buffer | undefined = undefined; - - if (accountInfo) { - // Extract data from gill response - if (accountInfo.data) { - // Handle different data formats from gill - if (Array.isArray(accountInfo.data)) { - // If it's a tuple [data, encoding] - const [data, encoding] = accountInfo.data; - - if (encoding === ('base58' as any)) { - // Convert base58 to buffer using bs58 - newBuffer = Buffer.from(bs58.decode(data)); - } else { - newBuffer = Buffer.from(data, 'base64'); - } - } - } - } - - // Convert gill's account key to PublicKey - // Note: accountInfo doesn't have a key property, we need to get it from the notification - // For now, we'll use a placeholder - this needs to be fixed based on the actual gill API - const accountId = new PublicKey('11111111111111111111111111111111'); // Placeholder - const accountIdString = accountId.toBase58(); - - const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString); - - // Track WebSocket notification time for this account - this.lastWsNotificationTime.set(accountIdString, Date.now()); - - // If this account was being polled, stop polling it - if (this.accountsCurrentlyPolling.has(accountIdString)) { - this.accountsCurrentlyPolling.delete(accountIdString); - - // If no more accounts are being polled, stop batch polling - if ( - this.accountsCurrentlyPolling.size === 0 && - this.batchPollingTimeout - ) { - clearTimeout(this.batchPollingTimeout); - this.batchPollingTimeout = undefined; - } - } - - if (!existingBufferAndSlot) { - if (newBuffer) { - this.bufferAndSlotMap.set(accountIdString, { - buffer: newBuffer, - slot: newSlot, - }); - const account = this.decodeBuffer(this.accountDiscriminator, newBuffer); - this.onChange(accountId, account, { slot: newSlot }, newBuffer); - } - return; - } - - if (newSlot < existingBufferAndSlot.slot) { - return; - } - - const oldBuffer = existingBufferAndSlot.buffer; - if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) { - this.bufferAndSlotMap.set(accountIdString, { - buffer: newBuffer, - slot: newSlot, - }); - const account = this.decodeBuffer(this.accountDiscriminator, newBuffer); - this.onChange(accountId, account, { slot: newSlot }, newBuffer); - } - } - - private startMonitoringForAccounts(): void { - // Clear any existing polling timeouts - this.clearPollingTimeouts(); - - // Start monitoring for each account in the accountsToMonitor set - this.accountsToMonitor.forEach((accountIdString) => { - this.startMonitoringForAccount(accountIdString); - }); - } - - private startMonitoringForAccount(accountIdString: string): void { - // Clear existing timeout for this account - const existingTimeout = this.pollingTimeouts.get(accountIdString); - if (existingTimeout) { - clearTimeout(existingTimeout); - } - - // Set up monitoring timeout - only start polling if no WS notification in 30s - const timeoutId = setTimeout(async () => { - // Check if we've received a WS notification for this account recently - const lastNotificationTime = - this.lastWsNotificationTime.get(accountIdString); - const currentTime = Date.now(); - - if ( - !lastNotificationTime || - currentTime - lastNotificationTime >= this.pollingIntervalMs - ) { - // No recent WS notification, start polling - await this.pollAccount(accountIdString); - // Schedule next poll - this.startPollingForAccount(accountIdString); - } else { - // We received a WS notification recently, continue monitoring - this.startMonitoringForAccount(accountIdString); - } - }, this.pollingIntervalMs); - - this.pollingTimeouts.set(accountIdString, timeoutId); - } - - private startPollingForAccount(accountIdString: string): void { - // Add account to polling set - this.accountsCurrentlyPolling.add(accountIdString); - - // If this is the first account being polled, start batch polling - if (this.accountsCurrentlyPolling.size === 1) { - this.startBatchPolling(); - } - } - - private startBatchPolling(): void { - // Clear existing batch polling timeout - if (this.batchPollingTimeout) { - clearTimeout(this.batchPollingTimeout); - } - - // Set up batch polling interval - this.batchPollingTimeout = setTimeout(async () => { - await this.pollAllAccounts(); - // Schedule next batch poll - this.startBatchPolling(); - }, this.pollingIntervalMs); - } - - private async pollAllAccounts(): Promise { - try { - // Get all accounts currently being polled - const accountsToPoll = Array.from(this.accountsCurrentlyPolling); - if (accountsToPoll.length === 0) { - return; - } - - // Fetch all accounts in a single batch request - const accountAddresses = accountsToPoll.map( - (accountId) => accountId as Address - ); - const rpcResponse = await this.rpc - .getMultipleAccounts(accountAddresses, { - commitment: this.options.commitment as GillCommitment, - encoding: 'base64', - }) - .send(); - - const currentSlot = Number(rpcResponse.context.slot); - - // Process each account response - for (let i = 0; i < accountsToPoll.length; i++) { - const accountIdString = accountsToPoll[i]; - const accountInfo = rpcResponse.value[i]; - - if (!accountInfo) { - continue; - } - - const existingBufferAndSlot = - this.bufferAndSlotMap.get(accountIdString); - - if (!existingBufferAndSlot) { - // Account not in our map yet, add it - let newBuffer: Buffer | undefined = undefined; - if (accountInfo.data) { - if (Array.isArray(accountInfo.data)) { - const [data, encoding] = accountInfo.data; - newBuffer = Buffer.from(data, encoding); - } - } - - if (newBuffer) { - this.bufferAndSlotMap.set(accountIdString, { - buffer: newBuffer, - slot: currentSlot, - }); - const account = this.decodeBuffer( - this.accountDiscriminator, - newBuffer - ); - const accountId = new PublicKey(accountIdString); - this.onChange(accountId, account, { slot: currentSlot }, newBuffer); - } - continue; - } - - // Check if we missed an update - if (currentSlot > existingBufferAndSlot.slot) { - let newBuffer: Buffer | undefined = undefined; - if (accountInfo.data) { - if (Array.isArray(accountInfo.data)) { - const [data, encoding] = accountInfo.data; - if (encoding === ('base58' as any)) { - newBuffer = Buffer.from(bs58.decode(data)); - } else { - newBuffer = Buffer.from(data, 'base64'); - } - } - } - - // Check if buffer has changed - if ( - newBuffer && - (!existingBufferAndSlot.buffer || - !newBuffer.equals(existingBufferAndSlot.buffer)) - ) { - if (this.resubOpts?.logResubMessages) { - console.log( - `[${this.subscriptionName}] Batch polling detected missed update for account ${accountIdString}, resubscribing` - ); - } - // We missed an update, resubscribe - await this.unsubscribe(true); - this.receivingData = false; - await this.subscribe(this.onChange); - return; - } - } - } - } catch (error) { - if (this.resubOpts?.logResubMessages) { - console.log( - `[${this.subscriptionName}] Error batch polling accounts:`, - error - ); - } - } - } - - private async pollAccount(accountIdString: string): Promise { - try { - // Fetch current account data using gill's rpc - const accountAddress = accountIdString as Address; - const rpcResponse = await this.rpc - .getAccountInfo(accountAddress, { - commitment: this.options.commitment as GillCommitment, - encoding: 'base64', - }) - .send(); - - const currentSlot = Number(rpcResponse.context.slot); - const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString); - - if (!existingBufferAndSlot) { - // Account not in our map yet, add it - if (rpcResponse.value) { - let newBuffer: Buffer | undefined = undefined; - if (rpcResponse.value.data) { - if (Array.isArray(rpcResponse.value.data)) { - const [data, encoding] = rpcResponse.value.data; - newBuffer = Buffer.from(data, encoding); - } - } - - if (newBuffer) { - this.bufferAndSlotMap.set(accountIdString, { - buffer: newBuffer, - slot: currentSlot, - }); - const account = this.decodeBuffer( - this.accountDiscriminator, - newBuffer - ); - const accountId = new PublicKey(accountIdString); - this.onChange(accountId, account, { slot: currentSlot }, newBuffer); - } - } - return; - } - - // Check if we missed an update - if (currentSlot > existingBufferAndSlot.slot) { - let newBuffer: Buffer | undefined = undefined; - if (rpcResponse.value) { - if (rpcResponse.value.data) { - if (Array.isArray(rpcResponse.value.data)) { - const [data, encoding] = rpcResponse.value.data; - if (encoding === ('base58' as any)) { - newBuffer = Buffer.from(bs58.decode(data)); - } else { - newBuffer = Buffer.from(data, 'base64'); - } - } - } - } - - // Check if buffer has changed - if ( - newBuffer && - (!existingBufferAndSlot.buffer || - !newBuffer.equals(existingBufferAndSlot.buffer)) - ) { - if (this.resubOpts?.logResubMessages) { - console.log( - `[${this.subscriptionName}] Polling detected missed update for account ${accountIdString}, resubscribing` - ); - } - // We missed an update, resubscribe - await this.unsubscribe(true); - this.receivingData = false; - await this.subscribe(this.onChange); - return; - } - } - } catch (error) { - if (this.resubOpts?.logResubMessages) { - console.log( - `[${this.subscriptionName}] Error polling account ${accountIdString}:`, - error - ); - } - } - } - - private clearPollingTimeouts(): void { - this.pollingTimeouts.forEach((timeoutId) => { - clearTimeout(timeoutId); - }); - this.pollingTimeouts.clear(); - - // Clear batch polling timeout - if (this.batchPollingTimeout) { - clearTimeout(this.batchPollingTimeout); - this.batchPollingTimeout = undefined; - } - - // Clear accounts currently polling - this.accountsCurrentlyPolling.clear(); - } - - unsubscribe(onResub = false): Promise { - if (!onResub) { - this.resubOpts.resubTimeoutMs = undefined; - } - this.isUnsubscribing = true; - clearTimeout(this.timeoutId); - this.timeoutId = undefined; - - // Clear polling timeouts - this.clearPollingTimeouts(); - - // Abort the WebSocket subscription - if (this.abortController) { - this.abortController.abort('unsubscribing'); - this.abortController = undefined; - } - - this.listenerId = undefined; - this.isUnsubscribing = false; - - return Promise.resolve(); - } - - // Method to add accounts to the polling list - addAccountToMonitor(accountId: PublicKey): void { - const accountIdString = accountId.toBase58(); - this.accountsToMonitor.add(accountIdString); - - // If already subscribed, start monitoring for this account - if (this.listenerId != null && !this.isUnsubscribing) { - this.startMonitoringForAccount(accountIdString); - } - } - - // Method to remove accounts from the polling list - removeAccountFromMonitor(accountId: PublicKey): void { - const accountIdString = accountId.toBase58(); - this.accountsToMonitor.delete(accountIdString); - - // Clear monitoring timeout for this account - const timeoutId = this.pollingTimeouts.get(accountIdString); - if (timeoutId) { - clearTimeout(timeoutId); - this.pollingTimeouts.delete(accountIdString); - } - - // Remove from currently polling set if it was being polled - this.accountsCurrentlyPolling.delete(accountIdString); - - // If no more accounts are being polled, stop batch polling - if (this.accountsCurrentlyPolling.size === 0 && this.batchPollingTimeout) { - clearTimeout(this.batchPollingTimeout); - this.batchPollingTimeout = undefined; - } - } - - // Method to set polling interval - setPollingInterval(intervalMs: number): void { - this.pollingIntervalMs = intervalMs; - // Restart monitoring with new interval if already subscribed - if (this.listenerId != null && !this.isUnsubscribing) { - this.startMonitoringForAccounts(); - } - } -} diff --git a/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts b/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts new file mode 100644 index 0000000000..51e55ba649 --- /dev/null +++ b/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts @@ -0,0 +1,995 @@ +import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types'; +import { AnchorProvider, Program } from '@coral-xyz/anchor'; +import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; +import { + AccountInfoBase, + AccountInfoWithBase58EncodedData, + AccountInfoWithBase64EncodedData, + createSolanaClient, + isAddress, + Lamports, + Slot, + type Address, + type Commitment as GillCommitment, +} from 'gill'; +import bs58 from 'bs58'; + +type ProgramAccountSubscriptionAsyncIterable = AsyncIterable< + Readonly<{ + context: Readonly<{ + slot: Slot; + }>; + value: Readonly<{ + account: Readonly<{ + executable: boolean; + lamports: Lamports; + owner: Address; + rentEpoch: bigint; + space: bigint; + }> & + Readonly; + pubkey: Address; + }>; + }> +>; +/** + * WebSocketProgramAccountsSubscriberV2 + * + * High-level overview + * - WebSocket-first subscriber for Solana program accounts that also layers in + * targeted polling to detect missed updates reliably. + * - Emits decoded account updates via the provided `onChange` callback. + * - Designed to focus extra work on the specific accounts the consumer cares + * about ("monitored accounts") while keeping baseline WS behavior for the + * full program subscription. + * + * Why polling if this is a WebSocket subscriber? + * - WS infra can stall, drop, or reorder notifications under network stress or + * provider hiccups. When that happens, critical account changes can be missed. + * - To mitigate this, the class accepts a set of accounts (provided via constructor) to monitor + * and uses light polling to verify whether a WS change was missed. + * - If polling detects a newer slot with different data than the last seen + * buffer, a centralized resubscription is triggered to restore a clean stream. + * + * Initial fetch (on subscribe) + * - On `subscribe()`, we first perform a single batched fetch of all monitored + * accounts ("initial monitor fetch"). + * - Purpose: seed the internal `bufferAndSlotMap` and emit the latest state so + * consumers have up-to-date data immediately, even before WS events arrive. + * - This step does not decide resubscription; it only establishes ground truth. + * + * Continuous polling (only for monitored accounts) + * - After seeding, each monitored account is put into a monitoring cycle: + * 1) If no WS notification for an account is observed for `pollingIntervalMs`, + * we enqueue it for a batched fetch (buffered for a short window). + * 2) Once an account enters the "currently polling" set, a shared batch poll + * runs every `pollingIntervalMs` across all such accounts. + * 3) If WS notifications resume for an account, that account is removed from + * the polling set and returns to passive monitoring. + * - Polling compares the newly fetched buffer with the last stored buffer at a + * later slot. A difference indicates a missed update; we schedule a single + * resubscription (coalesced across accounts) to re-sync. + * + * Accounts the consumer cares about + * - Provide accounts up-front via the constructor `accountsToMonitor`, or add + * them dynamically with `addAccountToMonitor()` and remove with + * `removeAccountFromMonitor()`. + * - Only these accounts incur additional polling safeguards; other accounts are + * still processed from the WS stream normally. + * + * Resubscription strategy + * - Missed updates from any monitored account are coalesced and trigger a single + * resubscription after a short delay. This avoids rapid churn. + * - If `resubOpts.resubTimeoutMs` is set, an inactivity timer also performs a + * batch check of monitored accounts. If a missed update is found, the same + * centralized resubscription flow is used. + * + * Tuning knobs + * - `setPollingInterval(ms)`: adjust how often monitoring/polling runs + * (default 30s). Shorter = faster detection, higher RPC load. + * - Debounced immediate poll (~100ms): batches accounts added to polling right after inactivity. + * - Batch size for `getMultipleAccounts` is limited to 100, requests are chunked + * and processed concurrently. + */ + +export class WebSocketProgramAccountsSubscriberV2 + implements ProgramAccountSubscriber +{ + subscriptionName: string; + accountDiscriminator: string; + bufferAndSlotMap: Map = new Map(); + program: Program; + decodeBuffer: (accountName: string, ix: Buffer) => T; + onChange: ( + accountId: PublicKey, + data: T, + context: Context, + buffer: Buffer + ) => void; + listenerId?: number; + resubOpts: ResubOpts; + isUnsubscribing = false; + timeoutId?: ReturnType; + options: { filters: MemcmpFilter[]; commitment?: Commitment }; + + receivingData = false; + + // Gill client components + private rpc: ReturnType['rpc']; + private rpcSubscriptions: ReturnType< + typeof createSolanaClient + >['rpcSubscriptions']; + private abortController?: AbortController; + + // Polling logic for specific accounts + private accountsToMonitor: Set = new Set(); + private pollingIntervalMs: number = 30000; // 30 seconds + private pollingTimeouts: Map> = + new Map(); + private lastWsNotificationTime: Map = new Map(); // Track last WS notification time per account + private accountsCurrentlyPolling: Set = new Set(); // Track which accounts are being polled + private batchPollingTimeout?: ReturnType; // Single timeout for batch polling + + // Debounced immediate poll to batch multiple additions within a short window + private debouncedImmediatePollTimeout?: ReturnType; + private debouncedImmediatePollMs: number = 100; // configurable short window + + // Centralized resubscription handling + private missedChangeDetected = false; // Flag to track if any missed change was detected + private resubscriptionTimeout?: ReturnType; // Timeout for delayed resubscription + private accountsWithMissedUpdates: Set = new Set(); // Track which accounts had missed updates + + public constructor( + subscriptionName: string, + accountDiscriminator: string, + program: Program, + decodeBufferFn: (accountName: string, ix: Buffer) => T, + options: { filters: MemcmpFilter[]; commitment?: Commitment } = { + filters: [], + }, + resubOpts?: ResubOpts, + accountsToMonitor?: PublicKey[] // Optional list of accounts to poll + ) { + this.subscriptionName = subscriptionName; + this.accountDiscriminator = accountDiscriminator; + this.program = program; + this.decodeBuffer = decodeBufferFn; + this.resubOpts = resubOpts ?? { + resubTimeoutMs: 30000, + usePollingInsteadOfResub: true, + logResubMessages: false, + }; + if (this.resubOpts?.resubTimeoutMs < 1000) { + console.log( + 'resubTimeoutMs should be at least 1000ms to avoid spamming resub' + ); + } + this.options = options; + this.receivingData = false; + + // Initialize accounts to monitor + if (accountsToMonitor) { + accountsToMonitor.forEach((account) => { + this.accountsToMonitor.add(account.toBase58()); + }); + } + + // Initialize gill client using the same RPC URL as the program provider + const rpcUrl = (this.program.provider as AnchorProvider).connection + .rpcEndpoint; + const { rpc, rpcSubscriptions } = createSolanaClient({ + urlOrMoniker: rpcUrl, + }); + this.rpc = rpc; + this.rpcSubscriptions = rpcSubscriptions; + } + + private async handleNotificationLoop( + notificationPromise: Promise + ) { + try { + const subscriptionIterable = await notificationPromise; + for await (const notification of subscriptionIterable) { + try { + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + clearTimeout(this.timeoutId); + this.handleRpcResponse( + notification.context, + notification.value.pubkey, + notification.value.account.data + ); + this.setTimeout(); + } else { + this.handleRpcResponse( + notification.context, + notification.value.pubkey, + notification.value.account.data + ); + } + } catch (error) { + console.error( + `Error handling RPC response for pubkey ${notification.value.pubkey}:`, + error + ); + } + } + } catch (error) { + console.error( + `[${this.subscriptionName}] Error in notification loop:`, + error + ); + } + } + + async subscribe( + onChange: ( + accountId: PublicKey, + data: T, + context: Context, + buffer: Buffer + ) => void + ): Promise { + /** + * Start the WebSocket subscription and initialize polling safeguards. + * + * Flow + * - Seeds all monitored accounts with a single batched RPC fetch and emits + * their current state. + * - Subscribes to program notifications via WS using gill. + * - If `resubOpts.resubTimeoutMs` is set, starts an inactivity timer that + * batch-checks monitored accounts when WS goes quiet. + * - Begins monitoring for accounts that may need polling when WS + * notifications are not observed within `pollingIntervalMs`. + * + * @param onChange Callback invoked with decoded account data when an update + * is detected (via WS or batch RPC fetch). + */ + const startTime = performance.now(); + if (this.listenerId != null || this.isUnsubscribing) { + return; + } + + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] initializing subscription. This many monitored accounts: ${this.accountsToMonitor.size}` + ); + } + + this.onChange = onChange; + + // initial fetch of monitored data - only fetch and populate, don't check for missed changes + await this.fetchAndPopulateAllMonitoredAccounts(); + + // Create abort controller for proper cleanup + const abortController = new AbortController(); + this.abortController = abortController; + + this.listenerId = Math.random(); // Unique ID for logging purposes + + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + this.setTimeout(); + } + + // Subscribe to program account changes using gill's rpcSubscriptions + const programId = this.program.programId.toBase58(); + if (isAddress(programId)) { + const subscriptionPromise = this.rpcSubscriptions + .programNotifications(programId, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + filters: this.options.filters.map((filter) => { + // Convert filter bytes from base58 to base64 if needed + let bytes = filter.memcmp.bytes; + if ( + typeof bytes === 'string' && + /^[1-9A-HJ-NP-Za-km-z]+$/.test(bytes) + ) { + // Looks like base58 - convert to base64 + const decoded = bs58.decode(bytes); + bytes = Buffer.from(decoded).toString('base64'); + } + + return { + memcmp: { + offset: BigInt(filter.memcmp.offset), + bytes: bytes as any, + encoding: 'base64' as const, + }, + }; + }), + }) + .subscribe({ + abortSignal: abortController.signal, + }); + + // Start notification loop without awaiting + this.handleNotificationLoop(subscriptionPromise); + // Start monitoring for accounts that may need polling if no WS event is received + this.startMonitoringForAccounts(); + } + const endTime = performance.now(); + console.log( + `[PROFILING] ${this.subscriptionName}.subscribe() completed in ${ + endTime - startTime + }ms` + ); + } + + protected setTimeout(): void { + if (!this.onChange) { + throw new Error('onChange callback function must be set'); + } + this.timeoutId = setTimeout( + async () => { + if (this.isUnsubscribing) { + // If we are in the process of unsubscribing, do not attempt to resubscribe + return; + } + + if (this.receivingData) { + if (this.resubOpts?.logResubMessages) { + console.log( + `No ws data from ${this.subscriptionName} in ${this.resubOpts?.resubTimeoutMs}ms, checking for missed changes` + ); + } + + // Check for missed changes in monitored accounts + const missedChangeDetected = await this.fetchAllMonitoredAccounts(); + + if (missedChangeDetected) { + // Signal missed change with a generic identifier since we don't have specific account IDs from this context + this.signalMissedChange('timeout-check'); + } else { + // No missed changes, continue monitoring + this.receivingData = false; + this.setTimeout(); + } + } + }, + this.resubOpts?.resubTimeoutMs + ); + } + + handleRpcResponse( + context: { slot: bigint }, + accountId: Address, + accountInfo?: AccountInfoBase & + ( + | AccountInfoWithBase58EncodedData + | AccountInfoWithBase64EncodedData + )['data'] + ): void { + const newSlot = Number(context.slot); + let newBuffer: Buffer | undefined = undefined; + + if (accountInfo) { + // Handle different data formats from gill + if (Array.isArray(accountInfo)) { + // If it's a tuple [data, encoding] + const [data, encoding] = accountInfo; + + if (encoding === ('base58' as any)) { + // Convert base58 to buffer using bs58 + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + + const accountIdString = accountId.toString(); + const existingBufferAndSlot = this.bufferAndSlotMap.get(accountIdString); + + // Track WebSocket notification time for this account + this.lastWsNotificationTime.set(accountIdString, Date.now()); + + // If this account was being polled, stop polling it if the buffer has changed + if ( + this.accountsCurrentlyPolling.has(accountIdString) && + !existingBufferAndSlot?.buffer.equals(newBuffer) + ) { + this.accountsCurrentlyPolling.delete(accountIdString); + + // If no more accounts are being polled, stop batch polling + if ( + this.accountsCurrentlyPolling.size === 0 && + this.batchPollingTimeout + ) { + clearTimeout(this.batchPollingTimeout); + this.batchPollingTimeout = undefined; + } + } + + if (!existingBufferAndSlot) { + if (newBuffer) { + this.updateBufferAndHandleChange(newBuffer, newSlot, accountIdString); + } + return; + } + + if (newSlot < existingBufferAndSlot.slot) { + return; + } + + const oldBuffer = existingBufferAndSlot.buffer; + if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) { + this.updateBufferAndHandleChange(newBuffer, newSlot, accountIdString); + } + } + + private startMonitoringForAccounts(): void { + // Clear any existing polling timeouts + this.clearPollingTimeouts(); + + // Start monitoring for each account in the accountsToMonitor set + this.accountsToMonitor.forEach((accountIdString) => { + this.startMonitoringForAccount(accountIdString); + }); + } + + private startMonitoringForAccount(accountIdString: string): void { + // Clear existing timeout for this account + const existingTimeout = this.pollingTimeouts.get(accountIdString); + if (existingTimeout) { + clearTimeout(existingTimeout); + } + + // Set up monitoring timeout - only start polling if no WS notification in 30s + const timeoutId = setTimeout(async () => { + // Check if we've received a WS notification for this account recently + const lastNotificationTime = + this.lastWsNotificationTime.get(accountIdString) || 0; + const currentTime = Date.now(); + + if ( + !lastNotificationTime || + currentTime - lastNotificationTime >= this.pollingIntervalMs + ) { + if (this.resubOpts?.logResubMessages) { + console.debug( + `[${this.subscriptionName}] No recent WS notification for ${accountIdString}, adding to polling set` + ); + } + // No recent WS notification: add to polling and schedule debounced poll + this.accountsCurrentlyPolling.add(accountIdString); + this.scheduleDebouncedImmediatePoll(); + } else { + // We received a WS notification recently, continue monitoring + this.startMonitoringForAccount(accountIdString); + } + }, this.pollingIntervalMs); + + this.pollingTimeouts.set(accountIdString, timeoutId); + } + + private scheduleDebouncedImmediatePoll(): void { + if (this.debouncedImmediatePollTimeout) { + clearTimeout(this.debouncedImmediatePollTimeout); + } + this.debouncedImmediatePollTimeout = setTimeout(async () => { + try { + await this.pollAllAccounts(); + // After the immediate poll, ensure continuous batch polling is active + if ( + !this.batchPollingTimeout && + this.accountsCurrentlyPolling.size > 0 + ) { + this.startBatchPolling(); + } + } catch (e) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error during debounced immediate poll:`, + e + ); + } + } + }, this.debouncedImmediatePollMs); + } + + private startBatchPolling(): void { + if (this.resubOpts?.logResubMessages) { + console.debug(`[${this.subscriptionName}] Scheduling batch polling`); + } + // Clear existing batch polling timeout + if (this.batchPollingTimeout) { + clearTimeout(this.batchPollingTimeout); + } + + // Set up batch polling interval + this.batchPollingTimeout = setTimeout(async () => { + await this.pollAllAccounts(); + // Schedule next batch poll + this.startBatchPolling(); + }, this.pollingIntervalMs); + } + + private async pollAllAccounts(): Promise { + try { + // Get all accounts currently being polled + const accountsToPoll = Array.from(this.accountsCurrentlyPolling); + if (accountsToPoll.length === 0) { + return; + } + + if (this.resubOpts?.logResubMessages) { + console.debug( + `[${this.subscriptionName}] Polling all accounts`, + accountsToPoll.length, + 'accounts' + ); + } + + // Use the shared batch fetch method + await this.fetchAccountsBatch(accountsToPoll); + } catch (error) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error batch polling accounts:`, + error + ); + } + } + } + + /** + * Fetches and populates all monitored accounts data without checking for missed changes + * This is used during initial subscription to populate data + */ + private async fetchAndPopulateAllMonitoredAccounts(): Promise { + try { + // Get all accounts currently being polled + const accountsToMonitor = Array.from(this.accountsToMonitor); + if (accountsToMonitor.length === 0) { + return; + } + + // Fetch all accounts in a single batch request + const accountAddresses = accountsToMonitor.map( + (accountId) => accountId as Address + ); + const rpcResponse = await this.rpc + .getMultipleAccounts(accountAddresses, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + }) + .send(); + + const currentSlot = Number(rpcResponse.context.slot); + + // Process each account response + for (let i = 0; i < accountsToMonitor.length; i++) { + const accountIdString = accountsToMonitor[i]; + const accountInfo = rpcResponse.value[i]; + + if (!accountInfo) { + continue; + } + + const existingBufferAndSlot = + this.bufferAndSlotMap.get(accountIdString); + + if (!existingBufferAndSlot) { + // Account not in our map yet, add it + let newBuffer: Buffer | undefined = undefined; + if (accountInfo) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + newBuffer = Buffer.from(data, encoding); + } + } + + if (newBuffer) { + this.updateBufferAndHandleChange( + newBuffer, + currentSlot, + accountIdString + ); + } + continue; + } + + // For initial population, just update the slot if we have newer data + if (currentSlot > existingBufferAndSlot.slot) { + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + if (encoding === ('base58' as any)) { + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + + // Update with newer data if available + if (newBuffer) { + this.updateBufferAndHandleChange( + newBuffer, + currentSlot, + accountIdString + ); + } + } + } + } catch (error) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error fetching and populating monitored accounts:`, + error + ); + } + } + } + + /** + * Fetches all monitored accounts and checks for missed changes + * Returns true if a missed change was detected and resubscription is needed + */ + private async fetchAllMonitoredAccounts(): Promise { + try { + // Get all accounts currently being polled + const accountsToMonitor = Array.from(this.accountsToMonitor); + if (accountsToMonitor.length === 0) { + return false; + } + + // Fetch all accounts in a single batch request + const accountAddresses = accountsToMonitor.map( + (accountId) => accountId as Address + ); + const rpcResponse = await this.rpc + .getMultipleAccounts(accountAddresses, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + }) + .send(); + + const currentSlot = Number(rpcResponse.context.slot); + + // Process each account response + for (let i = 0; i < accountsToMonitor.length; i++) { + const accountIdString = accountsToMonitor[i]; + const accountInfo = rpcResponse.value[i]; + + if (!accountInfo) { + continue; + } + + const existingBufferAndSlot = + this.bufferAndSlotMap.get(accountIdString); + + if (!existingBufferAndSlot) { + // Account not in our map yet, add it + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + newBuffer = Buffer.from(data, encoding); + } + } + + if (newBuffer) { + this.updateBufferAndHandleChange( + newBuffer, + currentSlot, + accountIdString + ); + } + continue; + } + + // Check if we missed an update + if (currentSlot > existingBufferAndSlot.slot) { + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + if (encoding === ('base58' as any)) { + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + + // Check if buffer has changed + if ( + newBuffer && + (!existingBufferAndSlot.buffer || + !newBuffer.equals(existingBufferAndSlot.buffer)) + ) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Batch polling detected missed update for account ${accountIdString}, resubscribing` + ); + } + // We missed an update, return true to indicate resubscription is needed + return true; + } + } + } + + // No missed changes detected + return false; + } catch (error) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error batch polling accounts:`, + error + ); + } + return false; + } + } + + private async fetchAccountsBatch(accountIds: string[]): Promise { + try { + // Chunk account IDs into groups of 100 (getMultipleAccounts limit) + const chunkSize = 100; + const chunks: string[][] = []; + for (let i = 0; i < accountIds.length; i += chunkSize) { + chunks.push(accountIds.slice(i, i + chunkSize)); + } + + // Process all chunks concurrently + await Promise.all( + chunks.map(async (chunk) => { + const accountAddresses = chunk.map( + (accountId) => accountId as Address + ); + const rpcResponse = await this.rpc + .getMultipleAccounts(accountAddresses, { + commitment: this.options.commitment as GillCommitment, + encoding: 'base64', + }) + .send(); + + const currentSlot = Number(rpcResponse.context.slot); + + // Process each account response in this chunk + for (let i = 0; i < chunk.length; i++) { + const accountIdString = chunk[i]; + const accountInfo = rpcResponse.value[i]; + + if (!accountInfo) { + continue; + } + + const existingBufferAndSlot = + this.bufferAndSlotMap.get(accountIdString); + + if (!existingBufferAndSlot) { + // Account not in our map yet, add it + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + newBuffer = Buffer.from(data, encoding); + } + } + + if (newBuffer) { + this.updateBufferAndHandleChange( + newBuffer, + currentSlot, + accountIdString + ); + } + continue; + } + + // Check if we missed an update + if (currentSlot > existingBufferAndSlot.slot) { + let newBuffer: Buffer | undefined = undefined; + if (accountInfo.data) { + if (Array.isArray(accountInfo.data)) { + const [data, encoding] = accountInfo.data; + if (encoding === ('base58' as any)) { + newBuffer = Buffer.from(bs58.decode(data)); + } else { + newBuffer = Buffer.from(data, 'base64'); + } + } + } + + // Check if buffer has changed + if ( + newBuffer && + (!existingBufferAndSlot.buffer || + !newBuffer.equals(existingBufferAndSlot.buffer)) + ) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Batch polling detected missed update for account ${accountIdString}, signaling resubscription` + ); + } + // Signal missed change instead of immediately resubscribing + this.signalMissedChange(accountIdString); + return; + } + } + } + }) + ); + } catch (error) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Error fetching accounts batch:`, + error + ); + } + } + } + + private clearPollingTimeouts(): void { + this.pollingTimeouts.forEach((timeoutId) => { + clearTimeout(timeoutId); + }); + this.pollingTimeouts.clear(); + + // Clear batch polling timeout + if (this.batchPollingTimeout) { + clearTimeout(this.batchPollingTimeout); + this.batchPollingTimeout = undefined; + } + + // Clear initial fetch timeout + // if (this.initialFetchTimeout) { + // clearTimeout(this.initialFetchTimeout); + // this.initialFetchTimeout = undefined; + // } + + // Clear resubscription timeout + if (this.resubscriptionTimeout) { + clearTimeout(this.resubscriptionTimeout); + this.resubscriptionTimeout = undefined; + } + + // Clear accounts currently polling + this.accountsCurrentlyPolling.clear(); + + // Clear accounts pending initial monitor fetch + // this.accountsPendingInitialMonitorFetch.clear(); + + // Reset missed change flag and clear accounts with missed updates + this.missedChangeDetected = false; + this.accountsWithMissedUpdates.clear(); + } + + /** + * Centralized resubscription handler that only resubscribes once after checking all accounts + */ + private async handleResubscription(): Promise { + if (this.missedChangeDetected) { + if (this.resubOpts?.logResubMessages) { + console.log( + `[${this.subscriptionName}] Missed change detected for ${ + this.accountsWithMissedUpdates.size + } accounts: ${Array.from(this.accountsWithMissedUpdates).join( + ', ' + )}, resubscribing` + ); + } + await this.unsubscribe(true); + this.receivingData = false; + await this.subscribe(this.onChange); + this.missedChangeDetected = false; + this.accountsWithMissedUpdates.clear(); + } + } + + /** + * Signal that a missed change was detected and schedule resubscription + */ + private signalMissedChange(accountIdString: string): void { + if (!this.missedChangeDetected) { + this.missedChangeDetected = true; + this.accountsWithMissedUpdates.add(accountIdString); + + // Clear any existing resubscription timeout + if (this.resubscriptionTimeout) { + clearTimeout(this.resubscriptionTimeout); + } + + // Schedule resubscription after a short delay to allow for batch processing + this.resubscriptionTimeout = setTimeout(async () => { + await this.handleResubscription(); + }, 100); // 100ms delay to allow for batch processing + } else { + // If already detected, just add the account to the set + this.accountsWithMissedUpdates.add(accountIdString); + } + } + + unsubscribe(onResub = false): Promise { + if (!onResub) { + this.resubOpts.resubTimeoutMs = undefined; + } + this.isUnsubscribing = true; + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + // Clear polling timeouts + this.clearPollingTimeouts(); + + // Abort the WebSocket subscription + if (this.abortController) { + this.abortController.abort('unsubscribing'); + this.abortController = undefined; + } + + this.listenerId = undefined; + this.isUnsubscribing = false; + + return Promise.resolve(); + } + + // Method to add accounts to the polling list + /** + * Add an account to the monitored set. + * - Monitored accounts are subject to initial fetch and periodic batch polls + * if WS notifications are not observed within `pollingIntervalMs`. + */ + addAccountToMonitor(accountId: PublicKey): void { + const accountIdString = accountId.toBase58(); + this.accountsToMonitor.add(accountIdString); + + // If already subscribed, start monitoring for this account + if (this.listenerId != null && !this.isUnsubscribing) { + this.startMonitoringForAccount(accountIdString); + } + } + + // Method to remove accounts from the polling list + removeAccountFromMonitor(accountId: PublicKey): void { + const accountIdString = accountId.toBase58(); + this.accountsToMonitor.delete(accountIdString); + + // Clear monitoring timeout for this account + const timeoutId = this.pollingTimeouts.get(accountIdString); + if (timeoutId) { + clearTimeout(timeoutId); + this.pollingTimeouts.delete(accountIdString); + } + + // Remove from currently polling set if it was being polled + this.accountsCurrentlyPolling.delete(accountIdString); + + // If no more accounts are being polled, stop batch polling + if (this.accountsCurrentlyPolling.size === 0 && this.batchPollingTimeout) { + clearTimeout(this.batchPollingTimeout); + this.batchPollingTimeout = undefined; + } + } + + // Method to set polling interval + /** + * Set the monitoring/polling interval for monitored accounts. + * Shorter intervals detect missed updates sooner but increase RPC load. + */ + setPollingInterval(intervalMs: number): void { + this.pollingIntervalMs = intervalMs; + // Restart monitoring with new interval if already subscribed + if (this.listenerId != null && !this.isUnsubscribing) { + this.startMonitoringForAccounts(); + } + } + + private updateBufferAndHandleChange( + newBuffer: Buffer, + newSlot: number, + accountIdString: string + ) { + this.bufferAndSlotMap.set(accountIdString, { + buffer: newBuffer, + slot: newSlot, + }); + const account = this.decodeBuffer(this.accountDiscriminator, newBuffer); + const accountIdPubkey = new PublicKey(accountIdString); + this.onChange(accountIdPubkey, account, { slot: newSlot }, newBuffer); + } +} diff --git a/sdk/src/accounts/websocketProgramUserAccountSubscriber.ts b/sdk/src/accounts/websocketProgramUserAccountSubscriber.ts new file mode 100644 index 0000000000..6c95afe2d0 --- /dev/null +++ b/sdk/src/accounts/websocketProgramUserAccountSubscriber.ts @@ -0,0 +1,94 @@ +import { + DataAndSlot, + NotSubscribedError, + UserAccountEvents, + UserAccountSubscriber, +} from './types'; +import { Program } from '@coral-xyz/anchor'; +import StrictEventEmitter from 'strict-event-emitter-types'; +import { EventEmitter } from 'events'; +import { Context, PublicKey } from '@solana/web3.js'; +import { WebSocketProgramAccountSubscriber } from './webSocketProgramAccountSubscriber'; +import { UserAccount } from '../types'; + +export class WebSocketProgramUserAccountSubscriber + implements UserAccountSubscriber +{ + isSubscribed: boolean; + eventEmitter: StrictEventEmitter; + + private userAccountPublicKey: PublicKey; + private program: Program; + private programSubscriber: WebSocketProgramAccountSubscriber; + private userAccountAndSlot?: DataAndSlot; + + public constructor( + program: Program, + userAccountPublicKey: PublicKey, + programSubscriber: WebSocketProgramAccountSubscriber + ) { + this.isSubscribed = false; + this.program = program; + this.userAccountPublicKey = userAccountPublicKey; + this.eventEmitter = new EventEmitter(); + this.programSubscriber = programSubscriber; + } + + async subscribe(userAccount?: UserAccount): Promise { + if (this.isSubscribed) { + return true; + } + + if (userAccount) { + this.updateData(userAccount, 0); + } + + this.programSubscriber.onChange = ( + accountId: PublicKey, + data: UserAccount, + context: Context + ) => { + if (accountId.equals(this.userAccountPublicKey)) { + this.updateData(data, context.slot); + this.eventEmitter.emit('userAccountUpdate', data); + this.eventEmitter.emit('update'); + } + }; + + this.isSubscribed = true; + return true; + } + + async fetch(): Promise { + if (!this.isSubscribed) { + throw new NotSubscribedError( + 'Must subscribe before fetching account updates' + ); + } + + const account = await this.program.account.user.fetch( + this.userAccountPublicKey + ); + this.updateData(account as UserAccount, 0); + } + + updateData(userAccount: UserAccount, slot: number): void { + this.userAccountAndSlot = { + data: userAccount, + slot, + }; + } + + async unsubscribe(): Promise { + this.isSubscribed = false; + } + + getUserAccountAndSlot(): DataAndSlot { + if (!this.userAccountAndSlot) { + throw new NotSubscribedError( + 'Must subscribe before getting user account data' + ); + } + return this.userAccountAndSlot; + } +} diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index d1fafa3d05..2ca381e61c 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -137,7 +137,6 @@ import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName'; import { MMOraclePriceData, OraclePriceData } from './oracles/types'; import { DriftClientConfig } from './driftClientConfig'; import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; -import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; import { RetryTxSender } from './tx/retryTxSender'; import { User } from './user'; import { UserSubscriptionConfig } from './userConfig'; @@ -194,6 +193,8 @@ import { getOracleId } from './oracles/oracleId'; import { SignedMsgOrderParams } from './types'; import { sha256 } from '@noble/hashes/sha256'; import { getOracleConfidenceFromMMOracleData } from './oracles/utils'; +import { Commitment } from 'gill'; +import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; type RemainingAccountParams = { userAccounts: UserAccount[]; @@ -371,6 +372,8 @@ export class DriftClient { resubTimeoutMs: config.accountSubscription?.resubTimeoutMs, logResubMessages: config.accountSubscription?.logResubMessages, commitment: config.accountSubscription?.commitment, + programUserAccountSubscriber: + config.accountSubscription?.programUserAccountSubscriber, }; this.userStatsAccountSubscriptionConfig = { type: 'websocket', @@ -436,7 +439,10 @@ export class DriftClient { } ); } else { - this.accountSubscriber = new WebSocketDriftClientAccountSubscriber( + const accountSubscriberClass = + config.accountSubscription?.driftClientAccountSubscriber ?? + WebSocketDriftClientAccountSubscriber; + this.accountSubscriber = new accountSubscriberClass( this.program, config.perpMarketIndexes ?? [], config.spotMarketIndexes ?? [], @@ -447,9 +453,7 @@ export class DriftClient { resubTimeoutMs: config.accountSubscription?.resubTimeoutMs, logResubMessages: config.accountSubscription?.logResubMessages, }, - config.accountSubscription?.commitment, - config.accountSubscription?.perpMarketAccountSubscriber, - config.accountSubscription?.oracleAccountSubscriber + config.accountSubscription?.commitment as Commitment ); } this.eventEmitter = this.accountSubscriber.eventEmitter; @@ -610,7 +614,8 @@ export class DriftClient { public getSpotMarketAccount( marketIndex: number ): SpotMarketAccount | undefined { - return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data; + return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex) + ?.data; } /** @@ -621,7 +626,8 @@ export class DriftClient { marketIndex: number ): Promise { await this.accountSubscriber.fetch(); - return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex).data; + return this.accountSubscriber.getSpotMarketAccountAndSlot(marketIndex) + ?.data; } public getSpotMarketAccounts(): SpotMarketAccount[] { diff --git a/sdk/src/driftClientConfig.ts b/sdk/src/driftClientConfig.ts index ebfa4b6954..b3723a2ae8 100644 --- a/sdk/src/driftClientConfig.ts +++ b/sdk/src/driftClientConfig.ts @@ -5,7 +5,7 @@ import { PublicKey, TransactionVersion, } from '@solana/web3.js'; -import { IWallet, TxParams } from './types'; +import { IWallet, TxParams, UserAccount } from './types'; import { OracleInfo } from './oracles/types'; import { BulkAccountLoader } from './accounts/bulkAccountLoader'; import { DriftEnv } from './config'; @@ -19,6 +19,9 @@ import { import { Coder, Program } from '@coral-xyz/anchor'; import { WebSocketAccountSubscriber } from './accounts/webSocketAccountSubscriber'; import { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscriberV2'; +import { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber'; +import { WebSocketDriftClientAccountSubscriberV2 } from './accounts/webSocketDriftClientAccountSubscriberV2'; +import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; export type DriftClientConfig = { connection: Connection; @@ -63,6 +66,7 @@ export type DriftClientSubscriptionConfig = resubTimeoutMs?: number; logResubMessages?: boolean; commitment?: Commitment; + programUserAccountSubscriber?: WebSocketProgramAccountSubscriber; perpMarketAccountSubscriber?: new ( accountName: string, program: Program, @@ -71,14 +75,17 @@ export type DriftClientSubscriptionConfig = resubOpts?: ResubOpts, commitment?: Commitment ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber; - oracleAccountSubscriber?: new ( - accountName: string, + /** If you use V2 here, whatever you pass for perpMarketAccountSubscriber and oracleAccountSubscriber will be ignored and it will use v2 under the hood regardless */ + driftClientAccountSubscriber?: new ( program: Program, - accountPublicKey: PublicKey, - decodeBuffer?: (buffer: Buffer) => any, - resubOpts?: ResubOpts, - commitment?: Commitment - ) => WebSocketAccountSubscriberV2 | WebSocketAccountSubscriber; + perpMarketIndexes: number[], + spotMarketIndexes: number[], + oracleInfos: OracleInfo[], + shouldFindAllMarketsAndOracles: boolean, + delistedMarketSetting: DelistedMarketSetting + ) => + | WebSocketDriftClientAccountSubscriber + | WebSocketDriftClientAccountSubscriberV2; } | { type: 'polling'; diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 902f4a5337..4214847795 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -12,6 +12,10 @@ export * from './accounts/webSocketDriftClientAccountSubscriber'; export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber'; export * from './accounts/webSocketHighLeverageModeConfigAccountSubscriber'; export { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscriberV2'; +export { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber'; +export { WebSocketProgramUserAccountSubscriber } from './accounts/websocketProgramUserAccountSubscriber'; +export { WebSocketProgramAccountsSubscriberV2 } from './accounts/webSocketProgramAccountsSubscriberV2'; +export { WebSocketDriftClientAccountSubscriberV2 } from './accounts/webSocketDriftClientAccountSubscriberV2'; export * from './accounts/bulkAccountLoader'; export * from './accounts/bulkUserSubscription'; export * from './accounts/bulkUserStatsSubscription'; diff --git a/sdk/src/memcmp.ts b/sdk/src/memcmp.ts index 300f2a75d0..896971ebbf 100644 --- a/sdk/src/memcmp.ts +++ b/sdk/src/memcmp.ts @@ -112,3 +112,20 @@ export function getSignedMsgUserOrdersFilter(): MemcmpFilter { }, }; } + +export function getPerpMarketAccountsFilter(): MemcmpFilter { + return { + memcmp: { + offset: 0, + bytes: bs58.encode(BorshAccountsCoder.accountDiscriminator('PerpMarket')), + }, + }; +} +export function getSpotMarketAccountsFilter(): MemcmpFilter { + return { + memcmp: { + offset: 0, + bytes: bs58.encode(BorshAccountsCoder.accountDiscriminator('SpotMarket')), + }, + }; +} diff --git a/sdk/src/oracles/oracleId.ts b/sdk/src/oracles/oracleId.ts index c5b63e743e..3999d3ce72 100644 --- a/sdk/src/oracles/oracleId.ts +++ b/sdk/src/oracles/oracleId.ts @@ -24,9 +24,43 @@ export function getOracleSourceNum(source: OracleSource): number { throw new Error('Invalid oracle source'); } +export function getOracleSourceFromNum(sourceNum: number): OracleSource { + if (sourceNum === OracleSourceNum.PYTH) return 'pyth'; + if (sourceNum === OracleSourceNum.PYTH_1K) return 'pyth1K'; + if (sourceNum === OracleSourceNum.PYTH_1M) return 'pyth1M'; + if (sourceNum === OracleSourceNum.PYTH_PULL) return 'pythPull'; + if (sourceNum === OracleSourceNum.PYTH_1K_PULL) return 'pyth1KPull'; + if (sourceNum === OracleSourceNum.PYTH_1M_PULL) return 'pyth1MPull'; + if (sourceNum === OracleSourceNum.SWITCHBOARD) return 'switchboard'; + if (sourceNum === OracleSourceNum.QUOTE_ASSET) return 'quoteAsset'; + if (sourceNum === OracleSourceNum.PYTH_STABLE_COIN) return 'pythStableCoin'; + if (sourceNum === OracleSourceNum.PYTH_STABLE_COIN_PULL) + return 'pythStableCoinPull'; + if (sourceNum === OracleSourceNum.PRELAUNCH) return 'prelaunch'; + if (sourceNum === OracleSourceNum.SWITCHBOARD_ON_DEMAND) + return 'switchboardOnDemand'; + if (sourceNum === OracleSourceNum.PYTH_LAZER) return 'pythLazer'; + if (sourceNum === OracleSourceNum.PYTH_LAZER_1K) return 'pythLazer1K'; + if (sourceNum === OracleSourceNum.PYTH_LAZER_1M) return 'pythLazer1M'; + if (sourceNum === OracleSourceNum.PYTH_LAZER_STABLE_COIN) + return 'pythLazerStableCoin'; + throw new Error('Invalid oracle source'); +} + export function getOracleId( publicKey: PublicKey, source: OracleSource ): string { return `${publicKey.toBase58()}-${getOracleSourceNum(source)}`; } + +export function getPublicKeyAndSourceFromOracleId(oracleId: string): { + publicKey: PublicKey; + source: OracleSource; +} { + const [publicKey, source] = oracleId.split('-'); + return { + publicKey: new PublicKey(publicKey), + source: getOracleSourceFromNum(parseInt(source)), + }; +} diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 0c027fa8bb..7cd2c60401 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -75,6 +75,7 @@ import { } from './types'; import { standardizeBaseAssetAmount } from './math/orders'; import { UserStats } from './userStats'; +import { WebSocketProgramUserAccountSubscriber } from './accounts/websocketProgramUserAccountSubscriber'; import { calculateAssetWeight, calculateLiabilityWeight, @@ -149,15 +150,26 @@ export class User { } ); } else { - this.accountSubscriber = new WebSocketUserAccountSubscriber( - config.driftClient.program, - config.userAccountPublicKey, - { - resubTimeoutMs: config.accountSubscription?.resubTimeoutMs, - logResubMessages: config.accountSubscription?.logResubMessages, - }, - config.accountSubscription?.commitment - ); + if ( + config.accountSubscription?.type === 'websocket' && + config.accountSubscription?.programUserAccountSubscriber + ) { + this.accountSubscriber = new WebSocketProgramUserAccountSubscriber( + config.driftClient.program, + config.userAccountPublicKey, + config.accountSubscription.programUserAccountSubscriber + ); + } else { + this.accountSubscriber = new WebSocketUserAccountSubscriber( + config.driftClient.program, + config.userAccountPublicKey, + { + resubTimeoutMs: config.accountSubscription?.resubTimeoutMs, + logResubMessages: config.accountSubscription?.logResubMessages, + }, + config.accountSubscription?.commitment + ); + } } this.eventEmitter = this.accountSubscriber.eventEmitter; } diff --git a/sdk/src/userConfig.ts b/sdk/src/userConfig.ts index f575ea2b58..d5ba2147b9 100644 --- a/sdk/src/userConfig.ts +++ b/sdk/src/userConfig.ts @@ -2,6 +2,8 @@ import { DriftClient } from './driftClient'; import { Commitment, PublicKey } from '@solana/web3.js'; import { BulkAccountLoader } from './accounts/bulkAccountLoader'; import { GrpcConfigs, UserAccountSubscriber } from './accounts/types'; +import { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber'; +import { UserAccount } from './types'; export type UserConfig = { accountSubscription?: UserSubscriptionConfig; @@ -21,6 +23,7 @@ export type UserSubscriptionConfig = resubTimeoutMs?: number; logResubMessages?: boolean; commitment?: Commitment; + programUserAccountSubscriber?: WebSocketProgramAccountSubscriber; } | { type: 'polling'; From 1ace60b595a314cf6196517ae12b561cf6351006 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 05:09:16 +0000 Subject: [PATCH 135/216] sdk: release v2.136.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index d3c8614111..98e9ca4ace 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.0 \ No newline at end of file +2.136.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 596bef9d8b..d91254aaca 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.0", + "version": "2.136.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 22f5e877449945ee7bfd860b203af9584d8df343 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 26 Aug 2025 20:42:18 +0800 Subject: [PATCH 136/216] refactor(sdk): add decimal override for bignum prettyPrint --- sdk/src/factory/bigNum.ts | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/sdk/src/factory/bigNum.ts b/sdk/src/factory/bigNum.ts index f74de0692b..10ad92a320 100644 --- a/sdk/src/factory/bigNum.ts +++ b/sdk/src/factory/bigNum.ts @@ -250,7 +250,8 @@ export class BigNum { public prettyPrint( useTradePrecision?: boolean, - precisionOverride?: number + precisionOverride?: number, + decimalOverride?: number ): string { const [leftSide, rightSide] = this.printShort( useTradePrecision, @@ -258,6 +259,21 @@ export class BigNum { ).split(BigNum.delim); let formattedLeftSide = leftSide; + let formattedRightSide = rightSide; + + // Apply decimal override if specified + if (decimalOverride !== undefined) { + if (decimalOverride === 0) { + formattedRightSide = undefined; + } else { + // If no decimal part exists, create one with zeros + const currentRightSide = rightSide || ''; + // Pad with zeros if needed or truncate if too long + formattedRightSide = currentRightSide + .padEnd(decimalOverride, '0') + .substring(0, decimalOverride); + } + } const isNeg = formattedLeftSide.includes('-'); if (isNeg) { @@ -277,7 +293,7 @@ export class BigNum { } return `${isNeg ? '-' : ''}${formattedLeftSide}${ - rightSide ? `${BigNum.delim}${rightSide}` : '' + formattedRightSide ? `${BigNum.delim}${formattedRightSide}` : '' }`; } @@ -503,15 +519,16 @@ export class BigNum { */ public toNotional( useTradePrecision?: boolean, - precisionOverride?: number + precisionOverride?: number, + decimalOverride?: number ): string { const prefix = `${this.lt(BigNum.zero()) ? `-` : ``}$`; const usingCustomPrecision = - true && (useTradePrecision || precisionOverride); + true && (useTradePrecision || precisionOverride || decimalOverride); let val = usingCustomPrecision - ? this.prettyPrint(useTradePrecision, precisionOverride) + ? this.prettyPrint(useTradePrecision, precisionOverride, decimalOverride) : BigNum.fromPrint(this.toFixed(2), new BN(2)).prettyPrint(); // Append trailing zeroes out to 2 decimal places if not using custom precision From 44e8911d532ec912ca52f369261dd2861b27b493 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:47:35 +0000 Subject: [PATCH 137/216] sdk: release v2.136.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 98e9ca4ace..19532b700d 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.1 \ No newline at end of file +2.136.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index d91254aaca..6432fe1ca1 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.1", + "version": "2.136.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 033a48e100a8273e5fdc47115a85606204f48fb3 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Tue, 26 Aug 2025 13:47:05 -0700 Subject: [PATCH 138/216] sdk: while valid tx sender memory leak fix --- sdk/src/tx/whileValidTxSender.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/tx/whileValidTxSender.ts b/sdk/src/tx/whileValidTxSender.ts index 29a147dd26..28b4ea0f23 100644 --- a/sdk/src/tx/whileValidTxSender.ts +++ b/sdk/src/tx/whileValidTxSender.ts @@ -258,6 +258,7 @@ export class WhileValidTxSender extends BaseTxSender { throw e; } finally { stopWaiting(); + this.untilValid.delete(txid); } return { txSig: txid, slot }; From d281aaa995f789d3392de19bb882b0c793484ec9 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 21:10:54 +0000 Subject: [PATCH 139/216] sdk: release v2.136.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 19532b700d..90ec1cc575 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.2 \ No newline at end of file +2.136.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 6432fe1ca1..42f762dba5 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.2", + "version": "2.136.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From fd14960de2be9fd0bbd63a51d3f2c0ac228f9019 Mon Sep 17 00:00:00 2001 From: asuma Date: Thu, 28 Aug 2025 17:01:44 +0900 Subject: [PATCH 140/216] [ FIX ] `posaune0423/fix tx fee payer` (#1837) --- sdk/src/driftClient.ts | 16 ++++++++-------- sdk/src/tx/baseTxSender.ts | 10 +++++----- sdk/src/tx/txHandler.ts | 12 +++++++----- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 2ca381e61c..ab293636b0 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -1102,7 +1102,7 @@ export class DriftClient { this.wallet.publicKey // only allow payer to initialize own user stats account ), authority: this.wallet.publicKey, - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), @@ -1142,7 +1142,7 @@ export class DriftClient { accounts: { signedMsgUserOrders: signedMsgUserAccountPublicKey, authority, - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -1183,7 +1183,7 @@ export class DriftClient { accounts: { signedMsgUserOrders: signedMsgUserAccountPublicKey, authority, - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, user: await getUserAccountPublicKey( this.program.programId, @@ -1321,7 +1321,7 @@ export class DriftClient { authority ?? this.wallet.publicKey ), authority: authority ?? this.wallet.publicKey, - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -1407,7 +1407,7 @@ export class DriftClient { user: userAccountPublicKey, userStats: this.getUserStatsAccountPublicKey(), authority: this.wallet.publicKey, - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), @@ -1457,7 +1457,7 @@ export class DriftClient { user: userAccountPublicKey, authority: this.wallet.publicKey, userStats: this.getUserStatsAccountPublicKey(), - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -8630,7 +8630,7 @@ export class DriftClient { this.wallet.publicKey // only allow payer to initialize own insurance fund stake account ), authority: this.wallet.publicKey, - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), @@ -9743,7 +9743,7 @@ export class DriftClient { const tx = await asV0Tx({ connection: this.connection, ixs: [pullIx], - payer: this.wallet.publicKey, + payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, computeUnitLimitMultiple: 1.3, lookupTables: await this.fetchAllLookupTableAccounts(), }); diff --git a/sdk/src/tx/baseTxSender.ts b/sdk/src/tx/baseTxSender.ts index 23db04be91..5bc07df98d 100644 --- a/sdk/src/tx/baseTxSender.ts +++ b/sdk/src/tx/baseTxSender.ts @@ -173,18 +173,18 @@ export abstract class BaseTxSender implements TxSender { if (preSigned) { signedTx = tx; - // @ts-ignore - } else if (this.wallet.payer) { - // @ts-ignore - tx.sign((additionalSigners ?? []).concat(this.wallet.payer)); - signedTx = tx; } else { + // Sign with user first for instruction authorities signedTx = await this.txHandler.signVersionedTx( tx, additionalSigners, undefined, this.wallet ); + // Add payer signature if available + if (this.wallet.payer) { + signedTx.sign([this.wallet.payer]); + } } if (opts === undefined) { diff --git a/sdk/src/tx/txHandler.ts b/sdk/src/tx/txHandler.ts index bd9b2b702e..289f2046be 100644 --- a/sdk/src/tx/txHandler.ts +++ b/sdk/src/tx/txHandler.ts @@ -191,7 +191,7 @@ export class TxHandler { [wallet, confirmationOpts] = this.getProps(wallet, confirmationOpts); - tx.feePayer = wallet.publicKey; + tx.feePayer = wallet.payer?.publicKey ?? wallet.publicKey; recentBlockhash = recentBlockhash ? recentBlockhash : await this.getLatestBlockhashForTransaction(); @@ -398,7 +398,7 @@ export class TxHandler { [wallet] = this.getProps(wallet); const message = new TransactionMessage({ - payerKey: wallet.publicKey, + payerKey: wallet.payer?.publicKey ?? wallet.publicKey, recentBlockhash: recentBlockhash.blockhash, instructions: ixs, }).compileToLegacyMessage(); @@ -420,7 +420,7 @@ export class TxHandler { [wallet] = this.getProps(wallet); const message = new TransactionMessage({ - payerKey: wallet.publicKey, + payerKey: wallet.payer?.publicKey ?? wallet.publicKey, recentBlockhash: recentBlockhash.blockhash, instructions: ixs, }).compileToV0Message(lookupTableAccounts); @@ -649,7 +649,8 @@ export class TxHandler { for (const tx of Object.values(txsMap)) { if (!tx) continue; tx.recentBlockhash = recentBlockhash.blockhash; - tx.feePayer = wallet?.publicKey ?? this.wallet?.publicKey; + tx.feePayer = + wallet?.payer?.publicKey ?? wallet?.publicKey ?? this.wallet?.publicKey; // @ts-ignore tx.SIGNATURE_BLOCK_AND_EXPIRY = recentBlockhash; @@ -689,7 +690,8 @@ export class TxHandler { // Extra handling for legacy transactions for (const [_key, tx] of filteredTxEntries) { if (this.isLegacyTransaction(tx)) { - (tx as Transaction).feePayer = wallet.publicKey; + (tx as Transaction).feePayer = + wallet.payer?.publicKey ?? wallet.publicKey; } } From 566a0558d0ef701cdb4fe2bb30369cf68ecb2a35 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 08:06:18 +0000 Subject: [PATCH 141/216] sdk: release v2.136.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 90ec1cc575..b15a8c916c 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.3 \ No newline at end of file +2.136.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 42f762dba5..138eca83cd 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.3", + "version": "2.136.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From e9fd9f9ff8692df21a2224b7541f6e0783bcc13e Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Thu, 28 Aug 2025 11:24:15 -0400 Subject: [PATCH 142/216] sdk: add constant for spot market index 58 (#1840) * sdk: add spot market constant 58 * revert .sh --- sdk/src/constants/spotMarkets.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sdk/src/constants/spotMarkets.ts b/sdk/src/constants/spotMarkets.ts index 7fa6b7e77d..503607398c 100644 --- a/sdk/src/constants/spotMarkets.ts +++ b/sdk/src/constants/spotMarkets.ts @@ -921,6 +921,19 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [ pythFeedId: '0x2ad31d1c4a85fbf2156ce57fab4104124c5ef76a6386375ecfc8da1ed5ce1486', }, + { + symbol: 'LBTC', + marketIndex: 58, + poolId: 0, + oracle: new PublicKey('Fa3VKWbdb9yQ89vA9JfYnR6micY9LwGneoQ1So9JgXHT'), + oracleSource: OracleSource.PYTH_PULL, + mint: new PublicKey('LBTCgU4b3wsFKsPwBn1rRZDx5DoFutM6RPiEt1TPDsY'), + precision: new BN(10).pow(EIGHT), + precisionExp: EIGHT, + pythFeedId: + '0x8f257aab6e7698bb92b15511915e593d6f8eae914452f781874754b03d0c612b', + launchTs: 1756392947000, + }, ]; export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = { From dbac35ac16683fdbd9aa2acadcbaed6e8c8ad45f Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:29:25 +0000 Subject: [PATCH 143/216] sdk: release v2.136.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b15a8c916c..0a4829050f 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.4 \ No newline at end of file +2.136.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 138eca83cd..0ba2bfff55 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.4", + "version": "2.136.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From d327d15b04f93388009d6be77115bb3b1dbcbed2 Mon Sep 17 00:00:00 2001 From: wphan Date: Thu, 28 Aug 2025 09:12:17 -0700 Subject: [PATCH 144/216] Revert "[ FIX ] `posaune0423/fix tx fee payer` (#1837)" (#1841) This reverts commit 8cc07e0e179d4335fbb47f8aef5ae022b7143550. --- sdk/src/driftClient.ts | 16 ++++++++-------- sdk/src/tx/baseTxSender.ts | 10 +++++----- sdk/src/tx/txHandler.ts | 12 +++++------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index ab293636b0..2ca381e61c 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -1102,7 +1102,7 @@ export class DriftClient { this.wallet.publicKey // only allow payer to initialize own user stats account ), authority: this.wallet.publicKey, - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), @@ -1142,7 +1142,7 @@ export class DriftClient { accounts: { signedMsgUserOrders: signedMsgUserAccountPublicKey, authority, - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -1183,7 +1183,7 @@ export class DriftClient { accounts: { signedMsgUserOrders: signedMsgUserAccountPublicKey, authority, - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, user: await getUserAccountPublicKey( this.program.programId, @@ -1321,7 +1321,7 @@ export class DriftClient { authority ?? this.wallet.publicKey ), authority: authority ?? this.wallet.publicKey, - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -1407,7 +1407,7 @@ export class DriftClient { user: userAccountPublicKey, userStats: this.getUserStatsAccountPublicKey(), authority: this.wallet.publicKey, - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), @@ -1457,7 +1457,7 @@ export class DriftClient { user: userAccountPublicKey, authority: this.wallet.publicKey, userStats: this.getUserStatsAccountPublicKey(), - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }, @@ -8630,7 +8630,7 @@ export class DriftClient { this.wallet.publicKey // only allow payer to initialize own insurance fund stake account ), authority: this.wallet.publicKey, - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, rent: anchor.web3.SYSVAR_RENT_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, state: await this.getStatePublicKey(), @@ -9743,7 +9743,7 @@ export class DriftClient { const tx = await asV0Tx({ connection: this.connection, ixs: [pullIx], - payer: this.wallet.payer?.publicKey ?? this.wallet.publicKey, + payer: this.wallet.publicKey, computeUnitLimitMultiple: 1.3, lookupTables: await this.fetchAllLookupTableAccounts(), }); diff --git a/sdk/src/tx/baseTxSender.ts b/sdk/src/tx/baseTxSender.ts index 5bc07df98d..23db04be91 100644 --- a/sdk/src/tx/baseTxSender.ts +++ b/sdk/src/tx/baseTxSender.ts @@ -173,18 +173,18 @@ export abstract class BaseTxSender implements TxSender { if (preSigned) { signedTx = tx; + // @ts-ignore + } else if (this.wallet.payer) { + // @ts-ignore + tx.sign((additionalSigners ?? []).concat(this.wallet.payer)); + signedTx = tx; } else { - // Sign with user first for instruction authorities signedTx = await this.txHandler.signVersionedTx( tx, additionalSigners, undefined, this.wallet ); - // Add payer signature if available - if (this.wallet.payer) { - signedTx.sign([this.wallet.payer]); - } } if (opts === undefined) { diff --git a/sdk/src/tx/txHandler.ts b/sdk/src/tx/txHandler.ts index 289f2046be..bd9b2b702e 100644 --- a/sdk/src/tx/txHandler.ts +++ b/sdk/src/tx/txHandler.ts @@ -191,7 +191,7 @@ export class TxHandler { [wallet, confirmationOpts] = this.getProps(wallet, confirmationOpts); - tx.feePayer = wallet.payer?.publicKey ?? wallet.publicKey; + tx.feePayer = wallet.publicKey; recentBlockhash = recentBlockhash ? recentBlockhash : await this.getLatestBlockhashForTransaction(); @@ -398,7 +398,7 @@ export class TxHandler { [wallet] = this.getProps(wallet); const message = new TransactionMessage({ - payerKey: wallet.payer?.publicKey ?? wallet.publicKey, + payerKey: wallet.publicKey, recentBlockhash: recentBlockhash.blockhash, instructions: ixs, }).compileToLegacyMessage(); @@ -420,7 +420,7 @@ export class TxHandler { [wallet] = this.getProps(wallet); const message = new TransactionMessage({ - payerKey: wallet.payer?.publicKey ?? wallet.publicKey, + payerKey: wallet.publicKey, recentBlockhash: recentBlockhash.blockhash, instructions: ixs, }).compileToV0Message(lookupTableAccounts); @@ -649,8 +649,7 @@ export class TxHandler { for (const tx of Object.values(txsMap)) { if (!tx) continue; tx.recentBlockhash = recentBlockhash.blockhash; - tx.feePayer = - wallet?.payer?.publicKey ?? wallet?.publicKey ?? this.wallet?.publicKey; + tx.feePayer = wallet?.publicKey ?? this.wallet?.publicKey; // @ts-ignore tx.SIGNATURE_BLOCK_AND_EXPIRY = recentBlockhash; @@ -690,8 +689,7 @@ export class TxHandler { // Extra handling for legacy transactions for (const [_key, tx] of filteredTxEntries) { if (this.isLegacyTransaction(tx)) { - (tx as Transaction).feePayer = - wallet.payer?.publicKey ?? wallet.publicKey; + (tx as Transaction).feePayer = wallet.publicKey; } } From e3a8567a0d0f946f917c881ddb5cc65242353a0d Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:17:11 +0000 Subject: [PATCH 145/216] sdk: release v2.136.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 0a4829050f..8870fb0b9b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.5 \ No newline at end of file +2.136.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 0ba2bfff55..d7433a2992 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.5", + "version": "2.136.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 28fb909655e177395210638f9c2535aa31356e20 Mon Sep 17 00:00:00 2001 From: jordy25519 Date: Tue, 2 Sep 2025 19:34:10 +0800 Subject: [PATCH 146/216] =?UTF-8?q?Add=20buildDepositAndPlaceSignedMsgOrde?= =?UTF-8?q?rRequest=20helper=20for=20swift=20deposi=E2=80=A6=20(#1839)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add buildDepositAndPlaceSignedMsgOrderRequest helper for swift deposit to trade requests --- sdk/src/driftClient.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 2ca381e61c..dc45730204 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -6401,6 +6401,40 @@ export class DriftClient { }; } + /** + * Builds a deposit and place request for Swift service + * + * @param depositTx - The signed tx containing a drift deposit (e.g. see `createDepositTxn`) + * @param orderParamsMessage - The order parameters message to sign + * @param delegateSigner - Whether this is a delegate signer + * + * @returns request object for Swift service + */ + public buildDepositAndPlaceSignedMsgOrderRequest( + depositTx: VersionedTransaction, + orderParamsMessage: + | SignedMsgOrderParamsMessage + | SignedMsgOrderParamsDelegateMessage, + delegateSigner?: boolean + ): { + deposit_tx: Buffer; + swift_order: SignedMsgOrderParams; + } { + // Serialize the deposit transaction + const serializedDepositTx = Buffer.from(depositTx.serialize()); + + // Get the signed swift order using the existing method + const swiftOrder = this.signSignedMsgOrderParamsMessage( + orderParamsMessage, + delegateSigner + ); + + return { + deposit_tx: serializedDepositTx, + swift_order: swiftOrder, + }; + } + /* * Borsh encode signedMsg taker order params */ From d10cafa51afa20e4841ab81ea3fc964c33051c66 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:40:20 +0000 Subject: [PATCH 147/216] sdk: release v2.136.0-beta.7 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8870fb0b9b..afae437131 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.6 \ No newline at end of file +2.136.0-beta.7 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index d7433a2992..74a6995a03 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.6", + "version": "2.136.0-beta.7", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 100f01df5bebc5f67b4f062f4ed6cf36140aa4b0 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Wed, 3 Sep 2025 12:48:59 -0400 Subject: [PATCH 148/216] program: update-ref-fee-validate-rules (#1843) * program: update-ref-fee-validate-rules * update changelog --- CHANGELOG.md | 2 ++ programs/drift/src/validation/fee_structure.rs | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b80d006464..3aad1bc8d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: update referral fee validate rules ([#1843](https://github.com/drift-labs/protocol-v2/pull/1843)) + ### Fixes ### Breaking diff --git a/programs/drift/src/validation/fee_structure.rs b/programs/drift/src/validation/fee_structure.rs index c44ff3fd0f..19254b7af0 100644 --- a/programs/drift/src/validation/fee_structure.rs +++ b/programs/drift/src/validation/fee_structure.rs @@ -66,8 +66,8 @@ pub fn validate_fee_tier( fee_tier.maker_rebate_denominator )?; - let referee_discount_valid = fee_tier.referee_fee_numerator <= 20 - && fee_tier.referee_fee_denominator == FEE_PERCENTAGE_DENOMINATOR; // <= 20% + let referee_discount_valid = fee_tier.referee_fee_numerator <= FEE_PERCENTAGE_DENOMINATOR + && fee_tier.referee_fee_denominator == FEE_PERCENTAGE_DENOMINATOR; // <= 100% validate!( referee_discount_valid, @@ -77,8 +77,8 @@ pub fn validate_fee_tier( fee_tier.referee_fee_denominator )?; - let referrer_reward_valid = fee_tier.referrer_reward_numerator <= 20 - && fee_tier.referrer_reward_denominator == FEE_PERCENTAGE_DENOMINATOR; // <= 20% + let referrer_reward_valid = fee_tier.referrer_reward_numerator <= FEE_PERCENTAGE_DENOMINATOR + && fee_tier.referrer_reward_denominator == FEE_PERCENTAGE_DENOMINATOR; // <= 100% validate!( referrer_reward_valid, From 9cfc9ebf7c2810aa7e7fa2186687092f328392f8 Mon Sep 17 00:00:00 2001 From: wphan Date: Wed, 3 Sep 2025 10:09:16 -0700 Subject: [PATCH 149/216] v2.136.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aad1bc8d5..f3dce56271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +### Fixes + +### Breaking + +## [2.136.0] - 2025-09-03 + +### Features + - program: update referral fee validate rules ([#1843](https://github.com/drift-labs/protocol-v2/pull/1843)) ### Fixes diff --git a/Cargo.lock b/Cargo.lock index 0848eca3b4..6c40571441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.135.0" +version = "2.136.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index f3646a3f54..6d9488cca1 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.135.0" +version = "2.136.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index 74a6995a03..5c61843331 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0-beta.7", + "version": "2.136.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index c9eeec1e4d..0d781291fa 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.135.0", + "version": "2.136.0", "name": "drift", "instructions": [ { From 76ef78b8a1167b279f8f4b94db059801d5ba2bab Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:17:50 +0000 Subject: [PATCH 150/216] sdk: release v2.137.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index afae437131..756b9c2950 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.136.0-beta.7 \ No newline at end of file +2.137.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 5c61843331..95e2053984 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.136.0", + "version": "2.137.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From a473de749849585f86f08b324fcf50a9a1d0aa58 Mon Sep 17 00:00:00 2001 From: lil perp Date: Wed, 3 Sep 2025 15:35:29 -0400 Subject: [PATCH 151/216] program: rm lp (#1755) * program: make lp shares reduce only * init * rm more fields * make tests build * start sdk changes * merge master 2 * fix fmt + sqrt_k lower bound and some tests * more test update prog * rm anchor tests * rename of old lp related variables * remove multi AMMLiquiditySplit logic/enums * lint fix * fix tests * CHANGELOG --------- Co-authored-by: 0xbigz <83473873+0xbigz@users.noreply.github.com> --- CHANGELOG.md | 2 + programs/drift/src/controller/amm.rs | 3 +- programs/drift/src/controller/amm/tests.rs | 56 - programs/drift/src/controller/liquidation.rs | 63 +- programs/drift/src/controller/lp/tests.rs | 1818 ---------------- programs/drift/src/controller/mod.rs | 1 - programs/drift/src/controller/orders.rs | 173 +- .../src/controller/orders/amm_jit_tests.rs | 232 +- .../src/controller/orders/amm_lp_jit_tests.rs | 803 +------ programs/drift/src/controller/orders/tests.rs | 21 - programs/drift/src/controller/pnl.rs | 57 +- programs/drift/src/controller/position.rs | 116 +- .../drift/src/controller/position/tests.rs | 671 +----- programs/drift/src/controller/repeg.rs | 2 +- programs/drift/src/instructions/admin.rs | 61 - programs/drift/src/instructions/keeper.rs | 31 - programs/drift/src/instructions/user.rs | 229 +- programs/drift/src/lib.rs | 48 - programs/drift/src/math/amm_jit.rs | 61 +- programs/drift/src/math/amm_jit/tests.rs | 32 +- programs/drift/src/math/bankruptcy.rs | 1 - programs/drift/src/math/cp_curve/tests.rs | 175 +- programs/drift/src/math/funding.rs | 5 +- programs/drift/src/math/funding/tests.rs | 243 +-- programs/drift/src/math/lp.rs | 191 -- programs/drift/src/math/lp/tests.rs | 451 ---- programs/drift/src/math/margin.rs | 16 +- programs/drift/src/math/margin/tests.rs | 148 -- programs/drift/src/math/mod.rs | 1 - programs/drift/src/math/orders.rs | 1 - programs/drift/src/math/position.rs | 85 +- programs/drift/src/state/perp_market.rs | 193 +- programs/drift/src/state/user.rs | 123 +- programs/drift/src/validation/perp_market.rs | 32 +- programs/drift/src/validation/position.rs | 8 - .../accounts/webSocketAccountSubscriberV2.ts | 4 +- .../webSocketProgramAccountsSubscriberV2.ts | 4 +- sdk/src/idl/drift.json | 182 +- sdk/src/math/bankruptcy.ts | 3 +- sdk/src/user.ts | 361 +--- test-scripts/run-anchor-tests.sh | 6 - tests/liquidateMaxLps.ts | 227 -- tests/liquidatePerp.ts | 21 - tests/liquidatePerpAndLp.ts | 527 ----- tests/liquidityProvider.ts | 1896 ----------------- tests/perpLpJit.ts | 1250 ----------- tests/perpLpRiskMitigation.ts | 537 ----- tests/switchboardTxCus.ts | 3 - tests/tradingLP.ts | 281 --- 49 files changed, 167 insertions(+), 11288 deletions(-) delete mode 100644 programs/drift/src/controller/lp/tests.rs delete mode 100644 programs/drift/src/math/lp.rs delete mode 100644 programs/drift/src/math/lp/tests.rs delete mode 100644 tests/liquidateMaxLps.ts delete mode 100644 tests/liquidatePerpAndLp.ts delete mode 100644 tests/liquidityProvider.ts delete mode 100644 tests/perpLpJit.ts delete mode 100644 tests/perpLpRiskMitigation.ts delete mode 100644 tests/tradingLP.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f3dce56271..7d77bbb0f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) + ### Fixes ### Breaking diff --git a/programs/drift/src/controller/amm.rs b/programs/drift/src/controller/amm.rs index e2ba43d736..558d0a9e36 100644 --- a/programs/drift/src/controller/amm.rs +++ b/programs/drift/src/controller/amm.rs @@ -421,8 +421,7 @@ pub fn formulaic_update_k( let new_sqrt_k = bn::U192::from(market.amm.sqrt_k) .safe_mul(bn::U192::from(k_scale_numerator))? - .safe_div(bn::U192::from(k_scale_denominator))? - .max(bn::U192::from(market.amm.user_lp_shares.safe_add(1)?)); + .safe_div(bn::U192::from(k_scale_denominator))?; let update_k_result = get_update_k_result(market, new_sqrt_k, true)?; diff --git a/programs/drift/src/controller/amm/tests.rs b/programs/drift/src/controller/amm/tests.rs index c62e2959d5..a2a33fd6d5 100644 --- a/programs/drift/src/controller/amm/tests.rs +++ b/programs/drift/src/controller/amm/tests.rs @@ -255,62 +255,6 @@ fn iterative_no_bounds_formualic_k_tests() { assert_eq!(market.amm.total_fee_minus_distributions, 985625029); } -#[test] -fn decrease_k_up_to_user_lp_shares() { - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - user_lp_shares: 150 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - concentration_coef: MAX_CONCENTRATION_COEFFICIENT, - base_asset_amount_with_amm: -12295081967, - total_fee_minus_distributions: -100 * QUOTE_PRECISION as i128, - total_fee_withdrawn: 100 * QUOTE_PRECISION, - curve_update_intensity: 100, - ..AMM::default() - }, - ..PerpMarket::default() - }; - // let prev_sqrt_k = market.amm.sqrt_k; - let (new_terminal_quote_reserve, new_terminal_base_reserve) = - amm::calculate_terminal_reserves(&market.amm).unwrap(); - market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve; - let (min_base_asset_reserve, max_base_asset_reserve) = - amm::calculate_bid_ask_bounds(market.amm.concentration_coef, new_terminal_base_reserve) - .unwrap(); - market.amm.min_base_asset_reserve = min_base_asset_reserve; - market.amm.max_base_asset_reserve = max_base_asset_reserve; - - // let reserve_price = market.amm.reserve_price().unwrap(); - let now = 10000; - let oracle_price_data = OraclePriceData { - price: 50 * PRICE_PRECISION_I64, - confidence: 0, - delay: 2, - has_sufficient_number_of_data_points: true, - }; - - // negative funding cost - let mut count = 0; - let mut prev_k = market.amm.sqrt_k; - let mut new_k = 0; - while prev_k != new_k && count < 100000 { - let funding_cost = (QUOTE_PRECISION * 100000) as i128; - prev_k = market.amm.sqrt_k; - formulaic_update_k(&mut market, &oracle_price_data, funding_cost, now).unwrap(); - new_k = market.amm.sqrt_k; - msg!("quote_asset_reserve:{}", market.amm.quote_asset_reserve); - msg!("new_k:{}", new_k); - count += 1 - } - - assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967); - assert_eq!(market.amm.sqrt_k, 162234889619); - assert_eq!(market.amm.total_fee_minus_distributions, 29796232175); -} - #[test] fn update_pool_balances_test_high_util_borrow() { let mut market = PerpMarket { diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 057f4b8168..92d241dbc7 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -5,7 +5,6 @@ use anchor_lang::prelude::*; use crate::controller::amm::get_fee_pool_tokens; use crate::controller::funding::settle_funding_payment; -use crate::controller::lp::burn_lp_shares; use crate::controller::orders; use crate::controller::orders::{cancel_order, fill_perp_order, place_perp_order}; use crate::controller::position::{ @@ -181,8 +180,7 @@ pub fn liquidate_perp( let position_index = get_position_index(&user.perp_positions, market_index)?; validate!( user.perp_positions[position_index].is_open_position() - || user.perp_positions[position_index].has_open_order() - || user.perp_positions[position_index].is_lp(), + || user.perp_positions[position_index].has_open_order(), ErrorCode::PositionDoesntHaveOpenPositionOrOrders )?; @@ -227,27 +225,7 @@ pub fn liquidate_perp( drop(market); // burning lp shares = removing open bids/asks - let lp_shares = user.perp_positions[position_index].lp_shares; - if lp_shares > 0 { - let (position_delta, pnl) = burn_lp_shares( - &mut user.perp_positions[position_index], - perp_market_map.get_ref_mut(&market_index)?.deref_mut(), - lp_shares, - oracle_price, - )?; - - // emit LP record for shares removed - emit_stack::<_, { LPRecord::SIZE }>(LPRecord { - ts: now, - action: LPAction::RemoveLiquidity, - user: *user_key, - n_shares: lp_shares, - market_index, - delta_base_asset_amount: position_delta.base_asset_amount, - delta_quote_asset_amount: position_delta.quote_asset_amount, - pnl, - })?; - } + let lp_shares = 0; // check if user exited liquidation territory let intermediate_margin_calculation = if !canceled_order_ids.is_empty() || lp_shares > 0 { @@ -832,8 +810,7 @@ pub fn liquidate_perp_with_fill( let position_index = get_position_index(&user.perp_positions, market_index)?; validate!( user.perp_positions[position_index].is_open_position() - || user.perp_positions[position_index].has_open_order() - || user.perp_positions[position_index].is_lp(), + || user.perp_positions[position_index].has_open_order(), ErrorCode::PositionDoesntHaveOpenPositionOrOrders )?; @@ -878,27 +855,7 @@ pub fn liquidate_perp_with_fill( drop(market); // burning lp shares = removing open bids/asks - let lp_shares = user.perp_positions[position_index].lp_shares; - if lp_shares > 0 { - let (position_delta, pnl) = burn_lp_shares( - &mut user.perp_positions[position_index], - perp_market_map.get_ref_mut(&market_index)?.deref_mut(), - lp_shares, - oracle_price, - )?; - - // emit LP record for shares removed - emit_stack::<_, { LPRecord::SIZE }>(LPRecord { - ts: now, - action: LPAction::RemoveLiquidity, - user: *user_key, - n_shares: lp_shares, - market_index, - delta_base_asset_amount: position_delta.base_asset_amount, - delta_quote_asset_amount: position_delta.quote_asset_amount, - pnl, - })?; - } + let lp_shares = 0; // check if user exited liquidation territory let intermediate_margin_calculation = if !canceled_order_ids.is_empty() || lp_shares > 0 { @@ -2448,12 +2405,6 @@ pub fn liquidate_borrow_for_perp_pnl( base_asset_amount )?; - validate!( - !user_position.is_lp(), - ErrorCode::InvalidPerpPositionToLiquidate, - "user is an lp. must call liquidate_perp first" - )?; - let pnl = user_position.quote_asset_amount.cast::()?; validate!( @@ -2983,12 +2934,6 @@ pub fn liquidate_perp_pnl_for_deposit( base_asset_amount )?; - validate!( - !user_position.is_lp(), - ErrorCode::InvalidPerpPositionToLiquidate, - "user is an lp. must call liquidate_perp first" - )?; - let unsettled_pnl = user_position.quote_asset_amount.cast::()?; validate!( diff --git a/programs/drift/src/controller/lp/tests.rs b/programs/drift/src/controller/lp/tests.rs deleted file mode 100644 index 2ab426bc76..0000000000 --- a/programs/drift/src/controller/lp/tests.rs +++ /dev/null @@ -1,1818 +0,0 @@ -use crate::controller::lp::*; -use crate::controller::pnl::settle_pnl; -use crate::state::perp_market::AMM; -use crate::state::user::PerpPosition; -use crate::PRICE_PRECISION; -use crate::{SettlePnlMode, BASE_PRECISION_I64}; -use std::str::FromStr; - -use anchor_lang::Owner; -use solana_program::pubkey::Pubkey; - -use crate::create_account_info; -use crate::create_anchor_account_info; -use crate::math::casting::Cast; -use crate::math::constants::{ - AMM_RESERVE_PRECISION, BASE_PRECISION_I128, BASE_PRECISION_U64, LIQUIDATION_FEE_PRECISION, - PEG_PRECISION, QUOTE_PRECISION_I128, QUOTE_SPOT_MARKET_INDEX, SPOT_BALANCE_PRECISION, - SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, -}; -use crate::math::margin::{ - calculate_margin_requirement_and_total_collateral_and_liability_info, - calculate_perp_position_value_and_pnl, meets_maintenance_margin_requirement, - MarginRequirementType, -}; -use crate::math::position::{ - // get_new_position_amounts, - get_position_update_type, - PositionUpdateType, -}; -use crate::state::margin_calculation::{MarginCalculation, MarginContext}; -use crate::state::oracle::{HistoricalOracleData, OracleSource}; -use crate::state::oracle::{OraclePriceData, StrictOraclePrice}; -use crate::state::oracle_map::OracleMap; -use crate::state::perp_market::{MarketStatus, PerpMarket, PoolBalance}; -use crate::state::perp_market_map::PerpMarketMap; -use crate::state::spot_market::{SpotBalanceType, SpotMarket}; -use crate::state::spot_market_map::SpotMarketMap; -use crate::state::state::{OracleGuardRails, State, ValidityGuardRails}; -use crate::state::user::{SpotPosition, User}; -use crate::test_utils::*; -use crate::test_utils::{get_positions, get_pyth_price, get_spot_positions}; -use anchor_lang::prelude::Clock; - -#[test] -fn test_lp_wont_collect_improper_funding() { - let mut position = PerpPosition { - base_asset_amount: 1, - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 1, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_short = -10; - market.amm.cumulative_funding_rate_long = -10; - market.amm.cumulative_funding_rate_long = -10; - - let result = settle_lp_position(&mut position, &mut market); - assert_eq!(result, Err(ErrorCode::InvalidPerpPositionDetected)); -} - -#[test] -fn test_full_long_settle() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 1, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_short = -10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 10); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - // net baa doesnt change - assert_eq!( - og_market.amm.base_asset_amount_with_amm, - market.amm.base_asset_amount_with_amm - ); - - // burn - let lp_shares = position.lp_shares; - burn_lp_shares(&mut position, &mut market, lp_shares, 0).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); -} - -#[test] -fn test_full_short_settle() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - peg_multiplier: 1, - user_lp_shares: 100 * AMM_RESERVE_PRECISION, - order_step_size: 1, - ..AMM::default_test() - }; - - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - mint_lp_shares(&mut position, &mut market, 100 * BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = -10; - market.amm.quote_asset_amount_per_lp = 10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, -10); - assert_eq!(position.last_quote_asset_amount_per_lp, 10); - assert_eq!(position.base_asset_amount, -10 * 100); - assert_eq!(position.quote_asset_amount, 10 * 100); -} - -#[test] -fn test_partial_short_settle() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 3, - ..AMM::default_test() - }; - - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = -10; - market.amm.quote_asset_amount_per_lp = 10; - - market.amm.base_asset_amount_with_unsettled_lp = 9; - market.amm.base_asset_amount_long = 9; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.base_asset_amount, -9); - assert_eq!(position.quote_asset_amount, 10); - assert_eq!(position.remainder_base_asset_amount, -1); - assert_eq!(position.last_base_asset_amount_per_lp, -10); - assert_eq!(position.last_quote_asset_amount_per_lp, 10); - - // burn - let _position = position; - let lp_shares = position.lp_shares; - burn_lp_shares(&mut position, &mut market, lp_shares, 0).unwrap(); - assert_eq!(position.lp_shares, 0); -} - -#[test] -fn test_partial_long_settle() { - let mut position = PerpPosition { - lp_shares: BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: -10, - quote_asset_amount_per_lp: 10, - order_step_size: 3, - ..AMM::default_test() - }; - - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.base_asset_amount, -9); - assert_eq!(position.quote_asset_amount, 10); - assert_eq!(position.remainder_base_asset_amount, -1); - assert_eq!(position.last_base_asset_amount_per_lp, -10); - assert_eq!(position.last_quote_asset_amount_per_lp, 10); -} - -#[test] -fn test_remainder_long_settle_too_large_order_step_size() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 5 * BASE_PRECISION_U64, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm = 10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(position.remainder_base_asset_amount, 10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -10); - // net baa doesnt change after settle_lp_position - assert_eq!(market.amm.base_asset_amount_with_amm, 10); - - // burn - let lp_shares = position.lp_shares; - assert_eq!(lp_shares, BASE_PRECISION_U64); - burn_lp_shares(&mut position, &mut market, lp_shares, 22).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); - assert_eq!(position.quote_asset_amount, -11); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 0); -} - -#[test] -fn test_remainder_overflows_too_large_order_step_size() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 5 * BASE_PRECISION_U64, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm = 10; - market.amm.base_asset_amount_short = 0; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(position.remainder_base_asset_amount, 10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -10); - // net baa doesnt change after settle_lp_position - assert_eq!(market.amm.base_asset_amount_with_amm, 10); - - market.amm.base_asset_amount_per_lp += BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp += -16900000000; - market.amm.base_asset_amount_with_unsettled_lp += -(BASE_PRECISION_I128 + 1); - // market.amm.base_asset_amount_short ; - market.amm.base_asset_amount_with_amm += BASE_PRECISION_I128 + 1; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 1000000011); - assert_eq!(position.last_quote_asset_amount_per_lp, -16900000010); - assert_eq!(position.quote_asset_amount, -16900000010); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 1000000011); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // might break i32 limit - market.amm.base_asset_amount_per_lp = 3 * BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp = -(3 * 16900000000); - market.amm.base_asset_amount_with_unsettled_lp = -(3 * BASE_PRECISION_I128 + 1); - market.amm.base_asset_amount_short = -(3 * BASE_PRECISION_I128 + 1); - - // not allowed to settle when remainder is above i32 but below order size - assert!(settle_lp_position(&mut position, &mut market).is_err()); - - // assert_eq!(position.last_base_asset_amount_per_lp, 1000000001); - // assert_eq!(position.last_quote_asset_amount_per_lp, -16900000000); - assert_eq!(position.quote_asset_amount, -16900000010); - assert_eq!(position.base_asset_amount, 0); - // assert_eq!(position.remainder_base_asset_amount, 1000000001); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // past order_step_size on market - market.amm.base_asset_amount_per_lp = 5 * BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp = -116900000000; - market.amm.base_asset_amount_with_unsettled_lp = -(5 * BASE_PRECISION_I128 + 1); - market.amm.base_asset_amount_short = -(5 * BASE_PRECISION_I128); - market.amm.base_asset_amount_with_amm = 1; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -1); - assert_eq!(market.amm.base_asset_amount_short, -5000000000); - assert_eq!(market.amm.base_asset_amount_long, 5 * BASE_PRECISION_I128); - assert_eq!(market.amm.base_asset_amount_with_amm, 1); - - assert_eq!(position.last_base_asset_amount_per_lp, 5000000001); - assert_eq!(position.last_quote_asset_amount_per_lp, -116900000000); - assert_eq!(position.quote_asset_amount, -116900000000); - assert_eq!(position.base_asset_amount, 5000000000); - assert_eq!(position.remainder_base_asset_amount, 1); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // burn - let lp_shares = position.lp_shares; - assert_eq!(lp_shares, BASE_PRECISION_U64); - burn_lp_shares(&mut position, &mut market, lp_shares, 22).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); - assert_eq!(position.quote_asset_amount, -116900000001); - assert_eq!(position.base_asset_amount, 5000000000); - assert_eq!(position.remainder_base_asset_amount, 0); - - assert_eq!(market.amm.base_asset_amount_with_amm, 0); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - assert_eq!(market.amm.base_asset_amount_short, -5000000000); - assert_eq!(market.amm.base_asset_amount_long, 5000000000); -} - -#[test] -fn test_remainder_burn_large_order_step_size() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: 2 * BASE_PRECISION_U64, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - let og_market = market; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm += 10; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 10); - assert_eq!(position.last_quote_asset_amount_per_lp, -10); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -10); - assert_eq!(position.remainder_base_asset_amount, 10); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, -10); - // net baa doesnt change after settle_lp_position - assert_eq!(market.amm.base_asset_amount_with_amm, 10); - - market.amm.base_asset_amount_per_lp = BASE_PRECISION_I128 + 1; - market.amm.quote_asset_amount_per_lp = -16900000000; - market.amm.base_asset_amount_with_unsettled_lp += -(BASE_PRECISION_I128 + 1); - market.amm.base_asset_amount_with_amm += BASE_PRECISION_I128 + 1; - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.last_base_asset_amount_per_lp, 1000000001); - assert_eq!(position.last_quote_asset_amount_per_lp, -16900000000); - assert_eq!(position.quote_asset_amount, -16900000000); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 1000000001); - assert_eq!( - (position.remainder_base_asset_amount as u64) < market.amm.order_step_size, - true - ); - - // burn with overflowed remainder - let lp_shares = position.lp_shares; - assert_eq!(lp_shares, BASE_PRECISION_U64); - burn_lp_shares(&mut position, &mut market, lp_shares, 22).unwrap(); - assert_eq!(position.lp_shares, 0); - assert_eq!(og_market.amm.sqrt_k, market.amm.sqrt_k); - assert_eq!(position.quote_asset_amount, -16900000023); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, 0); -} - -#[test] -pub fn test_lp_settle_pnl() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - position.last_cumulative_funding_rate = 1337; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let clock = Clock { - slot: 0, - epoch_start_timestamp: 0, - epoch: 0, - leader_schedule_epoch: 0, - unix_timestamp: 0, - }; - let mut oracle_map = OracleMap::load_one(&oracle_account_info, clock.slot, None).unwrap(); - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 2 * BASE_PRECISION_U64 / 100, - quote_asset_amount: -150 * QUOTE_PRECISION_I128, - base_asset_amount_with_amm: BASE_PRECISION_I128, - base_asset_amount_long: BASE_PRECISION_I128, - oracle: oracle_price_key, - concentration_coef: 1000001, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: oracle_price.agg.price, - last_oracle_price_twap_5min: oracle_price.agg.price, - last_oracle_price_twap: oracle_price.agg.price, - ..HistoricalOracleData::default() - }, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - number_of_users_with_base: 1, - number_of_users: 1, - status: MarketStatus::Active, - liquidator_fee: LIQUIDATION_FEE_PRECISION / 100, - pnl_pool: PoolBalance { - scaled_balance: (50 * SPOT_BALANCE_PRECISION), - market_index: QUOTE_SPOT_MARKET_INDEX, - ..PoolBalance::default() - }, - unrealized_pnl_maintenance_asset_weight: SPOT_WEIGHT_PRECISION.cast().unwrap(), - ..PerpMarket::default() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 10; - market.amm.quote_asset_amount_per_lp = -10; - market.amm.base_asset_amount_with_unsettled_lp = -10; - market.amm.base_asset_amount_with_amm += 10; - market.amm.cumulative_funding_rate_long = 169; - market.amm.cumulative_funding_rate_short = 169; - - settle_lp_position(&mut position, &mut market).unwrap(); - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - deposit_balance: 100 * SPOT_BALANCE_PRECISION, - ..SpotMarket::default() - }; - - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - let user_key = Pubkey::default(); - let authority = Pubkey::default(); - - let mut user = User { - perp_positions: get_positions(position), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 50 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let state = State { - oracle_guard_rails: OracleGuardRails { - validity: ValidityGuardRails { - slots_before_stale_for_amm: 10, // 5s - slots_before_stale_for_margin: 120, // 60s - confidence_interval_max_size: 1000, - too_volatile_ratio: 5, - }, - ..OracleGuardRails::default() - }, - ..State::default() - }; - - let MarginCalculation { - total_collateral: total_collateral1, - margin_requirement: margin_requirement1, - .. - } = calculate_margin_requirement_and_total_collateral_and_liability_info( - &user, - &market_map, - &spot_market_map, - &mut oracle_map, - MarginContext::standard(MarginRequirementType::Initial), - ) - .unwrap(); - - assert_eq!(total_collateral1, 49999988); - assert_eq!(margin_requirement1, 2099020); // $2+ for margin req - - let result = settle_pnl( - 0, - &mut user, - &authority, - &user_key, - &market_map, - &spot_market_map, - &mut oracle_map, - &clock, - &state, - None, - SettlePnlMode::MustSettle, - ); - - assert_eq!(result, Ok(())); - // assert_eq!(result, Err(ErrorCode::InsufficientCollateralForSettlingPNL)) -} - -#[test] -fn test_lp_margin_calc() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - position.last_cumulative_funding_rate = 1337; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let slot = 0; - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 2 * BASE_PRECISION_U64 / 100, - quote_asset_amount: -150 * QUOTE_PRECISION_I128, - base_asset_amount_with_amm: BASE_PRECISION_I128, - base_asset_amount_long: BASE_PRECISION_I128, - oracle: oracle_price_key, - concentration_coef: 1000001, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: oracle_price.agg.price, - last_oracle_price_twap_5min: oracle_price.agg.price, - last_oracle_price_twap: oracle_price.agg.price, - ..HistoricalOracleData::default() - }, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - number_of_users_with_base: 1, - number_of_users: 1, - status: MarketStatus::Active, - liquidator_fee: LIQUIDATION_FEE_PRECISION / 100, - pnl_pool: PoolBalance { - scaled_balance: (50 * SPOT_BALANCE_PRECISION), - market_index: QUOTE_SPOT_MARKET_INDEX, - ..PoolBalance::default() - }, - unrealized_pnl_maintenance_asset_weight: SPOT_WEIGHT_PRECISION.cast().unwrap(), - ..PerpMarket::default() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 100 * BASE_PRECISION_I128; - market.amm.quote_asset_amount_per_lp = -BASE_PRECISION_I128; - market.amm.base_asset_amount_with_unsettled_lp = -100 * BASE_PRECISION_I128; - market.amm.base_asset_amount_short = -100 * BASE_PRECISION_I128; - market.amm.cumulative_funding_rate_long = 169 * 100000000; - market.amm.cumulative_funding_rate_short = 169 * 100000000; - - settle_lp_position(&mut position, &mut market).unwrap(); - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - deposit_balance: 100 * SPOT_BALANCE_PRECISION, - ..SpotMarket::default() - }; - - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - let mut user = User { - perp_positions: get_positions(position), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 5000 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - user.perp_positions[0].base_asset_amount = BASE_PRECISION_I128 as i64; - - // user has lp shares + long and last cumulative funding doesnt match - assert_eq!(user.perp_positions[0].lp_shares, 1000000000); - assert_eq!( - user.perp_positions[0].base_asset_amount, - BASE_PRECISION_I128 as i64 - ); - assert!( - user.perp_positions[0].last_cumulative_funding_rate != market.amm.last_funding_rate_long - ); - - let result = - meets_maintenance_margin_requirement(&user, &market_map, &spot_market_map, &mut oracle_map); - - assert_eq!(result.unwrap(), true); - - // add move lower - let oracle_price_data = OraclePriceData { - price: oracle_price.agg.price, - confidence: 100000, - delay: 1, - has_sufficient_number_of_data_points: true, - }; - - assert_eq!(market.amm.base_asset_amount_per_lp, 100000000000); - assert_eq!(market.amm.quote_asset_amount_per_lp, -1000000000); - assert_eq!(market.amm.cumulative_funding_rate_long, 16900000000); - assert_eq!(market.amm.cumulative_funding_rate_short, 16900000000); - - assert_eq!(user.perp_positions[0].lp_shares, 1000000000); - assert_eq!(user.perp_positions[0].base_asset_amount, 1000000000); - assert_eq!( - user.perp_positions[0].last_base_asset_amount_per_lp, - 100000000000 - ); - assert_eq!( - user.perp_positions[0].last_quote_asset_amount_per_lp, - -1000000000 - ); - assert_eq!( - user.perp_positions[0].last_cumulative_funding_rate, - 16900000000 - ); - - // increase markets so user has to settle lp - market.amm.base_asset_amount_per_lp *= 2; - market.amm.quote_asset_amount_per_lp *= 20; - - // update funding so user has unsettled funding - market.amm.cumulative_funding_rate_long *= 2; - market.amm.cumulative_funding_rate_short *= 2; - - apply_lp_rebase_to_perp_market(&mut market, 1).unwrap(); - - let sim_user_pos = user.perp_positions[0] - .simulate_settled_lp_position(&market, oracle_price_data.price) - .unwrap(); - assert_ne!( - sim_user_pos.base_asset_amount, - user.perp_positions[0].base_asset_amount - ); - assert_eq!(sim_user_pos.base_asset_amount, 101000000000); - assert_eq!(sim_user_pos.quote_asset_amount, -20000000000); - assert_eq!(sim_user_pos.last_cumulative_funding_rate, 16900000000); - - let strict_quote_price = StrictOraclePrice::test(1000000); - // ensure margin calc doesnt incorrectly count funding rate (funding pnl MUST come before settling lp) - let ( - margin_requirement, - weighted_unrealized_pnl, - worse_case_base_asset_value, - _open_order_fraction, - _base_asset_value, - ) = calculate_perp_position_value_and_pnl( - &user.perp_positions[0], - &market, - &oracle_price_data, - &strict_quote_price, - crate::math::margin::MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - assert_eq!(margin_requirement, 1012000000); // $1010 + $2 mr for lp_shares - assert_eq!(weighted_unrealized_pnl, -9916900000); // $-9900000000 upnl (+ -16900000 from old funding) - assert_eq!(worse_case_base_asset_value, 10100000000); //$10100 -} - -#[test] -fn test_lp_has_correct_entry_be_price() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 100000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 101000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - - market.amm.base_asset_amount_per_lp = BASE_PRECISION_I128; - market.amm.quote_asset_amount_per_lp = -99_999_821; - market.amm.base_asset_amount_with_unsettled_lp = BASE_PRECISION_I128; - market.amm.base_asset_amount_long = BASE_PRECISION_I128; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(position.get_entry_price().unwrap(), 99999821); - - assert_eq!(position.quote_entry_amount, -99999821); - assert_eq!(position.quote_break_even_amount, -99999821); - assert_eq!(position.quote_asset_amount, -99999821); - - market.amm.base_asset_amount_per_lp -= BASE_PRECISION_I128 / 2; - market.amm.quote_asset_amount_per_lp += 97_999_821; - market.amm.base_asset_amount_with_unsettled_lp = BASE_PRECISION_I128 / 2; - market.amm.base_asset_amount_long = BASE_PRECISION_I128 / 2; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(position.get_entry_price().unwrap(), 99999822); - - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.quote_entry_amount, -49999911); - assert_eq!(position.quote_break_even_amount, -49999911); - assert_eq!(position.quote_asset_amount, -2000000); - assert_eq!(position.base_asset_amount, 500_000_000); - - let base_delta = -BASE_PRECISION_I128 / 4; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp += 98_999_821 / 4; - let (update_base_delta, _) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - base_delta, - market.amm.order_step_size as u128, - ) - .unwrap(); - - market.amm.base_asset_amount_with_unsettled_lp += update_base_delta; - market.amm.base_asset_amount_long += update_base_delta; - - settle_lp_position(&mut position, &mut market).unwrap(); - assert_eq!(position.get_entry_price().unwrap(), 99999824); - assert_eq!(position.get_cost_basis().unwrap(), -75833183); - - assert_eq!(position.base_asset_amount, 300000000); - assert_eq!(position.remainder_base_asset_amount, -50000000); - assert_eq!(position.quote_entry_amount, -24999956); - assert_eq!(position.quote_break_even_amount, -24999956); - assert_eq!(position.quote_asset_amount, 22749955); -} - -#[test] -fn test_lp_has_correct_entry_be_price_sim_no_remainders() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 2000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 0); - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.base_asset_amount, 0); - let mut num_position_flips = 0; - let mut flip_indexes: Vec = Vec::new(); - - for i in 0..3000 { - if i % 3 == 0 { - let px = 100_000_000 - i; - let multi = i % 19 + 1; - let divisor = 10; - let base_delta = -BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp += px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += base_delta; - market.amm.base_asset_amount_short += base_delta; - } else { - // buy - let px = 99_199_821 + i; - let multi = i % 5 + 1; - let divisor = 5; - let base_delta = BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp -= px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += base_delta; - market.amm.base_asset_amount_long += base_delta; - } - - let position_base_before = position.base_asset_amount; - - settle_lp_position(&mut position, &mut market).unwrap(); - - if position_base_before.signum() != position.base_asset_amount.signum() { - num_position_flips += 1; - flip_indexes.push(i); - } - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - let iii = position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount as i64) - .unwrap(); - msg!( - "{}: entry: {}, be: {} cb:{} ({}/{})", - i, - entry, - be, - cb, - iii, - position.base_asset_amount, - ); - assert_eq!(position.remainder_base_asset_amount, 0); - - if position.get_base_asset_amount_with_remainder_abs().unwrap() != 0 { - assert!(entry <= 100 * PRICE_PRECISION as i128); - assert!(entry >= 99 * PRICE_PRECISION as i128); - } - } - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - assert_eq!(position.base_asset_amount, 200500000000); - assert_eq!(entry, 99202392); - assert_eq!(be, 99202392); - assert_eq!(cb, 95227357); - assert_eq!(num_position_flips, 4); - assert_eq!(flip_indexes, [0, 1, 18, 19]); -} - -#[test] -fn test_lp_remainder_position_updates() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(880), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-881), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.remainder_base_asset_amount, -1); - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: -199 * 1000000, - base_asset_amount: BASE_PRECISION_I64, - remainder_base_asset_amount: Some(-BASE_PRECISION_I64 / 22), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - assert_eq!(position.base_asset_amount, 1000000000); - assert_eq!(position.remainder_base_asset_amount, -45454546); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: 199 * 1000000, - base_asset_amount: -BASE_PRECISION_I64 * 2, - remainder_base_asset_amount: Some(BASE_PRECISION_I64 / 23), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, -101912122); - assert_eq!(position.base_asset_amount, -1000000000); - assert_eq!(position.remainder_base_asset_amount, -1976286); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); -} - -#[test] -fn test_lp_remainder_position_updates_2() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 300000000, - remainder_base_asset_amount: Some(33333333), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 500000000, - remainder_base_asset_amount: Some(0), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 0); - assert_eq!(position.base_asset_amount, 800000000); - assert_eq!(position.remainder_base_asset_amount, 33333333); - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - let position_delta = PositionDelta { - quote_asset_amount: 199 * 10000, - base_asset_amount: -300000000, - remainder_base_asset_amount: Some(-63636363), - }; - - let pnl: i64 = update_position_and_market(&mut position, &mut market, &position_delta).unwrap(); - assert_eq!(pnl, 1990000); - assert_eq!(position.base_asset_amount, 500000000); - assert_eq!(position.remainder_base_asset_amount, -30303030); - assert_eq!(market.amm.base_asset_amount_long, 500000000); - assert_eq!(market.amm.base_asset_amount_short, 0); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 500000000); - assert_eq!(market.amm.base_asset_amount_with_amm, 0); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); -} - -#[test] -fn test_lp_has_correct_entry_be_price_sim() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 2000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 0); - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.base_asset_amount, 0); - let mut num_position_flips = 0; - let mut flip_indexes: Vec = Vec::new(); - - let mut total_remainder = 0; - for i in 0..3000 { - if i % 3 == 0 { - let px = 100_000_000 - i; - let multi = i % 19 + 1; - let divisor = 11; - let base_delta = -BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp += px * multi / divisor; - - let (update_base_delta, rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - base_delta, - market.amm.order_step_size as u128, - ) - .unwrap(); - total_remainder += rr; - - let (total_remainder_f, _rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - total_remainder, - market.amm.order_step_size as u128, - ) - .unwrap(); - if total_remainder_f != 0 { - total_remainder -= total_remainder_f; - msg!("total_remainder update {}", total_remainder); - } - - market.amm.base_asset_amount_with_unsettled_lp += update_base_delta; - market.amm.base_asset_amount_long += update_base_delta; - } else { - // buy - let px = 99_199_821 + i; - let multi = i % 5 + 1; - let divisor = 6; - let base_delta = BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp -= px * multi / divisor; - - let (update_base_delta, rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - base_delta, - market.amm.order_step_size as u128, - ) - .unwrap(); - total_remainder += rr; - - let (total_remainder_f, _rr) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - total_remainder, - market.amm.order_step_size as u128, - ) - .unwrap(); - if total_remainder_f != 0 { - total_remainder -= total_remainder_f; - } - - market.amm.base_asset_amount_with_unsettled_lp += update_base_delta; - market.amm.base_asset_amount_short += update_base_delta; - } - - let position_base_before = position.base_asset_amount; - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::position::validate_perp_position_with_perp_market(&position, &market) - .unwrap(); - - settle_lp_position(&mut position, &mut market).unwrap(); - - if position_base_before.signum() != position.base_asset_amount.signum() { - num_position_flips += 1; - flip_indexes.push(i); - } - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - let iii = position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount as i64) - .unwrap(); - msg!( - "{}: entry: {}, be: {} cb:{} ({}/{})", - i, - entry, - be, - cb, - iii, - position.base_asset_amount, - ); - // assert_ne!(position.remainder_base_asset_amount, 0); - - if position.get_base_asset_amount_with_remainder_abs().unwrap() != 0 { - assert!(entry <= 100 * PRICE_PRECISION as i128); - assert!(entry >= 99 * PRICE_PRECISION as i128); - } - } - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - assert_eq!(entry, 99202570); - assert_eq!(be, 99202570); - assert_eq!(cb, 91336780); - assert_eq!(num_position_flips, 5); - assert_eq!(flip_indexes, [1, 18, 19, 36, 37]); - assert_eq!(position.base_asset_amount, 91300000000); -} - -#[test] -fn test_lp_has_correct_entry_be_price_sim_more_flips() { - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - let amm = AMM { - order_step_size: BASE_PRECISION_U64 / 10, - sqrt_k: BASE_PRECISION_U64 as u128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - assert_eq!(market.amm.user_lp_shares, 0); - assert_eq!(market.amm.sqrt_k, 1000000000); - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - assert_eq!(market.amm.user_lp_shares, 1000000000); - assert_eq!(market.amm.sqrt_k, 2000000000); - assert_eq!(position.get_entry_price().unwrap(), 0); - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 0); - assert_eq!(position.remainder_base_asset_amount, 0); - assert_eq!(position.base_asset_amount, 0); - let mut num_position_flips = 0; - let mut flip_indexes: Vec = Vec::new(); - - for i in 0..3000 { - if i % 2 == 0 { - let px = 99_800_000 - i * i % 4; - let multi = i % 7 + 1 + i; - let divisor = 10; - let amt2 = -BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += amt2; - market.amm.quote_asset_amount_per_lp += px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += amt2; - market.amm.base_asset_amount_short += amt2; - } else { - // buy - let px = 99_199_821 + i * i % 4; - let multi = i % 7 + 1 + i; - let divisor = 10; - let base_delta = BASE_PRECISION_I128 * multi / divisor; - market.amm.base_asset_amount_per_lp += base_delta; - market.amm.quote_asset_amount_per_lp -= px * multi / divisor; - market.amm.base_asset_amount_with_unsettled_lp += base_delta; - market.amm.base_asset_amount_long += base_delta; - } - - let position_base_before = position.base_asset_amount; - - settle_lp_position(&mut position, &mut market).unwrap(); - - if position_base_before.signum() != position.base_asset_amount.signum() { - num_position_flips += 1; - flip_indexes.push(i); - } - assert_eq!(position.remainder_base_asset_amount, 0); - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - let iii = position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount as i64) - .unwrap(); - msg!( - "{}: entry: {}, be: {} cb:{} ({}/{})", - i, - entry, - be, - cb, - iii, - position.base_asset_amount, - ); - - if position.get_base_asset_amount_with_remainder_abs().unwrap() != 0 { - assert!(entry <= 99_800_000_i128); - assert!(entry >= 99_199_820_i128); - } - } - - assert_eq!(num_position_flips, 3000); - // assert_eq!(flip_indexes, [0, 1, 18, 19]); - - let entry = position.get_entry_price().unwrap(); - let be = position.get_breakeven_price().unwrap(); - let cb = position.get_cost_basis().unwrap(); - - assert_eq!(position.base_asset_amount, 150200000000); - assert_eq!(position.remainder_base_asset_amount, 0); - - assert_eq!(entry, 99199822); - assert_eq!(be, 99199822); - assert_eq!(cb, -801664962); -} - -#[test] -fn test_get_position_update_type_lp_opens() { - // position is empty, every inc must be open - let position = PerpPosition { - ..PerpPosition::default() - }; - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let position = PerpPosition { - ..PerpPosition::default() - }; - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Open - ); -} - -#[test] -fn test_get_position_update_type_lp_negative_position() { - // $119 short - let position = PerpPosition { - base_asset_amount: -1000000000 * 2, - quote_asset_amount: 119000000 * 2, - quote_entry_amount: 119000000 * 2, - quote_break_even_amount: 119000000 * 2, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 119000000); - assert_eq!(position.get_breakeven_price().unwrap(), 119000000); - assert_eq!(position.get_entry_price().unwrap(), 119000000); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); // more negative - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81, - remainder_base_asset_amount: Some(-81000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81000, - remainder_base_asset_amount: Some(-81), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); -} - -#[test] -fn test_get_position_update_type_lp_positive_position() { - // $119 long - let position = PerpPosition { - base_asset_amount: 1000000000 * 2, - quote_asset_amount: -119000000 * 2, - quote_entry_amount: -119000000 * 2, - quote_break_even_amount: -119000000 * 2, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 119000000); - assert_eq!(position.get_breakeven_price().unwrap(), 119000000); - assert_eq!(position.get_entry_price().unwrap(), 119000000); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // no base/remainder is reduce (should be skipped earlier) - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81, - remainder_base_asset_amount: Some(-81000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81000, - remainder_base_asset_amount: Some(-81), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); -} - -#[test] -fn test_get_position_update_type_lp_positive_position_with_positive_remainder() { - // $119 long - let position = PerpPosition { - base_asset_amount: 1000000000 * 2, - remainder_base_asset_amount: 7809809, - quote_asset_amount: -119000000 * 2, - quote_entry_amount: -119000000 * 2, - quote_break_even_amount: -119000000 * 2, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 119000000); - assert_eq!(position.get_breakeven_price().unwrap(), 118537123); - assert_eq!(position.get_entry_price().unwrap(), 118537123); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000001 * 2, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-7809809), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: Some(-7809809 - 1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: -898989, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 10000, - base_asset_amount: 0, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // no base/remainder is reduce (should be skipped earlier) - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - let delta = PositionDelta { - quote_asset_amount: 1899990, - base_asset_amount: -8989898, - remainder_base_asset_amount: Some(-88881000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81, - remainder_base_asset_amount: Some(-81000), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); - - // opposite sign remainder/base - let delta = PositionDelta { - quote_asset_amount: -88888, - base_asset_amount: 81000, - remainder_base_asset_amount: Some(-81), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); -} - -#[test] -fn test_get_position_update_type_positive_remainder() { - // $119 long (only a remainder size) - let position = PerpPosition { - base_asset_amount: 0, - remainder_base_asset_amount: 7809809, - quote_asset_amount: -119000000 * 7809809 / BASE_PRECISION_I64, - quote_entry_amount: -119000000 * 7809809 / BASE_PRECISION_I64, - quote_break_even_amount: -119000000 * 7809809 / BASE_PRECISION_I64, - ..PerpPosition::default() - }; - - assert_eq!(position.get_cost_basis().unwrap(), 0); - assert_eq!(position.get_breakeven_price().unwrap(), 118999965); - assert_eq!(position.get_entry_price().unwrap(), 118999965); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000001 * 2, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Increase - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-8791), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Reduce - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-7809809), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Close - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: 0, - remainder_base_asset_amount: Some(-7809809 - 1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: Some(-7809809 - 1), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: Some(0), - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller - - let delta = PositionDelta { - quote_asset_amount: 0, - base_asset_amount: -1000000000 * 2, - remainder_base_asset_amount: None, - }; - assert_eq!( - get_position_update_type(&position, &delta).unwrap(), - PositionUpdateType::Flip - ); // different signum but smaller -} diff --git a/programs/drift/src/controller/mod.rs b/programs/drift/src/controller/mod.rs index ecd9a3a6ab..1565eb1174 100644 --- a/programs/drift/src/controller/mod.rs +++ b/programs/drift/src/controller/mod.rs @@ -2,7 +2,6 @@ pub mod amm; pub mod funding; pub mod insurance; pub mod liquidation; -pub mod lp; pub mod orders; pub mod pda; pub mod pnl; diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index eb8e7d122f..8ac70e1c0d 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -8,12 +8,10 @@ use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use anchor_lang::prelude::*; use crate::controller::funding::settle_funding_payment; -use crate::controller::lp::burn_lp_shares; use crate::controller::position; use crate::controller::position::{ add_new_position, decrease_open_bids_and_asks, get_position_index, increase_open_bids_and_asks, - update_lp_market_position, update_position_and_market, update_quote_asset_amount, - PositionDirection, + update_position_and_market, update_quote_asset_amount, PositionDirection, }; use crate::controller::spot_balance::{ update_spot_balances, update_spot_market_cumulative_interest, @@ -37,7 +35,6 @@ use crate::math::fulfillment::{ determine_perp_fulfillment_methods, determine_spot_fulfillment_methods, }; use crate::math::liquidation::validate_user_not_being_liquidated; -use crate::math::lp::calculate_lp_shares_to_burn_for_risk_reduction; use crate::math::matching::{ are_orders_same_market_but_different_sides, calculate_fill_for_matched_orders, calculate_filler_multiplier_for_matched_orders, do_orders_cross, is_maker_for_taker, @@ -64,7 +61,7 @@ use crate::state::order_params::{ ModifyOrderParams, OrderParams, PlaceOrderOptions, PostOnlyParam, }; use crate::state::paused_operations::{PerpOperation, SpotOperation}; -use crate::state::perp_market::{AMMAvailability, AMMLiquiditySplit, MarketStatus, PerpMarket}; +use crate::state::perp_market::{AMMAvailability, MarketStatus, PerpMarket}; use crate::state::perp_market_map::PerpMarketMap; use crate::state::protected_maker_mode_config::ProtectedMakerParams; use crate::state::spot_fulfillment_params::{ExternalSpotFill, SpotFulfillmentParams}; @@ -1015,7 +1012,7 @@ pub fn fill_perp_order( // settle lp position so its tradeable let mut market = perp_market_map.get_ref_mut(&market_index)?; - controller::lp::settle_funding_payment_then_lp(user, &user_key, &mut market, now)?; + settle_funding_payment(user, &user_key, &mut market, now)?; validate!( matches!( @@ -1074,7 +1071,7 @@ pub fn fill_perp_order( let perp_market_index: u16; let user_can_skip_duration: bool; let amm_can_skip_duration: bool; - let amm_lp_allowed_to_jit_make: bool; + let amm_has_low_enough_inventory: bool; let oracle_valid_for_amm_fill: bool; let oracle_stale_for_margin: bool; let min_auction_duration: u8; @@ -1132,11 +1129,11 @@ pub fn fill_perp_order( amm_is_available &= amm_available_mm_oracle_recent_but_volatile; let amm_wants_to_jit_make = market.amm.amm_wants_to_jit_make(order_direction)?; - amm_lp_allowed_to_jit_make = market + amm_has_low_enough_inventory = market .amm - .amm_lp_allowed_to_jit_make(amm_wants_to_jit_make)?; + .amm_has_low_enough_inventory(amm_wants_to_jit_make)?; amm_can_skip_duration = - market.can_skip_auction_duration(&state, amm_lp_allowed_to_jit_make)?; + market.can_skip_auction_duration(&state, amm_has_low_enough_inventory)?; user_can_skip_duration = user.can_skip_auction_duration(user_stats)?; @@ -1851,7 +1848,6 @@ fn fulfill_perp_order( limit_price, None, *maker_price, - AMMLiquiditySplit::Shared, fill_mode.is_liquidation(), )?; @@ -1897,7 +1893,6 @@ fn fulfill_perp_order( fee_structure, oracle_map, fill_mode.is_liquidation(), - None, )?; if maker_fill_base_asset_amount != 0 { @@ -2140,7 +2135,6 @@ pub fn fulfill_perp_order_with_amm( limit_price: Option, override_base_asset_amount: Option, override_fill_price: Option, - liquidity_split: AMMLiquiditySplit, is_liquidation: bool, ) -> DriftResult<(u64, u64)> { let position_index = get_position_index(&user.perp_positions, market.market_index)?; @@ -2271,29 +2265,6 @@ pub fn fulfill_perp_order_with_amm( let user_position_delta = get_position_delta_for_fill(base_asset_amount, quote_asset_amount, order_direction)?; - if liquidity_split != AMMLiquiditySplit::ProtocolOwned { - update_lp_market_position( - market, - &user_position_delta, - fee_to_market_for_lp.cast()?, - liquidity_split, - )?; - } - - if market.amm.user_lp_shares > 0 { - let (new_terminal_quote_reserve, new_terminal_base_reserve) = - crate::math::amm::calculate_terminal_reserves(&market.amm)?; - market.amm.terminal_quote_asset_reserve = new_terminal_quote_reserve; - - let (min_base_asset_reserve, max_base_asset_reserve) = - crate::math::amm::calculate_bid_ask_bounds( - market.amm.concentration_coef, - new_terminal_base_reserve, - )?; - market.amm.min_base_asset_reserve = min_base_asset_reserve; - market.amm.max_base_asset_reserve = max_base_asset_reserve; - } - // Increment the protocol's total fee variables market.amm.total_fee = market.amm.total_fee.safe_add(fee_to_market.cast()?)?; market.amm.total_exchange_fee = market.amm.total_exchange_fee.safe_add(user_fee.cast()?)?; @@ -2389,7 +2360,7 @@ pub fn fulfill_perp_order_with_amm( let fill_record_id = get_then_update_id!(market, next_fill_record_id); let order_action_explanation = match (override_base_asset_amount, override_fill_price) { _ if is_liquidation => OrderActionExplanation::Liquidation, - (Some(_), Some(_)) => liquidity_split.get_order_action_explanation(), + (Some(_), Some(_)) => OrderActionExplanation::OrderFilledWithAMMJit, _ => OrderActionExplanation::OrderFilledWithAMM, }; let mut order_action_bit_flags: u8 = 0; @@ -2523,7 +2494,6 @@ pub fn fulfill_perp_order_with_match( fee_structure: &FeeStructure, oracle_map: &mut OracleMap, is_liquidation: bool, - amm_lp_allowed_to_jit_make: Option, ) -> DriftResult<(u64, u64, u64)> { if !are_orders_same_market_but_different_sides( &maker.orders[maker_order_index], @@ -2601,7 +2571,7 @@ pub fn fulfill_perp_order_with_match( let mut total_quote_asset_amount = 0_u64; let mut total_base_asset_amount = 0_u64; - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( + let jit_base_asset_amount = calculate_amm_jit_liquidity( market, taker_direction, maker_price, @@ -2610,7 +2580,6 @@ pub fn fulfill_perp_order_with_match( taker_base_asset_amount, maker_base_asset_amount, taker.orders[taker_order_index].has_limit_price(slot)?, - amm_lp_allowed_to_jit_make, )?; if jit_base_asset_amount > 0 { @@ -2636,7 +2605,6 @@ pub fn fulfill_perp_order_with_match( taker_limit_price, Some(jit_base_asset_amount), Some(maker_price), // match the makers price - amm_liquidity_split, is_liquidation, )?; @@ -3346,129 +3314,6 @@ pub fn can_reward_user_with_perp_pnl(user: &mut Option<&mut User>, market_index: } } -pub fn attempt_burn_user_lp_shares_for_risk_reduction( - state: &State, - user: &mut User, - user_key: Pubkey, - margin_calc: MarginCalculation, - perp_market_map: &PerpMarketMap, - spot_market_map: &SpotMarketMap, - oracle_map: &mut OracleMap, - clock: &Clock, - market_index: u16, -) -> DriftResult { - let now = clock.unix_timestamp; - let time_since_last_liquidity_change: i64 = now.safe_sub(user.last_add_perp_lp_shares_ts)?; - // avoid spamming update if orders have already been set - if time_since_last_liquidity_change >= state.lp_cooldown_time.cast()? { - burn_user_lp_shares_for_risk_reduction( - state, - user, - user_key, - market_index, - margin_calc, - perp_market_map, - spot_market_map, - oracle_map, - clock, - )?; - user.last_add_perp_lp_shares_ts = now; - } - - Ok(()) -} - -pub fn burn_user_lp_shares_for_risk_reduction( - state: &State, - user: &mut User, - user_key: Pubkey, - market_index: u16, - margin_calc: MarginCalculation, - perp_market_map: &PerpMarketMap, - spot_market_map: &SpotMarketMap, - oracle_map: &mut OracleMap, - clock: &Clock, -) -> DriftResult { - let position_index = get_position_index(&user.perp_positions, market_index)?; - let is_lp = user.perp_positions[position_index].is_lp(); - if !is_lp { - return Ok(()); - } - - let mut market = perp_market_map.get_ref_mut(&market_index)?; - - let quote_oracle_id = spot_market_map - .get_ref(&market.quote_spot_market_index)? - .oracle_id(); - let quote_oracle_price = oracle_map.get_price_data("e_oracle_id)?.price; - - let oracle_price_data = oracle_map.get_price_data(&market.oracle_id())?; - - let oracle_price = if market.status == MarketStatus::Settlement { - market.expiry_price - } else { - oracle_price_data.price - }; - - let user_custom_margin_ratio = user.max_margin_ratio; - let (lp_shares_to_burn, base_asset_amount_to_close) = - calculate_lp_shares_to_burn_for_risk_reduction( - &user.perp_positions[position_index], - &market, - oracle_price, - quote_oracle_price, - margin_calc.margin_shortage()?, - user_custom_margin_ratio, - user.is_high_leverage_mode(MarginRequirementType::Initial), - )?; - - let (position_delta, pnl) = burn_lp_shares( - &mut user.perp_positions[position_index], - &mut market, - lp_shares_to_burn, - oracle_price, - )?; - - // emit LP record for shares removed - emit_stack::<_, { LPRecord::SIZE }>(LPRecord { - ts: clock.unix_timestamp, - action: LPAction::RemoveLiquidityDerisk, - user: user_key, - n_shares: lp_shares_to_burn, - market_index, - delta_base_asset_amount: position_delta.base_asset_amount, - delta_quote_asset_amount: position_delta.quote_asset_amount, - pnl, - })?; - - let direction_to_close = user.perp_positions[position_index].get_direction_to_close(); - - let params = OrderParams::get_close_perp_params( - &market, - direction_to_close, - base_asset_amount_to_close, - )?; - - drop(market); - - if user.has_room_for_new_order() { - controller::orders::place_perp_order( - state, - user, - user_key, - perp_market_map, - spot_market_map, - oracle_map, - &None, - clock, - params, - PlaceOrderOptions::default().explanation(OrderActionExplanation::DeriskLp), - )?; - } - - Ok(()) -} - pub fn pay_keeper_flat_reward_for_perps( user: &mut User, filler: Option<&mut User>, diff --git a/programs/drift/src/controller/orders/amm_jit_tests.rs b/programs/drift/src/controller/orders/amm_jit_tests.rs index 8f79f5dcd5..6d8ff77eef 100644 --- a/programs/drift/src/controller/orders/amm_jit_tests.rs +++ b/programs/drift/src/controller/orders/amm_jit_tests.rs @@ -43,7 +43,7 @@ pub mod amm_jit { use crate::math::constants::{CONCENTRATION_PRECISION, PRICE_PRECISION_U64}; use crate::state::fill_mode::FillMode; use crate::state::oracle::{HistoricalOracleData, OracleSource}; - use crate::state::perp_market::{AMMLiquiditySplit, MarketStatus, PerpMarket, AMM}; + use crate::state::perp_market::{MarketStatus, PerpMarket, AMM}; use crate::state::perp_market_map::PerpMarketMap; use crate::state::spot_market::{SpotBalanceType, SpotMarket}; use crate::state::spot_market_map::SpotMarketMap; @@ -113,7 +113,6 @@ pub mod amm_jit { PRICE_PRECISION_U64, Some(PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::ProtocolOwned, ) .unwrap(); } @@ -1309,235 +1308,6 @@ pub mod amm_jit { assert!(quote_asset_amount_surplus > 0); } - #[test] - fn fulfill_with_amm_jit_taker_short_with_split_lps() { - let now = 0_i64; - let slot = 0_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_spread: 250, - long_spread: 125, - short_spread: 125, - max_spread: 20000, - base_asset_amount_with_amm: (AMM_RESERVE_PRECISION / 2) as i128, - base_asset_amount_long: (AMM_RESERVE_PRECISION / 2) as i128, - user_lp_shares: 20 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 100, - concentration_coef: CONCENTRATION_PRECISION + 1, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - let (new_ask_base_asset_reserve, new_ask_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Long) - .unwrap(); - let (new_bid_base_asset_reserve, new_bid_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Short) - .unwrap(); - market.amm.ask_base_asset_reserve = new_ask_base_asset_reserve; - market.amm.bid_base_asset_reserve = new_bid_base_asset_reserve; - market.amm.ask_quote_asset_reserve = new_ask_quote_asset_reserve; - market.amm.bid_quote_asset_reserve = new_bid_quote_asset_reserve; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64, - slot: 0, - auction_start_price: 0, - auction_end_price: 100 * PRICE_PRECISION_I64, - auction_duration: 0, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -BASE_PRECISION_I64, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 / 2, - price: 100 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 / 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - let (base_asset_amount, _) = fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 100 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(market.amm.historical_oracle_data.last_oracle_price), - now, - slot, - 0, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - // base is filled - assert!(base_asset_amount > 0); - assert_eq!(base_asset_amount, 1000000000); // 1 base - // let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let market_after = market_map.get_ref(&0).unwrap(); - assert!( - market_after.amm.base_asset_amount_with_amm.abs() - < market.amm.base_asset_amount_with_amm.abs() - ); - - let quote_asset_amount_surplus = market_after.amm.total_mm_fee - market.amm.total_mm_fee; - assert!(quote_asset_amount_surplus > 0); - assert_eq!(market_after.amm.base_asset_amount_with_amm, 100000000); - assert_eq!(market_after.amm.base_asset_amount_long, 1000000000); - assert_eq!(market_after.amm.base_asset_amount_short, -1000000000); - - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - -100000000 - ); - - assert_eq!(market.amm.quote_asset_amount_per_lp, 0); - assert_eq!(market_after.amm.quote_asset_amount_per_lp, -497272); - - assert_eq!(market.amm.base_asset_amount_per_lp, 0); - assert_eq!(market_after.amm.base_asset_amount_per_lp, 5000000); - - assert_eq!(market_after.amm.total_exchange_fee, 32373); - assert_eq!(market_after.amm.total_fee_minus_distributions, 36039); - - crate::validation::perp_market::validate_perp_market(&market).unwrap(); - crate::validation::perp_market::validate_perp_market(&market_after).unwrap(); - } - #[test] fn fulfill_with_amm_jit_taker_short_unavailable_amm() { let now = 0_i64; diff --git a/programs/drift/src/controller/orders/amm_lp_jit_tests.rs b/programs/drift/src/controller/orders/amm_lp_jit_tests.rs index 2ef6a02bb7..550574a1c7 100644 --- a/programs/drift/src/controller/orders/amm_lp_jit_tests.rs +++ b/programs/drift/src/controller/orders/amm_lp_jit_tests.rs @@ -47,7 +47,7 @@ pub mod amm_lp_jit { use crate::math::constants::{CONCENTRATION_PRECISION, PRICE_PRECISION_U64}; use crate::state::fill_mode::FillMode; use crate::state::oracle::{HistoricalOracleData, OracleSource}; - use crate::state::perp_market::{AMMLiquiditySplit, MarketStatus, PerpMarket, AMM}; + use crate::state::perp_market::{MarketStatus, PerpMarket, AMM}; use crate::state::perp_market_map::PerpMarketMap; use crate::state::spot_market::{SpotBalanceType, SpotMarket}; use crate::state::spot_market_map::SpotMarketMap; @@ -106,7 +106,6 @@ pub mod amm_lp_jit { 100 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 500000000); @@ -117,7 +116,6 @@ pub mod amm_lp_jit { 100 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 500000000); @@ -128,7 +126,6 @@ pub mod amm_lp_jit { 99_920_000, Some(100 * PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 300000000); @@ -139,7 +136,6 @@ pub mod amm_lp_jit { 99 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 0); @@ -153,7 +149,6 @@ pub mod amm_lp_jit { 99 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 45454000); @@ -164,7 +159,6 @@ pub mod amm_lp_jit { 101 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 45454000); @@ -175,7 +169,6 @@ pub mod amm_lp_jit { 102 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 0); @@ -186,7 +179,6 @@ pub mod amm_lp_jit { 104 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 0); @@ -199,7 +191,6 @@ pub mod amm_lp_jit { 104 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 0); @@ -210,7 +201,6 @@ pub mod amm_lp_jit { 105 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 0); @@ -222,7 +212,6 @@ pub mod amm_lp_jit { 105 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 9803000); @@ -233,7 +222,6 @@ pub mod amm_lp_jit { 95 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 0); @@ -301,7 +289,6 @@ pub mod amm_lp_jit { 100 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Short, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 500000000); @@ -312,12 +299,11 @@ pub mod amm_lp_jit { 100 * PRICE_PRECISION_U64, Some(100 * PRICE_PRECISION_I64), PositionDirection::Long, - AMMLiquiditySplit::Shared, ) .unwrap(); assert_eq!(jit_base_asset_amount, 500000000); - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( + let jit_base_asset_amount = calculate_amm_jit_liquidity( &mut market, PositionDirection::Short, 100 * PRICE_PRECISION_U64, @@ -326,146 +312,9 @@ pub mod amm_lp_jit { BASE_PRECISION_U64, BASE_PRECISION_U64, false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::ProtocolOwned); - assert_eq!(jit_base_asset_amount, 500000000); - } - - #[test] - fn amm_lp_jit_amm_lp_same_side_imbalanced() { - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, // lps are long vs target, wants shorts - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - base_asset_amount_with_amm: -((AMM_RESERVE_PRECISION / 2) as i128), // amm is too long vs target, wants shorts - base_asset_amount_short: -((AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 1000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - base_spread: 20000, - long_spread: 20000, - short_spread: 20000, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - // lp needs nearly 5 base to get to target - assert_eq!( - market.amm.imbalanced_base_asset_amount_with_lp().unwrap(), - 4_941_986_570 - ); - - let (new_ask_base_asset_reserve, new_ask_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Long) - .unwrap(); - let (new_bid_base_asset_reserve, new_bid_quote_asset_reserve) = - crate::math::amm_spread::calculate_spread_reserves(&market, PositionDirection::Short) - .unwrap(); - market.amm.ask_base_asset_reserve = new_ask_base_asset_reserve; - market.amm.bid_base_asset_reserve = new_bid_base_asset_reserve; - market.amm.ask_quote_asset_reserve = new_ask_quote_asset_reserve; - market.amm.bid_quote_asset_reserve = new_bid_quote_asset_reserve; - - let amm_inventory_pct = calculate_inventory_liquidity_ratio( - market.amm.base_asset_amount_with_amm, - market.amm.base_asset_reserve, - market.amm.min_base_asset_reserve, - market.amm.max_base_asset_reserve, - ) - .unwrap(); - assert_eq!(amm_inventory_pct, PERCENTAGE_PRECISION_I128 / 200); // .5% of amm inventory is in position - - // maker order satisfies taker, vAMM doing match - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Long, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64, - BASE_PRECISION_U64, - false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::Shared); - assert_eq!(jit_base_asset_amount, 500000000); - - // taker order is heading to vAMM - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Long, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64 * 2, - BASE_PRECISION_U64, - false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::ProtocolOwned); - assert_eq!(jit_base_asset_amount, 0); // its coming anyways - - // no jit for additional long (more shorts for amm) - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Long, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64 * 100, - BASE_PRECISION_U64 * 100, - false, - None, ) .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::Shared); assert_eq!(jit_base_asset_amount, 500000000); - - // wrong direction (increases lp and vamm inventory) - let (jit_base_asset_amount, amm_liquidity_split) = calculate_amm_jit_liquidity( - &mut market, - PositionDirection::Short, - 100 * PRICE_PRECISION_U64, - Some(100 * PRICE_PRECISION_I64), - BASE_PRECISION_U64, - BASE_PRECISION_U64, - BASE_PRECISION_U64, - false, - None, - ) - .unwrap(); - assert_eq!(amm_liquidity_split, AMMLiquiditySplit::ProtocolOwned); - assert_eq!(jit_base_asset_amount, 0); } #[test] @@ -676,654 +525,6 @@ pub mod amm_lp_jit { assert_eq!(market_after.amm.total_exchange_fee, 7500); } - #[test] - fn fulfill_with_amm_lp_jit_small_maker_order() { - let now = 0_i64; - let slot = 5_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: -((AMM_RESERVE_PRECISION / 2) as i128), - base_asset_amount_short: -((AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 90 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 / 2 + BASE_PRECISION_U64 * 2, // if amm takes half it would flip - slot: 0, - price: 100 * PRICE_PRECISION as u64, - auction_start_price: 0, - auction_end_price: 200 * PRICE_PRECISION_I64, - auction_duration: 10, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 / 2 + BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 1000 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64 + BASE_PRECISION_U64 / 2, // maker wants full = amm wants BASE_PERCISION - price: 99 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -(BASE_PRECISION_I64 + BASE_PRECISION_I64 / 2), - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 99 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(PRICE_PRECISION_I64), - now, - slot, - 10, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - // maker got full size - let maker = makers_and_referrers.get_ref_mut(&maker_key).unwrap(); - let maker_position = &maker.perp_positions[0]; - assert_eq!( - maker_position.base_asset_amount, - -(BASE_PRECISION_I64 + BASE_PRECISION_I64 / 2) - ); - - // nets to zero - let market_after = market_map.get_ref(&0).unwrap(); - - // make sure lps got more - assert_eq!(market_after.amm.base_asset_amount_per_lp, -510801343); - assert_eq!(market_after.amm.base_asset_amount_with_amm, -50000000); - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - 50000000 - ); - - assert!(market_after.amm.base_asset_amount_per_lp != market.amm.base_asset_amount_per_lp); - assert!(market_after.amm.quote_asset_amount_per_lp != market.amm.quote_asset_amount_per_lp); - assert_eq!(market_after.amm.total_fee_minus_distributions, 2488712); //2510987 would-be w/o LP - assert_eq!(market_after.amm.total_exchange_fee, 47025); - } - - #[test] - fn fulfill_with_amm_lp_jit_taker_long_max_amount() { - let now = 0_i64; - let slot = 0_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: -((AMM_RESERVE_PRECISION / 2) as i128), - base_asset_amount_short: -((AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 * 2, // if amm takes half it would flip - slot: 0, - price: 100 * PRICE_PRECISION as u64, - auction_start_price: 0, - auction_end_price: 100 * PRICE_PRECISION_I64, - auction_duration: 0, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64 * 2, // maker wants full = amm wants BASE_PERCISION - price: 99 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 99 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(PRICE_PRECISION_I64), - now, - slot, - 10, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - assert_eq!(market.amm.base_asset_amount_with_amm, -500000000); - assert_eq!(market.amm.base_asset_amount_per_lp, -505801343); - - let market_after = market_map.get_ref(&0).unwrap(); - - // make sure moves closer TODO - assert_eq!(market_after.amm.base_asset_amount_per_lp, -510801343); - - // nets to zero - assert_eq!(market_after.amm.base_asset_amount_with_amm, -50000000); - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - 50000000 - ); - - let maker = makers_and_referrers.get_ref_mut(&maker_key).unwrap(); - let maker_position: &PerpPosition = &maker.perp_positions[0]; - // maker got (full - net_baa) - assert_eq!( - maker_position.base_asset_amount as i128, - -BASE_PRECISION_I128 * 2 - market.amm.base_asset_amount_with_amm - ); - - let taker_position: &PerpPosition = &taker.perp_positions[0]; - assert_eq!( - taker_position.base_asset_amount as i128, - BASE_PRECISION_I128 * 2 - ); - } - - #[test] - fn fulfill_with_amm_lp_only_jit_taker_long_max_amount() { - let now = 0_i64; - let slot = 0_u64; - - let mut oracle_price = get_pyth_price(100, 6); - let oracle_price_key = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let pyth_program = crate::ids::pyth_program::id(); - create_account_info!( - oracle_price, - &oracle_price_key, - &pyth_program, - oracle_account_info - ); - let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); - - // net users are short - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - terminal_quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_per_lp: -505801343, - quote_asset_amount_per_lp: 10715933, - target_base_asset_amount_per_lp: -1000000000, - bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, - bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, - ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 0, - base_asset_amount_long: ((166 * AMM_RESERVE_PRECISION / 2) as i128), - base_asset_amount_short: -((166 * AMM_RESERVE_PRECISION / 2) as i128), - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 100 * PEG_PRECISION, - max_slippage_ratio: 50, - max_fill_reserve_fraction: 100, - order_step_size: 10000000, - order_tick_size: 1, - oracle: oracle_price_key, - amm_jit_intensity: 200, - historical_oracle_data: HistoricalOracleData { - last_oracle_price: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap: (100 * PRICE_PRECISION) as i64, - last_oracle_price_twap_5min: (100 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, // some lps exist - concentration_coef: CONCENTRATION_PRECISION + 1, - ..AMM::default() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - status: MarketStatus::Initialized, - ..PerpMarket::default_test() - }; - market.amm.max_base_asset_reserve = u64::MAX as u128; - market.amm.min_base_asset_reserve = 0; - - create_anchor_account_info!(market, PerpMarket, market_account_info); - let market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); - - let mut spot_market = SpotMarket { - market_index: 0, - oracle_source: OracleSource::QuoteAsset, - cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, - decimals: 6, - initial_asset_weight: SPOT_WEIGHT_PRECISION, - maintenance_asset_weight: SPOT_WEIGHT_PRECISION, - historical_oracle_data: HistoricalOracleData::default_price(QUOTE_PRECISION_I64), - ..SpotMarket::default() - }; - create_anchor_account_info!(spot_market, SpotMarket, spot_market_account_info); - let spot_market_map = SpotMarketMap::load_one(&spot_market_account_info, true).unwrap(); - - // taker wants to go long (would improve balance) - let mut taker = User { - orders: get_orders(Order { - market_index: 0, - status: OrderStatus::Open, - order_type: OrderType::Market, - direction: PositionDirection::Long, - base_asset_amount: BASE_PRECISION_U64 * 2, // if amm takes half it would flip - slot: 0, - price: 100 * PRICE_PRECISION as u64, - auction_start_price: 0, - auction_end_price: 100 * PRICE_PRECISION_I64, - auction_duration: 0, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_bids: BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - - let maker_key = Pubkey::from_str("My11111111111111111111111111111111111111113").unwrap(); - let maker_authority = - Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); - let mut maker = User { - authority: maker_authority, - orders: get_orders(Order { - market_index: 0, - post_only: true, - order_type: OrderType::Limit, - direction: PositionDirection::Short, - base_asset_amount: BASE_PRECISION_U64 * 2, // maker wants full = amm wants BASE_PERCISION - price: 99 * PRICE_PRECISION_U64, - ..Order::default() - }), - perp_positions: get_positions(PerpPosition { - market_index: 0, - open_orders: 1, - open_asks: -BASE_PRECISION_I64 * 2, - ..PerpPosition::default() - }), - spot_positions: get_spot_positions(SpotPosition { - market_index: 0, - balance_type: SpotBalanceType::Deposit, - scaled_balance: 100 * 100 * SPOT_BALANCE_PRECISION_U64, - ..SpotPosition::default() - }), - ..User::default() - }; - create_anchor_account_info!(maker, &maker_key, User, maker_account_info); - let makers_and_referrers = UserMap::load_one(&maker_account_info).unwrap(); - - let mut filler = User::default(); - - let fee_structure = get_fee_structure(); - - let (taker_key, _, filler_key) = get_user_keys(); - - let mut taker_stats = UserStats::default(); - - let mut maker_stats = UserStats { - authority: maker_authority, - ..UserStats::default() - }; - create_anchor_account_info!(maker_stats, UserStats, maker_stats_account_info); - let maker_and_referrer_stats = UserStatsMap::load_one(&maker_stats_account_info).unwrap(); - - let mut filler_stats = UserStats::default(); - - assert_eq!(market.amm.total_fee, 0); - assert_eq!(market.amm.total_fee_minus_distributions, 0); - assert_eq!(market.amm.net_revenue_since_last_funding, 0); - assert_eq!(market.amm.total_mm_fee, 0); - assert_eq!(market.amm.total_fee_withdrawn, 0); - - fulfill_perp_order( - &mut taker, - 0, - &taker_key, - &mut taker_stats, - &makers_and_referrers, - &maker_and_referrer_stats, - &[(maker_key, 0, 99 * PRICE_PRECISION_U64)], - &mut Some(&mut filler), - &filler_key, - &mut Some(&mut filler_stats), - None, - &spot_market_map, - &market_map, - &mut oracle_map, - &fee_structure, - 0, - Some(PRICE_PRECISION_I64), - now, - slot, - 10, - crate::state::perp_market::AMMAvailability::AfterMinDuration, - FillMode::Fill, - false, - ) - .unwrap(); - - assert_eq!(market.amm.base_asset_amount_per_lp, -505801343); - assert_eq!(market.amm.quote_asset_amount_per_lp, 10715933); - - let market_after = market_map.get_ref(&0).unwrap(); - - // make sure moves closer - assert_eq!(market_after.amm.base_asset_amount_per_lp, -605801343); - assert_eq!(market_after.amm.quote_asset_amount_per_lp, 20619497); - - // nets to zero - assert_eq!(market_after.amm.base_asset_amount_with_amm, 0); - assert_eq!(market_after.amm.base_asset_amount_long, 85000000000); - assert_eq!(market_after.amm.base_asset_amount_short, -84000000000); - - assert_eq!( - market_after.amm.base_asset_amount_with_unsettled_lp, - 1000000000 - ); - validate_perp_market(&market).unwrap(); - validate_perp_market(&market_after).unwrap(); - - let maker = makers_and_referrers.get_ref_mut(&maker_key).unwrap(); - let maker_position: &PerpPosition = &maker.perp_positions[0]; - // maker got (full - net_unsettled_lp) - assert_eq!( - maker_position.base_asset_amount as i128, - -(BASE_PRECISION_I128 * 2) + market_after.amm.base_asset_amount_with_unsettled_lp - ); - - let taker_position: &PerpPosition = &taker.perp_positions[0]; - assert_eq!( - taker_position.base_asset_amount as i128, - BASE_PRECISION_I128 * 2 - ); - } - #[test] fn fulfill_with_amm_lp_jit_taker_short_max_amount() { let now = 0_i64; diff --git a/programs/drift/src/controller/orders/tests.rs b/programs/drift/src/controller/orders/tests.rs index 96038e98c7..b5e681f090 100644 --- a/programs/drift/src/controller/orders/tests.rs +++ b/programs/drift/src/controller/orders/tests.rs @@ -486,7 +486,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -610,7 +609,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -734,7 +732,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -858,7 +855,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -981,7 +977,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1071,7 +1066,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1162,7 +1156,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1253,7 +1246,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1344,7 +1336,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1455,7 +1446,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1571,7 +1561,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1692,7 +1681,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1814,7 +1802,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -1960,7 +1947,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -2081,7 +2067,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -2212,7 +2197,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2364,7 +2348,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2514,7 +2497,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2665,7 +2647,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut oracle_map, false, - None, ) .unwrap(); @@ -2797,7 +2778,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); @@ -2928,7 +2908,6 @@ pub mod fulfill_order_with_maker_order { &fee_structure, &mut get_oracle_map(), false, - None, ) .unwrap(); diff --git a/programs/drift/src/controller/pnl.rs b/programs/drift/src/controller/pnl.rs index 5514123de8..166030d6ee 100644 --- a/programs/drift/src/controller/pnl.rs +++ b/programs/drift/src/controller/pnl.rs @@ -1,9 +1,6 @@ use crate::controller::amm::{update_pnl_pool_and_user_balance, update_pool_balances}; use crate::controller::funding::settle_funding_payment; -use crate::controller::orders::{ - attempt_burn_user_lp_shares_for_risk_reduction, cancel_orders, - validate_market_within_price_band, -}; +use crate::controller::orders::{cancel_orders, validate_market_within_price_band}; use crate::controller::position::{ get_position_index, update_position_and_market, update_quote_asset_amount, update_quote_asset_and_break_even_amount, update_settled_pnl, PositionDelta, @@ -81,7 +78,7 @@ pub fn settle_pnl( validate_market_within_price_band(&market, state, oracle_price)?; - crate::controller::lp::settle_funding_payment_then_lp(user, user_key, &mut market, now)?; + settle_funding_payment(user, user_key, &mut market, now)?; drop(market); @@ -89,48 +86,7 @@ pub fn settle_pnl( let unrealized_pnl = user.perp_positions[position_index].get_unrealized_pnl(oracle_price)?; // cannot settle negative pnl this way on a user who is in liquidation territory - if user.perp_positions[position_index].is_lp() && !user.is_advanced_lp() { - let margin_calc = calculate_margin_requirement_and_total_collateral_and_liability_info( - user, - perp_market_map, - spot_market_map, - oracle_map, - MarginContext::standard(MarginRequirementType::Initial) - .margin_buffer(state.liquidation_margin_buffer_ratio), - )?; - - if !margin_calc.meets_margin_requirement() { - msg!("market={} lp does not meet initial margin requirement, attempting to burn shares for risk reduction", - market_index); - attempt_burn_user_lp_shares_for_risk_reduction( - state, - user, - *user_key, - margin_calc, - perp_market_map, - spot_market_map, - oracle_map, - clock, - market_index, - )?; - - // if the unrealized pnl is negative, return early after trying to burn shares - if unrealized_pnl < 0 - && !(meets_settle_pnl_maintenance_margin_requirement( - user, - perp_market_map, - spot_market_map, - oracle_map, - )?) - { - msg!( - "Unable to settle market={} negative pnl as user is in liquidation territory", - market_index - ); - return Ok(()); - } - } - } else if unrealized_pnl < 0 { + if unrealized_pnl < 0 { // may already be cached let meets_margin_requirement = match meets_margin_requirement { Some(meets_margin_requirement) => meets_margin_requirement, @@ -462,12 +418,6 @@ pub fn settle_expired_position( "User must first cancel open orders for expired market" )?; - validate!( - user.perp_positions[position_index].lp_shares == 0, - ErrorCode::PerpMarketSettlementUserHasActiveLP, - "User must first burn lp shares for expired market" - )?; - let base_asset_value = calculate_base_asset_value_with_expiry_price( &user.perp_positions[position_index], perp_market.expiry_price, @@ -479,7 +429,6 @@ pub fn settle_expired_position( let position_delta = PositionDelta { quote_asset_amount: base_asset_value, base_asset_amount: -user.perp_positions[position_index].base_asset_amount, - remainder_base_asset_amount: None, }; update_position_and_market( diff --git a/programs/drift/src/controller/position.rs b/programs/drift/src/controller/position.rs index 53b76188d4..97add169ea 100644 --- a/programs/drift/src/controller/position.rs +++ b/programs/drift/src/controller/position.rs @@ -17,7 +17,7 @@ use crate::math::position::{ use crate::math::safe_math::SafeMath; use crate::math_error; use crate::safe_increment; -use crate::state::perp_market::{AMMLiquiditySplit, PerpMarket}; +use crate::state::perp_market::PerpMarket; use crate::state::user::{PerpPosition, PerpPositions, User}; use crate::validate; @@ -74,21 +74,11 @@ pub fn get_position_index(user_positions: &PerpPositions, market_index: u16) -> pub struct PositionDelta { pub quote_asset_amount: i64, pub base_asset_amount: i64, - pub remainder_base_asset_amount: Option, } impl PositionDelta { - pub fn get_delta_base_with_remainder_abs(&self) -> DriftResult { - let delta_base_i128 = - if let Some(remainder_base_asset_amount) = self.remainder_base_asset_amount { - self.base_asset_amount - .safe_add(remainder_base_asset_amount.cast()?)? - .abs() - .cast::()? - } else { - self.base_asset_amount.abs().cast::()? - }; - Ok(delta_base_i128) + pub fn get_delta_base_abs(&self) -> DriftResult { + self.base_asset_amount.abs().cast::() } } @@ -97,7 +87,7 @@ pub fn update_position_and_market( market: &mut PerpMarket, delta: &PositionDelta, ) -> DriftResult { - if delta.base_asset_amount == 0 && delta.remainder_base_asset_amount.unwrap_or(0) == 0 { + if delta.base_asset_amount == 0 { update_quote_asset_amount(position, market, delta.quote_asset_amount)?; return Ok(delta.quote_asset_amount); } @@ -105,14 +95,8 @@ pub fn update_position_and_market( let update_type = get_position_update_type(position, delta)?; // Update User - let ( - new_base_asset_amount, - new_settled_base_asset_amount, - new_quote_asset_amount, - new_remainder_base_asset_amount, - ) = get_new_position_amounts(position, delta, market)?; - - market.update_market_with_counterparty(delta, new_settled_base_asset_amount)?; + let (new_base_asset_amount, new_quote_asset_amount) = + get_new_position_amounts(position, delta)?; let (new_quote_entry_amount, new_quote_break_even_amount, pnl) = match update_type { PositionUpdateType::Open | PositionUpdateType::Increase => { @@ -127,8 +111,8 @@ pub fn update_position_and_market( (new_quote_entry_amount, new_quote_break_even_amount, 0_i64) } PositionUpdateType::Reduce | PositionUpdateType::Close => { - let current_base_i128 = position.get_base_asset_amount_with_remainder_abs()?; - let delta_base_i128 = delta.get_delta_base_with_remainder_abs()?; + let current_base_i128 = position.get_base_asset_amount_abs()?; + let delta_base_i128 = delta.get_delta_base_abs()?; let new_quote_entry_amount = position.quote_entry_amount.safe_sub( position @@ -156,8 +140,8 @@ pub fn update_position_and_market( (new_quote_entry_amount, new_quote_break_even_amount, pnl) } PositionUpdateType::Flip => { - let current_base_i128 = position.get_base_asset_amount_with_remainder_abs()?; - let delta_base_i128 = delta.get_delta_base_with_remainder_abs()?; + let current_base_i128 = position.get_base_asset_amount_abs()?; + let delta_base_i128 = delta.get_delta_base_abs()?; // same calculation for new_quote_entry_amount let new_quote_break_even_amount = delta.quote_asset_amount.safe_sub( @@ -209,7 +193,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_long = market .amm .base_asset_amount_long - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_long = market .amm .quote_entry_amount_long @@ -223,7 +207,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_short = market .amm .base_asset_amount_short - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_short = market .amm .quote_entry_amount_short @@ -239,7 +223,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_long = market .amm .base_asset_amount_long - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_long = market.amm.quote_entry_amount_long.safe_sub( position .quote_entry_amount @@ -257,7 +241,7 @@ pub fn update_position_and_market( market.amm.base_asset_amount_short = market .amm .base_asset_amount_short - .safe_add(new_settled_base_asset_amount.cast()?)?; + .safe_add(delta.base_asset_amount.cast()?)?; market.amm.quote_entry_amount_short = market.amm.quote_entry_amount_short.safe_sub( position @@ -358,9 +342,6 @@ pub fn update_position_and_market( _ => {} } - let new_position_base_with_remainder = - new_base_asset_amount.safe_add(new_remainder_base_asset_amount)?; - // Update user position if let PositionUpdateType::Close = update_type { position.last_cumulative_funding_rate = 0; @@ -368,7 +349,7 @@ pub fn update_position_and_market( update_type, PositionUpdateType::Open | PositionUpdateType::Increase | PositionUpdateType::Flip ) { - if new_position_base_with_remainder > 0 { + if new_base_asset_amount > 0 { position.last_cumulative_funding_rate = market.amm.cumulative_funding_rate_long.cast()?; } else { @@ -388,7 +369,6 @@ pub fn update_position_and_market( new_base_asset_amount )?; - position.remainder_base_asset_amount = new_remainder_base_asset_amount.cast::()?; position.base_asset_amount = new_base_asset_amount; position.quote_asset_amount = new_quote_asset_amount; @@ -398,62 +378,6 @@ pub fn update_position_and_market( Ok(pnl) } -pub fn update_lp_market_position( - market: &mut PerpMarket, - delta: &PositionDelta, - fee_to_market: i128, - liquidity_split: AMMLiquiditySplit, -) -> DriftResult { - if market.amm.user_lp_shares == 0 || liquidity_split == AMMLiquiditySplit::ProtocolOwned { - return Ok(0); // no need to split with LP - } - - let base_unit: i128 = market.amm.get_per_lp_base_unit()?; - - let (per_lp_delta_base, per_lp_delta_quote, per_lp_fee) = - market - .amm - .calculate_per_lp_delta(delta, fee_to_market, liquidity_split, base_unit)?; - - market.amm.base_asset_amount_per_lp = market - .amm - .base_asset_amount_per_lp - .safe_add(-per_lp_delta_base)?; - - market.amm.quote_asset_amount_per_lp = market - .amm - .quote_asset_amount_per_lp - .safe_add(-per_lp_delta_quote)?; - - // update per lp position - market.amm.quote_asset_amount_per_lp = - market.amm.quote_asset_amount_per_lp.safe_add(per_lp_fee)?; - - let lp_delta_base = market - .amm - .calculate_lp_base_delta(per_lp_delta_base, base_unit)?; - let lp_delta_quote = market - .amm - .calculate_lp_base_delta(per_lp_delta_quote, base_unit)?; - - market.amm.base_asset_amount_with_amm = market - .amm - .base_asset_amount_with_amm - .safe_sub(lp_delta_base)?; - - market.amm.base_asset_amount_with_unsettled_lp = market - .amm - .base_asset_amount_with_unsettled_lp - .safe_add(lp_delta_base)?; - - market.amm.quote_asset_amount_with_unsettled_lp = market - .amm - .quote_asset_amount_with_unsettled_lp - .safe_add(lp_delta_quote.cast()?)?; - - Ok(lp_delta_base) -} - pub fn update_position_with_base_asset_amount( base_asset_amount: u64, direction: PositionDirection, @@ -549,10 +473,7 @@ pub fn update_quote_asset_amount( return Ok(()); } - if position.quote_asset_amount == 0 - && position.base_asset_amount == 0 - && position.remainder_base_asset_amount == 0 - { + if position.quote_asset_amount == 0 && position.base_asset_amount == 0 { market.number_of_users = market.number_of_users.safe_add(1)?; } @@ -560,10 +481,7 @@ pub fn update_quote_asset_amount( market.amm.quote_asset_amount = market.amm.quote_asset_amount.safe_add(delta.cast()?)?; - if position.quote_asset_amount == 0 - && position.base_asset_amount == 0 - && position.remainder_base_asset_amount == 0 - { + if position.quote_asset_amount == 0 && position.base_asset_amount == 0 { market.number_of_users = market.number_of_users.saturating_sub(1); } diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index 71d7520efc..b80f19c121 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -1,10 +1,7 @@ use crate::controller::amm::{ calculate_base_swap_output_with_spread, move_price, recenter_perp_market_amm, swap_base_asset, }; -use crate::controller::lp::{apply_lp_rebase_to_perp_market, settle_lp_position}; -use crate::controller::position::{ - update_lp_market_position, update_position_and_market, PositionDelta, -}; +use crate::controller::position::{update_position_and_market, PositionDelta}; use crate::controller::repeg::_update_amm; use crate::math::amm::calculate_market_open_bids_asks; @@ -13,13 +10,12 @@ use crate::math::constants::{ PRICE_PRECISION_I64, PRICE_PRECISION_U64, QUOTE_PRECISION_I128, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, }; -use crate::math::lp::calculate_settle_lp_metrics; use crate::math::oracle::OracleValidity; use crate::math::position::swap_direction_to_close_position; use crate::math::repeg; use crate::state::oracle::{MMOraclePriceData, OraclePriceData, PrelaunchOracle}; use crate::state::oracle_map::OracleMap; -use crate::state::perp_market::{AMMLiquiditySplit, PerpMarket, AMM}; +use crate::state::perp_market::{PerpMarket, AMM}; use crate::state::perp_market_map::PerpMarketMap; use crate::state::state::State; use crate::state::user::PerpPosition; @@ -44,34 +40,6 @@ use anchor_lang::Owner; use solana_program::pubkey::Pubkey; use std::str::FromStr; -#[test] -fn full_amm_split() { - let delta = PositionDelta { - base_asset_amount: 10 * BASE_PRECISION_I64, - quote_asset_amount: -10 * BASE_PRECISION_I64, - remainder_base_asset_amount: None, - }; - - let amm = AMM { - user_lp_shares: 0, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 10 * AMM_RESERVE_PRECISION_I128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - update_lp_market_position(&mut market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 0); - assert_eq!( - market.amm.base_asset_amount_with_amm, - 10 * AMM_RESERVE_PRECISION_I128 - ); -} - #[test] fn amm_pool_balance_liq_fees_example() { let perp_market_str = String::from("Ct8MLGv1N/dquEe6RHLCjPXRFs689/VXwfnq/aHEADtX6J/C8GaZXDKZ6iACt2rxmu8p8Fh+gR3ERNNiw5jAdKhvts0jU4yP8/YGAAAAAAAAAAAAAAAAAAEAAAAAAAAAYOoGAAAAAAD08AYAAAAAAFDQ0WcAAAAAU20cou///////////////zqG0jcAAAAAAAAAAAAAAACyy62lmssEAAAAAAAAAAAAAAAAAAAAAACuEBLjOOAUAAAAAAAAAAAAiQqZJDPTFAAAAAAAAAAAANiFEAAAAAAAAAAAAAAAAABEI0dQmUcTAAAAAAAAAAAAxIkaBDObFgAAAAAAAAAAAD4fkf+02RQAAAAAAAAAAABN+wYAAAAAAAAAAAAAAAAAy1BRbfXSFAAAAAAAAAAAAADOOHkhTQcAAAAAAAAAAAAAFBriILP4////////////SMyW3j0AAAAAAAAAAAAAALgVvHwEAAAAAAAAAAAAAAAAADQm9WscAAAAAAAAAAAAURkvFjoAAAAAAAAAAAAAAHIxjo/f/f/////////////TuoG31QEAAAAAAAAAAAAAP8QC+7L9/////////////3SO4oj1AQAAAAAAAAAAAAAAgFcGo5wAAAAAAAAAAAAAzxUAAAAAAADPFQAAAAAAAM8VAAAAAAAAPQwAAAAAAABk1DIXBgEAAAAAAAAAAAAAKqQCt7MAAAAAAAAAAAAAAP0Q55dSAAAAAAAAAAAAAACS+qA0KQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALB5hg2UAAAAAAAAAAAAAAAnMANRAAAAAAAAAAAAAAAAmdj/UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+LAqY3t8UAAAAAAAAAAAAhk/TOI3TFAAAAAAAAAAAAG1uRreN4BQAAAAAAAAAAABkKKeG3tIUAAAAAAAAAAAA8/YGAAAAAAD+/////////2DqBgAAAAAA5OoGAAAAAACi6gYAAAAAAKzxBgAAAAAAMj1zEwAAAABIAgAAAAAAAIy24v//////tMvRZwAAAAAQDgAAAAAAAADKmjsAAAAAZAAAAAAAAAAA8gUqAQAAAAAAAAAAAAAAs3+BskEAAADIfXYRAAAAAIIeqQIAAAAAdb7RZwAAAABxDAAAAAAAAJMMAAAAAAAAUNDRZwAAAAD6AAAA1DAAAIQAAAB9AAAAfgAAAAAAAABkADIAZGQMAQAAAAADAAAAX79DBQAAAABIC9oEAwAAAK3TwZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFdJRi1QRVJQICAgICAgICAgICAgICAgICAgICAgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADd4BgAAAAAAlCUAAAAAAAAcCgAAAAAAAGQAAABkAAAAqGEAAFDDAADECQAA4gQAAAAAAAAQJwAA2QAAAIgBAAAXAAEAAwAAAAAAAAEBAOgD9AEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); @@ -1144,6 +1112,8 @@ fn amm_perp_ref_offset() { AccountLoader::try_from(&perp_market_account_info).unwrap(); let mut perp_market = perp_market_loader.load_mut().unwrap(); + perp_market.amm.base_asset_amount_with_amm = 40000000000; // override old LP related fields + let reserve_price = perp_market.amm.reserve_price().unwrap(); let (b1, a1) = perp_market.amm.bid_ask_price(reserve_price).unwrap(); assert_eq!(reserve_price, 7101599); @@ -1306,573 +1276,12 @@ fn amm_perp_ref_offset() { assert_eq!(a3, 7138903); } -#[test] -fn amm_split_large_k() { - let perp_market_str = String::from("Ct8MLGv1N/dvAH3EF67yBqaUQerctpm4yqpK+QNSrXCQz76p+B+ka+8Ni2/aLOukHaFdQJXR2jkqDS+O0MbHvA9M+sjCgLVtQwhkAQAAAAAAAAAAAAAAAAIAAAAAAAAAkI1kAQAAAAB6XWQBAAAAAO8yzWQAAAAAnJ7I3f///////////////2dHvwAAAAAAAAAAAAAAAABGiVjX6roAAAAAAAAAAAAAAAAAAAAAAAB1tO47J+xiAAAAAAAAAAAAGD03Fis3mgAAAAAAAAAAAJxiDwAAAAAAAAAAAAAAAABxqRCIGRxiAAAAAAAAAAAAEy8wZfK9YwAAAAAAAAAAAGZeZCE+g3sAAAAAAAAAAAAKYeQAAAAAAAAAAAAAAAAAlIvoyyc3mgAAAAAAAAAAAADQdQKjbgAAAAAAAAAAAAAAwu8g05H/////////////E6tNHAIAAAAAAAAAAAAAAO3mFwd0AAAAAAAAAAAAAAAAgPQg5rUAAAAAAAAAAAAAGkDtXR4AAAAAAAAAAAAAAEv0WeZW/f////////////9kUidaqAIAAAAAAAAAAAAA0ZMEr1H9/////////////w5/U3uqAgAAAAAAAAAAAAAANfbqfCd3AAAAAAAAAAAAIhABAAAAAAAiEAEAAAAAACIQAQAAAAAAY1QBAAAAAAA5f3WMVAAAAAAAAAAAAAAAFhkiihsAAAAAAAAAAAAAAO2EfWc5AAAAAAAAAAAAAACM/5CAQgAAAAAAAAAAAAAAvenX0SsAAAAAAAAAAAAAALgPUogZAAAAAAAAAAAAAAC01x97AAAAAAAAAAAAAAAAOXzVbgAAAAAAAAAAAAAAAMG4+QwBAAAAAAAAAAAAAABwHI3fLeJiAAAAAAAAAAAABvigOblGmgAAAAAAAAAAALeRnZsi9mIAAAAAAAAAAAAqgs3ynCeaAAAAAAAAAAAAQwhkAQAAAAAAAAAAAAAAAJOMZAEAAAAAFKJkAQAAAABTl2QBAAAAALFuZAEAAAAAgrx7DAAAAAAUAwAAAAAAAAN1TAYAAAAAuC7NZAAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAA4fUFAAAAAAAAAAAAAAAAn2HvyMABAADGV6rZFwAAAE5Qg2oPAAAA8zHNZAAAAAAdYAAAAAAAAE2FAAAAAAAA6zLNZAAAAAD6AAAAaEIAABQDAAAUAwAAAAAAANcBAABkADIAZGQAAcDIUt4AAAAA0QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI9qQbynsAAAAAAAAAAAAAAAAAAAAAAAAFNPTC1QRVJQICAgICAgICAgICAgICAgICAgICAgICAghuS1//////8A4fUFAAAAAAB0O6QLAAAAR7PdeQMAAAD+Mc1kAAAAAADKmjsAAAAAAAAAAAAAAAAAAAAAAAAAAOULDwAAAAAAUBkAAAAAAADtAQAAAAAAAMgAAAAAAAAAECcAAKhhAADoAwAA9AEAAAAAAAAQJwAAZAIAAGQCAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let mut perp_market = perp_market_loader.load_mut().unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655); - - let og_baapl = perp_market.amm.base_asset_amount_per_lp; - let og_qaapl = perp_market.amm.quote_asset_amount_per_lp; - - // msg!("perp_market: {:?}", perp_market); - - // min long order for $2.3 - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64 / 10, - quote_asset_amount: -2300000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054758); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655); - - // min short order for $2.3 - let delta = PositionDelta { - base_asset_amount: -BASE_PRECISION_I64 / 10, - quote_asset_amount: 2300000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535654); - - let mut existing_position = PerpPosition { - market_index: 0, - base_asset_amount: 0, - quote_asset_amount: 0, - lp_shares: perp_market.amm.user_lp_shares as u64, - last_base_asset_amount_per_lp: og_baapl as i64, - last_quote_asset_amount_per_lp: og_qaapl as i64, - per_lp_base: 0, - ..PerpPosition::default() - }; - - settle_lp_position(&mut existing_position, &mut perp_market).unwrap(); - - assert_eq!(existing_position.base_asset_amount, 0); - assert_eq!(existing_position.remainder_base_asset_amount, 0); - assert_eq!(existing_position.quote_asset_amount, -33538939); // out of favor rounding - - assert_eq!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - // long order for $230 - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64 * 10, - quote_asset_amount: -230000000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574055043); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535660); - - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_baapl - perp_market.amm.base_asset_amount_per_lp) - / AMM_RESERVE_PRECISION_I128, - 9977763076 - ); - // assert_eq!((perp_market.amm.sqrt_k as i128) * (og_baapl-perp_market.amm.base_asset_amount_per_lp) / AMM_RESERVE_PRECISION_I128, 104297175); - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_qaapl - perp_market.amm.quote_asset_amount_per_lp) - / QUOTE_PRECISION_I128, - -173828625034 - ); - assert_eq!( - (perp_market.amm.sqrt_k as i128) - * (og_qaapl - perp_market.amm.quote_asset_amount_per_lp - 1) - / QUOTE_PRECISION_I128, - -208594350041 - ); - // assert_eq!(243360075047/9977763076 < 23, true); // ensure rounding in favor - - // long order for $230 - let delta = PositionDelta { - base_asset_amount: -BASE_PRECISION_I64 * 10, - quote_asset_amount: 230000000, - remainder_base_asset_amount: None, - }; - - let og_baapl = perp_market.amm.base_asset_amount_per_lp; - let og_qaapl = perp_market.amm.quote_asset_amount_per_lp; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535653); - - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_baapl - perp_market.amm.base_asset_amount_per_lp) - / AMM_RESERVE_PRECISION_I128, - -9977763076 - ); - // assert_eq!((perp_market.amm.sqrt_k as i128) * (og_baapl-perp_market.amm.base_asset_amount_per_lp) / AMM_RESERVE_PRECISION_I128, 104297175); - assert_eq!( - (perp_market.amm.sqrt_k as i128) * (og_qaapl - perp_market.amm.quote_asset_amount_per_lp) - / QUOTE_PRECISION_I128, - 243360075047 - ); - // assert_eq!(243360075047/9977763076 < 23, true); // ensure rounding in favor -} - -#[test] -fn test_quote_unsettled_lp() { - let perp_market_str = String::from("Ct8MLGv1N/dvAH3EF67yBqaUQerctpm4yqpK+QNSrXCQz76p+B+ka+8Ni2/aLOukHaFdQJXR2jkqDS+O0MbHvA9M+sjCgLVtzjkqCQAAAAAAAAAAAAAAAAIAAAAAAAAAl44wCQAAAAD54C0JAAAAAGJ4JmYAAAAAyqMxdXz//////////////wV1ZyH9//////////////8Uy592jFYPAAAAAAAAAAAAAAAAAAAAAAD6zIP0/dAIAAAAAAAAAAAA+srqThjtHwAAAAAAAAAAAJxiDwAAAAAAAAAAAAAAAAByWgjyVb4IAAAAAAAAAAAAOpuf9pLjCAAAAAAAAAAAAMRfA6LzxhAAAAAAAAAAAABs6IcCAAAAAAAAAAAAAAAAeXyo6oHtHwAAAAAAAAAAAABngilYXAEAAAAAAAAAAAAAZMIneaP+////////////GeN71uL//////////////+fnyHru//////////////8AIA8MEgUDAAAAAAAAAAAAv1P8g/EBAAAAAAAAAAAAACNQgLCty/////////////+KMQ7JGjMAAAAAAAAAAAAA4DK7xH3K/////////////2grSsB0NQAAAAAAAAAAAACsBC7WWDkCAAAAAAAAAAAAsis3AAAAAACyKzcAAAAAALIrNwAAAAAATGc8AAAAAADH51Hn/wYAAAAAAAAAAAAANXNbBAgCAAAAAAAAAAAAAPNHO0UKBQAAAAAAAAAAAABiEweaqQUAAAAAAAAAAAAAg16F138BAAAAAAAAAAAAAFBZFMk0AQAAAAAAAAAAAACoA6JpBwAAAAAAAAAAAAAALahXXQcAAAAAAAAAAAAAAMG4+QwBAAAAAAAAAAAAAADr9qfqkdAIAAAAAAAAAAAAlBk2nZ/uHwAAAAAAAAAAAHPdcUR+0QgAAAAAAAAAAAAF+03DR+sfAAAAAAAAAAAAzjkqCQAAAAAAAAAAAAAAAJXnMAkAAAAAT9IxCQAAAADyXDEJAAAAAKlJLgkAAAAAyg2YDwAAAABfBwAAAAAAANVPrUEAAAAAZW0mZgAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAA4fUFAAAAAAAAAAAAAAAAj0W2KSYpAABzqJhf6gAAAOD5o985AQAAS3gmZgAAAADxKQYAAAAAAMlUBgAAAAAAS3gmZgAAAADuAgAA7CwAAHcBAAC9AQAAAAAAAH0AAADECTIAZMgAAcDIUt4DAAAAFJMfEQAAAADBogAAAAAAAIneROQcpf//AAAAAAAAAAAAAAAAAAAAAFe4ynNxUwoAAAAAAAAAAAAAAAAAAAAAAFNPTC1QRVJQICAgICAgICAgICAgICAgICAgICAgICAgAJsy4v////8AZc0dAAAAAP8PpdToAAAANOVq3RYAAAB7cyZmAAAAAADh9QUAAAAAAAAAAAAAAAAAAAAAAAAAAEyBWwAAAAAA2DEAAAAAAABzBQAAAAAAAMgAAAAAAAAATB0AANQwAADoAwAA9AEAAAAAAAAQJwAAASoAACtgAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let mut perp_market = perp_market_loader.load_mut().unwrap(); - perp_market.amm.quote_asset_amount_with_unsettled_lp = 0; - - let mut existing_position: PerpPosition = PerpPosition::default(); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, -12324473595); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -564969495606); - - existing_position.last_quote_asset_amount_per_lp = - perp_market.amm.quote_asset_amount_per_lp as i64; - existing_position.last_base_asset_amount_per_lp = - perp_market.amm.base_asset_amount_per_lp as i64; - - let pos_delta = PositionDelta { - quote_asset_amount: QUOTE_PRECISION_I64 * 150, - base_asset_amount: -BASE_PRECISION_I64, - remainder_base_asset_amount: Some(-881), - }; - assert_eq!(perp_market.amm.quote_asset_amount_with_unsettled_lp, 0); - let fee_to_market = 1000000; // uno doll - let liq_split = AMMLiquiditySplit::Shared; - let base_unit: i128 = perp_market.amm.get_per_lp_base_unit().unwrap(); - assert_eq!(base_unit, 1_000_000_000_000); // 10^4 * base_precision - - let (per_lp_delta_base, per_lp_delta_quote, per_lp_fee) = perp_market - .amm - .calculate_per_lp_delta(&pos_delta, fee_to_market, liq_split, base_unit) - .unwrap(); - - assert_eq!(per_lp_delta_base, -211759); - assert_eq!(per_lp_delta_quote, 31764); - assert_eq!(per_lp_fee, 169); - - let pos_delta2 = PositionDelta { - quote_asset_amount: -QUOTE_PRECISION_I64 * 150, - base_asset_amount: BASE_PRECISION_I64, - remainder_base_asset_amount: Some(0), - }; - let (per_lp_delta_base, per_lp_delta_quote, per_lp_fee) = perp_market - .amm - .calculate_per_lp_delta(&pos_delta2, fee_to_market, liq_split, base_unit) - .unwrap(); - - assert_eq!(per_lp_delta_base, 211759); - assert_eq!(per_lp_delta_quote, -31763); - assert_eq!(per_lp_fee, 169); - - let expected_base_asset_amount_with_unsettled_lp = -75249424409; - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - // 0 - expected_base_asset_amount_with_unsettled_lp // ~-75 - ); - // let lp_delta_quote = perp_market - // .amm - // .calculate_lp_base_delta(per_lp_delta_quote, QUOTE_PRECISION_I128) - // .unwrap(); - // assert_eq!(lp_delta_quote, -19883754464333); - - let delta_base = - update_lp_market_position(&mut perp_market, &pos_delta, fee_to_market, liq_split).unwrap(); - assert_eq!( - perp_market.amm.user_lp_shares * 1000000 / perp_market.amm.sqrt_k, - 132561 - ); // 13.2 % of amm - assert_eq!( - perp_market.amm.quote_asset_amount_with_unsettled_lp, - 19884380 - ); // 19.884380/.132 ~= 150 (+ fee) - assert_eq!(delta_base, -132_561_910); // ~13% - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - // 0 - -75381986319 // ~-75 - ); - - // settle lp and quote with unsettled should go back to zero - existing_position.lp_shares = perp_market.amm.user_lp_shares as u64; - existing_position.per_lp_base = 3; - - let lp_metrics: crate::math::lp::LPMetrics = - calculate_settle_lp_metrics(&perp_market.amm, &existing_position).unwrap(); - - let position_delta = PositionDelta { - base_asset_amount: lp_metrics.base_asset_amount as i64, - quote_asset_amount: lp_metrics.quote_asset_amount as i64, - remainder_base_asset_amount: Some(lp_metrics.remainder_base_asset_amount as i64), - }; - - assert_eq!(position_delta.base_asset_amount, 100000000); - - assert_eq!( - position_delta.remainder_base_asset_amount.unwrap_or(0), - 32561910 - ); - - assert_eq!(position_delta.quote_asset_amount, -19778585); - - let pnl = update_position_and_market(&mut existing_position, &mut perp_market, &position_delta) - .unwrap(); - - //.132561*1e6*.8 = 106048.8 - assert_eq!(perp_market.amm.quote_asset_amount_with_unsettled_lp, 105795); //? - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - expected_base_asset_amount_with_unsettled_lp - 32561910 - ); - - assert_eq!(pnl, 0); -} - -#[test] -fn amm_split_large_k_with_rebase() { - let perp_market_str = String::from("Ct8MLGv1N/dvAH3EF67yBqaUQerctpm4yqpK+QNSrXCQz76p+B+ka+8Ni2/aLOukHaFdQJXR2jkqDS+O0MbHvA9M+sjCgLVtQwhkAQAAAAAAAAAAAAAAAAIAAAAAAAAAkI1kAQAAAAB6XWQBAAAAAO8yzWQAAAAAnJ7I3f///////////////2dHvwAAAAAAAAAAAAAAAABGiVjX6roAAAAAAAAAAAAAAAAAAAAAAAB1tO47J+xiAAAAAAAAAAAAGD03Fis3mgAAAAAAAAAAAJxiDwAAAAAAAAAAAAAAAABxqRCIGRxiAAAAAAAAAAAAEy8wZfK9YwAAAAAAAAAAAGZeZCE+g3sAAAAAAAAAAAAKYeQAAAAAAAAAAAAAAAAAlIvoyyc3mgAAAAAAAAAAAADQdQKjbgAAAAAAAAAAAAAAwu8g05H/////////////E6tNHAIAAAAAAAAAAAAAAO3mFwd0AAAAAAAAAAAAAAAAgPQg5rUAAAAAAAAAAAAAGkDtXR4AAAAAAAAAAAAAAEv0WeZW/f////////////9kUidaqAIAAAAAAAAAAAAA0ZMEr1H9/////////////w5/U3uqAgAAAAAAAAAAAAAANfbqfCd3AAAAAAAAAAAAIhABAAAAAAAiEAEAAAAAACIQAQAAAAAAY1QBAAAAAAA5f3WMVAAAAAAAAAAAAAAAFhkiihsAAAAAAAAAAAAAAO2EfWc5AAAAAAAAAAAAAACM/5CAQgAAAAAAAAAAAAAAvenX0SsAAAAAAAAAAAAAALgPUogZAAAAAAAAAAAAAAC01x97AAAAAAAAAAAAAAAAOXzVbgAAAAAAAAAAAAAAAMG4+QwBAAAAAAAAAAAAAABwHI3fLeJiAAAAAAAAAAAABvigOblGmgAAAAAAAAAAALeRnZsi9mIAAAAAAAAAAAAqgs3ynCeaAAAAAAAAAAAAQwhkAQAAAAAAAAAAAAAAAJOMZAEAAAAAFKJkAQAAAABTl2QBAAAAALFuZAEAAAAAgrx7DAAAAAAUAwAAAAAAAAN1TAYAAAAAuC7NZAAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAA4fUFAAAAAAAAAAAAAAAAn2HvyMABAADGV6rZFwAAAE5Qg2oPAAAA8zHNZAAAAAAdYAAAAAAAAE2FAAAAAAAA6zLNZAAAAAD6AAAAaEIAABQDAAAUAwAAAAAAANcBAABkADIAZGQAAcDIUt4AAAAA0QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI9qQbynsAAAAAAAAAAAAAAAAAAAAAAAAFNPTC1QRVJQICAgICAgICAgICAgICAgICAgICAgICAghuS1//////8A4fUFAAAAAAB0O6QLAAAAR7PdeQMAAAD+Mc1kAAAAAADKmjsAAAAAAAAAAAAAAAAAAAAAAAAAAOULDwAAAAAAUBkAAAAAAADtAQAAAAAAAMgAAAAAAAAAECcAAKhhAADoAwAA9AEAAAAAAAAQJwAAZAIAAGQCAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let mut perp_market = perp_market_loader.load_mut().unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498335213293 - ); - - let og_baawul = perp_market.amm.base_asset_amount_with_unsettled_lp; - let og_baapl = perp_market.amm.base_asset_amount_per_lp; - let og_qaapl = perp_market.amm.quote_asset_amount_per_lp; - - // update base - let base_change = 5; - apply_lp_rebase_to_perp_market(&mut perp_market, base_change).unwrap(); - - // noop delta - let delta = PositionDelta { - base_asset_amount: 0, - quote_asset_amount: 0, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, og_qaapl * 100000); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, og_baapl * 100000); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - og_baawul - ); - - // min long order for $2.3 - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64 / 10, - quote_asset_amount: -2300000, - remainder_base_asset_amount: None, - }; - - let u1 = - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - assert_eq!(u1, 96471070); - - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498431684363 - ); - - assert_eq!( - perp_market.amm.base_asset_amount_per_lp - og_baapl * 100000, - -287639 - ); - assert_eq!( - perp_market.amm.quote_asset_amount_per_lp - og_qaapl * 100000, - 6615 - ); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp - og_baawul, - 96471070 - ); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -57405475887639); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 1253565506615); - - let num = perp_market.amm.quote_asset_amount_per_lp - (og_qaapl * 100000); - let denom = perp_market.amm.base_asset_amount_per_lp - (og_baapl * 100000); - assert_eq!(-num * 1000000 / denom, 22997); // $22.997 cost basis for short (vs $23 actual) - - // min short order for $2.3 - let delta = PositionDelta { - base_asset_amount: -BASE_PRECISION_I64 / 10, - quote_asset_amount: 2300000, - remainder_base_asset_amount: None, - }; - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -57405475600000); - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 1253565499999); - assert_eq!( - (og_qaapl * 100000) - perp_market.amm.quote_asset_amount_per_lp, - 1 - ); - - let mut existing_position = PerpPosition { - market_index: 0, - base_asset_amount: 0, - quote_asset_amount: 0, - lp_shares: perp_market.amm.user_lp_shares as u64, - last_base_asset_amount_per_lp: og_baapl as i64, - last_quote_asset_amount_per_lp: og_qaapl as i64, - per_lp_base: 0, - ..PerpPosition::default() - }; - - settle_lp_position(&mut existing_position, &mut perp_market).unwrap(); - - assert_eq!(existing_position.base_asset_amount, 0); - assert_eq!(existing_position.remainder_base_asset_amount, 0); - assert_eq!(existing_position.quote_asset_amount, -335); // out of favor rounding... :/ - - assert_eq!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - assert_eq!( - perp_market - .amm - .imbalanced_base_asset_amount_with_lp() - .unwrap(), - -303686915482213 - ); - - assert_eq!(perp_market.amm.target_base_asset_amount_per_lp, -565000000); - - // update base back - let base_change = -2; - apply_lp_rebase_to_perp_market(&mut perp_market, base_change).unwrap(); - // noop delta - let delta = PositionDelta { - base_asset_amount: 0, - quote_asset_amount: 0, - remainder_base_asset_amount: None, - }; - - // unchanged with rebase (even when target!=0) - assert_eq!( - perp_market - .amm - .imbalanced_base_asset_amount_with_lp() - .unwrap(), - -303686915482213 - ); - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!( - perp_market.amm.quote_asset_amount_per_lp, - og_qaapl * 1000 - 1 - ); // down only rounding - assert_eq!(perp_market.amm.base_asset_amount_per_lp, og_baapl * 1000); - - // 1 long order for $23 before lp position does rebasing - let delta = PositionDelta { - base_asset_amount: BASE_PRECISION_I64, - quote_asset_amount: -23000000, - remainder_base_asset_amount: None, - }; - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054756000); - - update_lp_market_position(&mut perp_market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - let now = 110; - let clock_slot = 111; - let state = State::default(); - let oracle_price_data = OraclePriceData { - price: 23 * PRICE_PRECISION_I64, - confidence: PRICE_PRECISION_U64 / 100, - delay: 14, - has_sufficient_number_of_data_points: true, - }; - let mut mm_oracle_price = perp_market - .get_mm_oracle_price_data( - oracle_price_data, - clock_slot, - &state.oracle_guard_rails.validity, - ) - .unwrap(); - - let cost = _update_amm( - &mut perp_market, - &mut mm_oracle_price, - &state, - now, - clock_slot, - ) - .unwrap(); - assert_eq!(cost, -3017938); - - assert_eq!(perp_market.amm.quote_asset_amount_per_lp, 12535655660); - assert_eq!(perp_market.amm.base_asset_amount_per_lp, -574054784763); - assert_eq!( - existing_position.last_base_asset_amount_per_lp, - -57405475600000 - ); - assert_eq!(existing_position.per_lp_base, 5); - assert_ne!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - assert_eq!(perp_market.amm.base_asset_amount_long, 121646400000000); - assert_eq!(perp_market.amm.base_asset_amount_short, -121139000000000); - assert_eq!(perp_market.amm.base_asset_amount_with_amm, 8100106185); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 499299893815 - ); - let prev_with_unsettled_lp = perp_market.amm.base_asset_amount_with_unsettled_lp; - settle_lp_position(&mut existing_position, &mut perp_market).unwrap(); - - assert_eq!(perp_market.amm.base_asset_amount_long, 121646400000000); - assert_eq!(perp_market.amm.base_asset_amount_short, -121139900000000); - assert_eq!(perp_market.amm.base_asset_amount_with_amm, 8100106185); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498399893815 - ); - assert_eq!( - perp_market.amm.base_asset_amount_with_unsettled_lp, - 498399893815 - ); - assert!(perp_market.amm.base_asset_amount_with_unsettled_lp < prev_with_unsettled_lp); - - // 96.47% owned - assert_eq!(perp_market.amm.user_lp_shares, 33538939700000000); - assert_eq!(perp_market.amm.sqrt_k, 34765725006847590); - - assert_eq!(existing_position.per_lp_base, perp_market.amm.per_lp_base); - - assert_eq!(existing_position.base_asset_amount, -900000000); - assert_eq!(existing_position.remainder_base_asset_amount, -64680522); - assert_eq!(existing_position.quote_asset_amount, 22168904); // out of favor rounding... :/ - assert_eq!( - existing_position.last_base_asset_amount_per_lp, - perp_market.amm.base_asset_amount_per_lp as i64 - ); // out of favor rounding... :/ - assert_eq!( - existing_position.last_quote_asset_amount_per_lp, - perp_market.amm.quote_asset_amount_per_lp as i64 - ); // out of favor rounding... :/ -} - -#[test] -fn full_lp_split() { - let delta = PositionDelta { - base_asset_amount: 10 * BASE_PRECISION_I64, - quote_asset_amount: -10 * BASE_PRECISION_I64, - remainder_base_asset_amount: None, - }; - - let amm = AMM { - user_lp_shares: 100 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 10 * AMM_RESERVE_PRECISION_I128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - update_lp_market_position(&mut market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!( - market.amm.base_asset_amount_per_lp as i64, - -10 * BASE_PRECISION_I64 / 100 - ); - assert_eq!( - market.amm.quote_asset_amount_per_lp as i64, - 10 * BASE_PRECISION_I64 / 100 - ); - assert_eq!(market.amm.base_asset_amount_with_amm, 0); - assert_eq!( - market.amm.base_asset_amount_with_unsettled_lp, - 10 * AMM_RESERVE_PRECISION_I128 - ); -} - -#[test] -fn half_half_amm_lp_split() { - let delta = PositionDelta { - base_asset_amount: 10 * BASE_PRECISION_I64, - quote_asset_amount: -10 * BASE_PRECISION_I64, - remainder_base_asset_amount: None, - }; - - let amm = AMM { - user_lp_shares: 100 * AMM_RESERVE_PRECISION, - sqrt_k: 200 * AMM_RESERVE_PRECISION, - base_asset_amount_with_amm: 10 * AMM_RESERVE_PRECISION_I128, - ..AMM::default_test() - }; - let mut market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - update_lp_market_position(&mut market, &delta, 0, AMMLiquiditySplit::Shared).unwrap(); - - assert_eq!( - market.amm.base_asset_amount_with_amm, - 5 * AMM_RESERVE_PRECISION_I128 - ); - assert_eq!( - market.amm.base_asset_amount_with_unsettled_lp, - 5 * AMM_RESERVE_PRECISION_I128 - ); -} - #[test] fn test_position_entry_sim() { let mut existing_position: PerpPosition = PerpPosition::default(); let position_delta = PositionDelta { base_asset_amount: BASE_PRECISION_I64 / 2, quote_asset_amount: -99_345_000 / 2, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -1894,7 +1303,6 @@ fn test_position_entry_sim() { let position_delta_to_reduce = PositionDelta { base_asset_amount: -BASE_PRECISION_I64 / 5, quote_asset_amount: 99_245_000 / 5, - remainder_base_asset_amount: None, }; let pnl = update_position_and_market( @@ -1912,7 +1320,6 @@ fn test_position_entry_sim() { let position_delta_to_flip = PositionDelta { base_asset_amount: -BASE_PRECISION_I64, quote_asset_amount: 99_345_000, - remainder_base_asset_amount: None, }; let pnl = @@ -1931,7 +1338,6 @@ fn increase_long_from_no_position() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -1971,7 +1377,6 @@ fn increase_short_from_no_position() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2015,7 +1420,6 @@ fn increase_long() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2067,7 +1471,6 @@ fn increase_short() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 1, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2116,7 +1519,6 @@ fn reduce_long_profitable() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2167,7 +1569,6 @@ fn reduce_long_unprofitable() { let position_delta = PositionDelta { base_asset_amount: -1, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2218,7 +1619,6 @@ fn flip_long_to_short_profitable() { let position_delta = PositionDelta { base_asset_amount: -11, quote_asset_amount: 22, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2270,7 +1670,6 @@ fn flip_long_to_short_unprofitable() { let position_delta = PositionDelta { base_asset_amount: -11, quote_asset_amount: 10, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2323,7 +1722,6 @@ fn reduce_short_profitable() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2372,7 +1770,6 @@ fn decrease_short_unprofitable() { let position_delta = PositionDelta { base_asset_amount: 1, quote_asset_amount: -15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2421,7 +1818,6 @@ fn flip_short_to_long_profitable() { let position_delta = PositionDelta { base_asset_amount: 11, quote_asset_amount: -60, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2473,7 +1869,6 @@ fn flip_short_to_long_unprofitable() { let position_delta = PositionDelta { base_asset_amount: 11, quote_asset_amount: -120, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2525,7 +1920,6 @@ fn close_long_profitable() { let position_delta = PositionDelta { base_asset_amount: -10, quote_asset_amount: 15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2576,7 +1970,6 @@ fn close_long_unprofitable() { let position_delta = PositionDelta { base_asset_amount: -10, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2626,7 +2019,6 @@ fn close_short_profitable() { let position_delta = PositionDelta { base_asset_amount: 10, quote_asset_amount: -5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2674,7 +2066,6 @@ fn close_short_unprofitable() { let position_delta = PositionDelta { base_asset_amount: 10, quote_asset_amount: -15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2722,7 +2113,6 @@ fn close_long_with_quote_break_even_amount_less_than_quote_asset_amount() { let position_delta = PositionDelta { base_asset_amount: -10, quote_asset_amount: 5, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2773,7 +2163,6 @@ fn close_short_with_quote_break_even_amount_more_than_quote_asset_amount() { let position_delta = PositionDelta { base_asset_amount: 10, quote_asset_amount: -15, - remainder_base_asset_amount: None, }; let mut market = PerpMarket { amm: AMM { @@ -2842,7 +2231,15 @@ fn update_amm_near_boundary() { OracleMap::load_one(&jto_market_account_info, slot, None).unwrap(); let mut perp_market = perp_market_loader.load_mut().unwrap(); + assert_eq!(perp_market.amm.base_asset_amount_with_amm, 23831444927173); + assert_eq!( + perp_market.amm.base_asset_amount_with_unsettled_lp, + 562555072827 + ); + perp_market.amm.base_asset_amount_with_amm += + perp_market.amm.base_asset_amount_with_unsettled_lp; + perp_market.amm.base_asset_amount_with_unsettled_lp = 0; println!("perp_market: {:?}", perp_market.amm.last_update_slot); let oracle_price_data = oracle_map.get_price_data(&perp_market.oracle_id()).unwrap(); @@ -2851,10 +2248,13 @@ fn update_amm_near_boundary() { .unwrap(); let state = State::default(); + // perp_market.amm.sqrt_k -= perp_market.amm.user_lp_shares; + // perp_market.amm.user_lp_shares = 0; let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); - - assert_eq!(cost, 18803837952); + // assert_eq!(perp_market.amm.sqrt_k, 3295995551718929); + // assert_eq!(perp_market.amm.user_lp_shares, 267371000000000); + assert_eq!(cost, 18803544753); } #[test] @@ -2901,7 +2301,7 @@ fn update_amm_near_boundary2() { let cost: i128 = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); assert!(perp_market.amm.last_oracle_valid); - assert_eq!(cost, 2987010); + assert_eq!(cost, 2538958); } #[test] @@ -2948,7 +2348,7 @@ fn recenter_amm_1() { let cost = _update_amm(&mut perp_market, &mm_oracle_price_data, &state, now, slot).unwrap(); - assert_eq!(cost, 2987010); + assert_eq!(cost, 2538958); let inv = perp_market.amm.base_asset_amount_with_amm; assert_eq!(inv, 24521505718700); @@ -2960,8 +2360,8 @@ fn recenter_amm_1() { ) .unwrap(); - assert_eq!(r1_orig, 334835721519); - assert_eq!(r2_orig, 704842149); + assert_eq!(r1_orig, 334835274409); + assert_eq!(r2_orig, 704841208); let current_k = perp_market.amm.sqrt_k; let _current_peg = perp_market.amm.peg_multiplier; @@ -3077,7 +2477,14 @@ fn recenter_amm_2() { // refusal to decrease further assert_eq!(current_k, current_k); assert_eq!(perp_market.amm.user_lp_shares, current_k - 1); - assert_eq!(perp_market.amm.get_lower_bound_sqrt_k().unwrap(), current_k); + assert_eq!( + perp_market.amm.get_lower_bound_sqrt_k().unwrap(), + perp_market.amm.min_order_size as u128 + ); + + perp_market.amm.base_asset_amount_with_amm += + perp_market.amm.base_asset_amount_with_unsettled_lp; + perp_market.amm.base_asset_amount_with_unsettled_lp = 0; recenter_perp_market_amm(&mut perp_market, oracle_price_data.price as u128, new_k).unwrap(); @@ -3089,8 +2496,8 @@ fn recenter_amm_2() { assert_eq!(perp_market.amm.peg_multiplier, 1_120_000); // assert_eq!(perp_market.amm.quote_asset_reserve, 140625455708483789 * 2); // assert_eq!(perp_market.amm.base_asset_reserve, 140625456291516213 * 2); - assert_eq!(perp_market.amm.base_asset_reserve, 281250912291516214); - assert_eq!(perp_market.amm.quote_asset_reserve, 281250911708483790); + assert_eq!(perp_market.amm.base_asset_reserve, 281254004000000002); + assert_eq!(perp_market.amm.quote_asset_reserve, 281247820033992278); crate::validation::perp_market::validate_perp_market(&perp_market).unwrap(); @@ -3102,24 +2509,22 @@ fn recenter_amm_2() { .unwrap(); // adjusted slightly - assert_eq!(r1, 348628); // 354919762322 w/o k adj + assert_eq!(r1, 348620); // 354919762322 w/o k adj assert_eq!(r2, 22129); let new_scale = 2; let new_sqrt_k = perp_market.amm.sqrt_k * new_scale; let update_k_result = get_update_k_result(&perp_market, U192::from(new_sqrt_k), false).unwrap(); let adjustment_cost = adjust_k_cost(&mut perp_market, &update_k_result).unwrap(); - assert_eq!(adjustment_cost, 0); + assert_eq!(adjustment_cost, 19035); update_k(&mut perp_market, &update_k_result).unwrap(); - // higher lower bound now assert_eq!(perp_market.amm.sqrt_k, new_sqrt_k); assert_eq!(perp_market.amm.user_lp_shares, current_k - 1); - assert!(perp_market.amm.get_lower_bound_sqrt_k().unwrap() > current_k); assert_eq!( perp_market.amm.get_lower_bound_sqrt_k().unwrap(), - 140766081456000000 + 3092000000000 ); // assert_eq!(perp_market.amm.peg_multiplier, current_peg); } @@ -3165,7 +2570,7 @@ fn test_move_amm() { // let perp_market_old = market_map.get_ref(&4).unwrap(); let mut perp_market = market_map.get_ref_mut(&9).unwrap(); - + perp_market.amm.base_asset_amount_with_amm = -3092 * BASE_PRECISION as i128; println!( "perp_market latest slot: {:?}", perp_market.amm.last_update_slot @@ -3194,7 +2599,7 @@ fn test_move_amm() { assert_eq!(cost, 0); let inv = perp_market.amm.base_asset_amount_with_amm; - assert_eq!(inv, -291516212); + assert_eq!(inv, -3092000000000); let (_, _, r1_orig, r2_orig) = calculate_base_swap_output_with_spread( &perp_market.amm, @@ -3203,8 +2608,8 @@ fn test_move_amm() { ) .unwrap(); - assert_eq!(r1_orig, 326219); - assert_eq!(r2_orig, 20707); + assert_eq!(r1_orig, 3489128798); + assert_eq!(r2_orig, 215737299); let current_bar = perp_market.amm.base_asset_reserve; let _current_qar = perp_market.amm.quote_asset_reserve; let current_k = perp_market.amm.sqrt_k; diff --git a/programs/drift/src/controller/repeg.rs b/programs/drift/src/controller/repeg.rs index aad0a5db5c..c8dce61000 100644 --- a/programs/drift/src/controller/repeg.rs +++ b/programs/drift/src/controller/repeg.rs @@ -348,7 +348,7 @@ pub fn settle_expired_market( )?; validate!( - market.amm.base_asset_amount_with_unsettled_lp == 0 && market.amm.user_lp_shares == 0, + market.amm.base_asset_amount_with_unsettled_lp == 0, ErrorCode::MarketSettlementRequiresSettledLP, "Outstanding LP in market" )?; diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index a019e58a57..a4ecc2f8d1 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -2351,14 +2351,6 @@ pub fn handle_update_k(ctx: Context, sqrt_k: u128) -> Result<()> { perp_market.amm.sqrt_k )?; - validate!( - perp_market.amm.sqrt_k > perp_market.amm.user_lp_shares, - ErrorCode::InvalidUpdateK, - "cannot decrease sqrt_k={} below user_lp_shares={}", - perp_market.amm.sqrt_k, - perp_market.amm.user_lp_shares - )?; - perp_market.amm.total_fee_minus_distributions = perp_market .amm .total_fee_minus_distributions @@ -3432,59 +3424,6 @@ pub fn handle_update_perp_market_curve_update_intensity( Ok(()) } -#[access_control( - perp_market_valid(&ctx.accounts.perp_market) -)] -pub fn handle_update_perp_market_target_base_asset_amount_per_lp( - ctx: Context, - target_base_asset_amount_per_lp: i32, -) -> Result<()> { - let perp_market = &mut load_mut!(ctx.accounts.perp_market)?; - msg!("perp market {}", perp_market.market_index); - - msg!( - "perp_market.amm.target_base_asset_amount_per_lp: {} -> {}", - perp_market.amm.target_base_asset_amount_per_lp, - target_base_asset_amount_per_lp - ); - - perp_market.amm.target_base_asset_amount_per_lp = target_base_asset_amount_per_lp; - Ok(()) -} - -pub fn handle_update_perp_market_per_lp_base( - ctx: Context, - per_lp_base: i8, -) -> Result<()> { - let perp_market = &mut load_mut!(ctx.accounts.perp_market)?; - msg!("perp market {}", perp_market.market_index); - - let old_per_lp_base = perp_market.amm.per_lp_base; - msg!( - "updated perp_market per_lp_base {} -> {}", - old_per_lp_base, - per_lp_base - ); - - let expo_diff = per_lp_base.safe_sub(old_per_lp_base)?; - - validate!( - expo_diff.abs() == 1, - ErrorCode::DefaultError, - "invalid expo update (must be 1)", - )?; - - validate!( - per_lp_base.abs() <= 9, - ErrorCode::DefaultError, - "only consider lp_base within range of AMM_RESERVE_PRECISION", - )?; - - controller::lp::apply_lp_rebase_to_perp_market(perp_market, expo_diff)?; - - Ok(()) -} - pub fn handle_update_lp_cooldown_time( ctx: Context, lp_cooldown_time: u64, diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index c34b8dda82..1d29f0d46c 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -1073,37 +1073,6 @@ pub fn handle_settle_funding_payment<'c: 'info, 'info>( Ok(()) } -#[access_control( - amm_not_paused(&ctx.accounts.state) -)] -pub fn handle_settle_lp<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, SettleLP>, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - let AccountMaps { - perp_market_map, .. - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - let market = &mut perp_market_map.get_ref_mut(&market_index)?; - controller::lp::settle_funding_payment_then_lp(user, &user_key, market, now)?; - user.update_last_active_slot(clock.slot); - - Ok(()) -} - #[access_control( liq_not_paused(&ctx.accounts.state) )] diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 3f11a30c10..86229bc1bd 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -11,6 +11,7 @@ use anchor_spl::{ use solana_program::program::invoke; use solana_program::system_instruction::transfer; +use crate::controller::funding::settle_funding_payment; use crate::controller::orders::{cancel_orders, ModifyOrderId}; use crate::controller::position::update_position_and_market; use crate::controller::position::PositionDirection; @@ -1611,14 +1612,14 @@ pub fn handle_transfer_perp_position<'c: 'info, 'info>( &clock, )?; - controller::lp::settle_funding_payment_then_lp( + settle_funding_payment( &mut from_user, &from_user_key, perp_market_map.get_ref_mut(&market_index)?.deref_mut(), now, )?; - controller::lp::settle_funding_payment_then_lp( + settle_funding_payment( &mut to_user, &to_user_key, perp_market_map.get_ref_mut(&market_index)?.deref_mut(), @@ -2924,211 +2925,6 @@ pub fn handle_place_and_make_spot_order<'c: 'info, 'info>( Ok(()) } -#[access_control( - amm_not_paused(&ctx.accounts.state) -)] -pub fn handle_add_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - n_shares: u64, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - #[cfg(not(feature = "anchor-test"))] - { - msg!("add_perp_lp_shares is disabled"); - return Err(ErrorCode::DefaultError.into()); - } - - let AccountMaps { - perp_market_map, - spot_market_map, - mut oracle_map, - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - validate!(!user.is_bankrupt(), ErrorCode::UserBankrupt)?; - math::liquidation::validate_user_not_being_liquidated( - user, - &perp_market_map, - &spot_market_map, - &mut oracle_map, - state.liquidation_margin_buffer_ratio, - )?; - - { - let mut market = perp_market_map.get_ref_mut(&market_index)?; - - validate!( - matches!(market.status, MarketStatus::Active), - ErrorCode::MarketStatusInvalidForNewLP, - "Market Status doesn't allow for new LP liquidity" - )?; - - validate!( - !matches!(market.contract_type, ContractType::Prediction), - ErrorCode::MarketStatusInvalidForNewLP, - "Contract Type doesn't allow for LP liquidity" - )?; - - validate!( - !market.is_operation_paused(PerpOperation::AmmFill), - ErrorCode::MarketStatusInvalidForNewLP, - "Market amm fills paused" - )?; - - validate!( - n_shares >= market.amm.order_step_size, - ErrorCode::NewLPSizeTooSmall, - "minting {} shares is less than step size {}", - n_shares, - market.amm.order_step_size, - )?; - - controller::funding::settle_funding_payment(user, &user_key, &mut market, now)?; - - // standardize n shares to mint - let n_shares = crate::math::orders::standardize_base_asset_amount( - n_shares.cast()?, - market.amm.order_step_size, - )? - .cast::()?; - - controller::lp::mint_lp_shares( - user.force_get_perp_position_mut(market_index)?, - &mut market, - n_shares, - )?; - - user.last_add_perp_lp_shares_ts = now; - } - - // check margin requirements - meets_place_order_margin_requirement( - user, - &perp_market_map, - &spot_market_map, - &mut oracle_map, - true, - )?; - - user.update_last_active_slot(clock.slot); - - emit!(LPRecord { - ts: now, - action: LPAction::AddLiquidity, - user: user_key, - n_shares, - market_index, - ..LPRecord::default() - }); - - Ok(()) -} - -pub fn handle_remove_perp_lp_shares_in_expiring_market<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, RemoveLiquidityInExpiredMarket<'info>>, - shares_to_burn: u64, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - let AccountMaps { - perp_market_map, - mut oracle_map, - .. - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - // additional validate - { - let signer_is_admin = ctx.accounts.signer.key() == admin_hot_wallet::id(); - let market = perp_market_map.get_ref(&market_index)?; - validate!( - market.is_reduce_only()? || signer_is_admin, - ErrorCode::PerpMarketNotInReduceOnly, - "Can only permissionless burn when market is in reduce only" - )?; - } - - controller::lp::remove_perp_lp_shares( - perp_market_map, - &mut oracle_map, - state, - user, - user_key, - shares_to_burn, - market_index, - now, - )?; - - user.update_last_active_slot(clock.slot); - - Ok(()) -} - -#[access_control( - amm_not_paused(&ctx.accounts.state) -)] -pub fn handle_remove_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - shares_to_burn: u64, - market_index: u16, -) -> Result<()> { - let user_key = ctx.accounts.user.key(); - let user = &mut load_mut!(ctx.accounts.user)?; - - let state = &ctx.accounts.state; - let clock = Clock::get()?; - let now = clock.unix_timestamp; - - let AccountMaps { - perp_market_map, - mut oracle_map, - .. - } = load_maps( - &mut ctx.remaining_accounts.iter().peekable(), - &get_writable_perp_market_set(market_index), - &MarketSet::new(), - clock.slot, - Some(state.oracle_guard_rails), - )?; - - controller::lp::remove_perp_lp_shares( - perp_market_map, - &mut oracle_map, - state, - user, - user_key, - shares_to_burn, - market_index, - now, - )?; - - user.update_last_active_slot(clock.slot); - - Ok(()) -} - pub fn handle_update_user_name( ctx: Context, _sub_account_id: u16, @@ -4622,25 +4418,6 @@ pub struct PlaceAndMatchRFQOrders<'info> { pub ix_sysvar: AccountInfo<'info>, } -#[derive(Accounts)] -pub struct AddRemoveLiquidity<'info> { - pub state: Box>, - #[account( - mut, - constraint = can_sign_for_user(&user, &authority)?, - )] - pub user: AccountLoader<'info, User>, - pub authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct RemoveLiquidityInExpiredMarket<'info> { - pub state: Box>, - #[account(mut)] - pub user: AccountLoader<'info, User>, - pub signer: Signer<'info>, -} - #[derive(Accounts)] #[instruction( sub_account_id: u16, diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 92229f7e6a..9a14a80e59 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -351,30 +351,6 @@ pub mod drift { ) } - pub fn add_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - n_shares: u64, - market_index: u16, - ) -> Result<()> { - handle_add_perp_lp_shares(ctx, n_shares, market_index) - } - - pub fn remove_perp_lp_shares<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, AddRemoveLiquidity<'info>>, - shares_to_burn: u64, - market_index: u16, - ) -> Result<()> { - handle_remove_perp_lp_shares(ctx, shares_to_burn, market_index) - } - - pub fn remove_perp_lp_shares_in_expiring_market<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, RemoveLiquidityInExpiredMarket<'info>>, - shares_to_burn: u64, - market_index: u16, - ) -> Result<()> { - handle_remove_perp_lp_shares_in_expiring_market(ctx, shares_to_burn, market_index) - } - pub fn update_user_name( ctx: Context, _sub_account_id: u16, @@ -567,13 +543,6 @@ pub mod drift { handle_settle_funding_payment(ctx) } - pub fn settle_lp<'c: 'info, 'info>( - ctx: Context<'_, '_, 'c, 'info, SettleLP>, - market_index: u16, - ) -> Result<()> { - handle_settle_lp(ctx, market_index) - } - pub fn settle_expired_market<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, AdminUpdatePerpMarket<'info>>, market_index: u16, @@ -1397,23 +1366,6 @@ pub mod drift { handle_update_perp_market_curve_update_intensity(ctx, curve_update_intensity) } - pub fn update_perp_market_target_base_asset_amount_per_lp( - ctx: Context, - target_base_asset_amount_per_lp: i32, - ) -> Result<()> { - handle_update_perp_market_target_base_asset_amount_per_lp( - ctx, - target_base_asset_amount_per_lp, - ) - } - - pub fn update_perp_market_per_lp_base( - ctx: Context, - per_lp_base: i8, - ) -> Result<()> { - handle_update_perp_market_per_lp_base(ctx, per_lp_base) - } - pub fn update_lp_cooldown_time( ctx: Context, lp_cooldown_time: u64, diff --git a/programs/drift/src/math/amm_jit.rs b/programs/drift/src/math/amm_jit.rs index 17179b24b3..2d2c22c0fd 100644 --- a/programs/drift/src/math/amm_jit.rs +++ b/programs/drift/src/math/amm_jit.rs @@ -4,7 +4,7 @@ use crate::math::casting::Cast; use crate::math::constants::{AMM_RESERVE_PRECISION, PERCENTAGE_PRECISION_U64}; use crate::math::orders::standardize_base_asset_amount; use crate::math::safe_math::SafeMath; -use crate::state::perp_market::{AMMLiquiditySplit, PerpMarket}; +use crate::state::perp_market::PerpMarket; #[cfg(test)] mod tests; @@ -16,7 +16,6 @@ pub fn calculate_jit_base_asset_amount( auction_price: u64, valid_oracle_price: Option, taker_direction: PositionDirection, - liquidity_split: AMMLiquiditySplit, ) -> DriftResult { // AMM can only take up to 50% of size the maker is offering let mut max_jit_amount = maker_base_asset_amount.safe_div(2)?; @@ -105,8 +104,7 @@ pub fn calculate_jit_base_asset_amount( return Ok(0); } - jit_base_asset_amount = - calculate_clamped_jit_base_asset_amount(market, liquidity_split, jit_base_asset_amount)?; + jit_base_asset_amount = calculate_clamped_jit_base_asset_amount(market, jit_base_asset_amount)?; jit_base_asset_amount = jit_base_asset_amount.min(max_jit_amount); @@ -121,7 +119,6 @@ pub fn calculate_jit_base_asset_amount( // note: we split it into two (calc and clamp) bc its easier to maintain tests pub fn calculate_clamped_jit_base_asset_amount( market: &PerpMarket, - liquidity_split: AMMLiquiditySplit, jit_base_asset_amount: u64, ) -> DriftResult { // apply intensity @@ -133,19 +130,11 @@ pub fn calculate_clamped_jit_base_asset_amount( .cast::()?; // bound it; dont flip the net_baa - let max_amm_base_asset_amount = if liquidity_split != AMMLiquiditySplit::LPOwned { - market - .amm - .base_asset_amount_with_amm - .unsigned_abs() - .cast::()? - } else { - market - .amm - .imbalanced_base_asset_amount_with_lp()? - .unsigned_abs() - .cast::()? - }; + let max_amm_base_asset_amount = market + .amm + .base_asset_amount_with_amm + .unsigned_abs() + .cast::()?; let jit_base_asset_amount = jit_base_asset_amount.min(max_amm_base_asset_amount); @@ -161,11 +150,8 @@ pub fn calculate_amm_jit_liquidity( taker_base_asset_amount: u64, maker_base_asset_amount: u64, taker_has_limit_price: bool, - amm_lp_allowed_to_jit_make: Option, -) -> DriftResult<(u64, AMMLiquiditySplit)> { +) -> DriftResult { let mut jit_base_asset_amount: u64 = 0; - let mut liquidity_split: AMMLiquiditySplit = AMMLiquiditySplit::ProtocolOwned; - // taker has_limit_price = false means (limit price = 0 AND auction is complete) so // market order will always land and fill on amm next round let amm_will_fill_next_round: bool = @@ -173,46 +159,19 @@ pub fn calculate_amm_jit_liquidity( // return early if amm_will_fill_next_round { - return Ok((jit_base_asset_amount, liquidity_split)); + return Ok(jit_base_asset_amount); } let amm_wants_to_jit_make = market.amm.amm_wants_to_jit_make(taker_direction)?; - let amm_lp_wants_to_jit_make = market.amm.amm_lp_wants_to_jit_make(taker_direction)?; - let amm_lp_allowed_to_jit_make = match amm_lp_allowed_to_jit_make { - Some(allowed) => allowed, - None => market - .amm - .amm_lp_allowed_to_jit_make(amm_wants_to_jit_make)?, - }; - let split_with_lps = amm_lp_allowed_to_jit_make && amm_lp_wants_to_jit_make; - if amm_wants_to_jit_make { - liquidity_split = if split_with_lps { - AMMLiquiditySplit::Shared - } else { - AMMLiquiditySplit::ProtocolOwned - }; - jit_base_asset_amount = calculate_jit_base_asset_amount( market, base_asset_amount, maker_price, valid_oracle_price, taker_direction, - liquidity_split, - )?; - } else if split_with_lps { - liquidity_split = AMMLiquiditySplit::LPOwned; - - jit_base_asset_amount = calculate_jit_base_asset_amount( - market, - base_asset_amount, - maker_price, - valid_oracle_price, - taker_direction, - liquidity_split, )?; } - Ok((jit_base_asset_amount, liquidity_split)) + Ok(jit_base_asset_amount) } diff --git a/programs/drift/src/math/amm_jit/tests.rs b/programs/drift/src/math/amm_jit/tests.rs index adc9011f96..5e3488741d 100644 --- a/programs/drift/src/math/amm_jit/tests.rs +++ b/programs/drift/src/math/amm_jit/tests.rs @@ -13,12 +13,8 @@ fn balanced_market_zero_jit() { }; let jit_base_asset_amount = 100; - let jit_amount = calculate_clamped_jit_base_asset_amount( - &market, - AMMLiquiditySplit::ProtocolOwned, - jit_base_asset_amount, - ) - .unwrap(); + let jit_amount = + calculate_clamped_jit_base_asset_amount(&market, jit_base_asset_amount).unwrap(); assert_eq!(jit_amount, 0); } @@ -34,12 +30,8 @@ fn balanced_market_zero_intensity() { }; let jit_base_asset_amount = 100; - let jit_amount = calculate_clamped_jit_base_asset_amount( - &market, - AMMLiquiditySplit::ProtocolOwned, - jit_base_asset_amount, - ) - .unwrap(); + let jit_amount = + calculate_clamped_jit_base_asset_amount(&market, jit_base_asset_amount).unwrap(); assert_eq!(jit_amount, 0); } @@ -55,12 +47,8 @@ fn balanced_market_full_intensity() { }; let jit_base_asset_amount = 100; - let jit_amount = calculate_clamped_jit_base_asset_amount( - &market, - AMMLiquiditySplit::ProtocolOwned, - jit_base_asset_amount, - ) - .unwrap(); + let jit_amount = + calculate_clamped_jit_base_asset_amount(&market, jit_base_asset_amount).unwrap(); assert_eq!(jit_amount, 100); } @@ -76,11 +64,7 @@ fn balanced_market_half_intensity() { }; let jit_base_asset_amount = 100; - let jit_amount = calculate_clamped_jit_base_asset_amount( - &market, - AMMLiquiditySplit::ProtocolOwned, - jit_base_asset_amount, - ) - .unwrap(); + let jit_amount = + calculate_clamped_jit_base_asset_amount(&market, jit_base_asset_amount).unwrap(); assert_eq!(jit_amount, 50); } diff --git a/programs/drift/src/math/bankruptcy.rs b/programs/drift/src/math/bankruptcy.rs index 400c6504e8..287b103060 100644 --- a/programs/drift/src/math/bankruptcy.rs +++ b/programs/drift/src/math/bankruptcy.rs @@ -22,7 +22,6 @@ pub fn is_user_bankrupt(user: &User) -> bool { if perp_position.base_asset_amount != 0 || perp_position.quote_asset_amount > 0 || perp_position.has_open_order() - || perp_position.is_lp() { return false; } diff --git a/programs/drift/src/math/cp_curve/tests.rs b/programs/drift/src/math/cp_curve/tests.rs index 9448a6cd40..ca7de7e987 100644 --- a/programs/drift/src/math/cp_curve/tests.rs +++ b/programs/drift/src/math/cp_curve/tests.rs @@ -1,7 +1,4 @@ use crate::controller::amm::update_spreads; -use crate::controller::lp::burn_lp_shares; -use crate::controller::lp::mint_lp_shares; -use crate::controller::lp::settle_lp_position; use crate::controller::position::PositionDirection; use crate::math::amm::calculate_bid_ask_bounds; use crate::math::constants::BASE_PRECISION; @@ -338,7 +335,7 @@ fn amm_spread_adj_logic() { ..PerpPosition::default() }; - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); + // todo fix this market.amm.base_asset_amount_per_lp = 1; market.amm.quote_asset_amount_per_lp = -QUOTE_PRECISION_I64 as i128; @@ -369,173 +366,3 @@ fn amm_spread_adj_logic() { assert_eq!(market.amm.long_spread, 110); assert_eq!(market.amm.short_spread, 110); } - -#[test] -fn calculate_k_with_lps_tests() { - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 100 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, - terminal_quote_asset_reserve: 999900009999000 * AMM_RESERVE_PRECISION, - sqrt_k: 100 * AMM_RESERVE_PRECISION, - peg_multiplier: 50_000_000_000, - base_asset_amount_with_amm: (AMM_RESERVE_PRECISION / 10) as i128, - base_asset_amount_long: (AMM_RESERVE_PRECISION / 10) as i128, - order_step_size: 5, - max_spread: 1000, - ..AMM::default_test() - }, - margin_ratio_initial: 1000, - ..PerpMarket::default() - }; - // let (t_price, _t_qar, _t_bar) = calculate_terminal_price_and_reserves(&market.amm).unwrap(); - // market.amm.terminal_quote_asset_reserve = _t_qar; - - let mut position = PerpPosition { - ..PerpPosition::default() - }; - - mint_lp_shares(&mut position, &mut market, BASE_PRECISION_U64).unwrap(); - - market.amm.base_asset_amount_per_lp = 1; - market.amm.quote_asset_amount_per_lp = -QUOTE_PRECISION_I64 as i128; - - let reserve_price = market.amm.reserve_price().unwrap(); - update_spreads(&mut market, reserve_price, None).unwrap(); - - settle_lp_position(&mut position, &mut market).unwrap(); - - assert_eq!(position.base_asset_amount, 0); - assert_eq!(position.quote_asset_amount, -QUOTE_PRECISION_I64); - assert_eq!(position.last_base_asset_amount_per_lp, 1); - assert_eq!( - position.last_quote_asset_amount_per_lp, - -QUOTE_PRECISION_I64 - ); - - // increase k by 1% - let update_k_up = - get_update_k_result(&market, bn::U192::from(102 * AMM_RESERVE_PRECISION), false).unwrap(); - let (t_price, _t_qar, _t_bar) = - amm::calculate_terminal_price_and_reserves(&market.amm).unwrap(); - - // new terminal reserves are balanced, terminal price = peg) - // assert_eq!(t_qar, 999900009999000); - // assert_eq!(t_bar, 1000100000000000); - assert_eq!(t_price, 49901136949); // - // assert_eq!(update_k_up.sqrt_k, 101 * AMM_RESERVE_PRECISION); - - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - ); - assert_eq!(cost, 49400); //0.05 - - // lp whale adds - let lp_whale_amount = 1000 * BASE_PRECISION_U64; - mint_lp_shares(&mut position, &mut market, lp_whale_amount).unwrap(); - - // ensure same cost - let update_k_up = - get_update_k_result(&market, bn::U192::from(1102 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - ); - assert_eq!(cost, 49450); //0.05 - - let update_k_down = - get_update_k_result(&market, bn::U192::from(1001 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_down).unwrap(); - assert_eq!(cost, -4995004950); //amm rug - - // lp whale removes - burn_lp_shares(&mut position, &mut market, lp_whale_amount, 0).unwrap(); - - // ensure same cost - let update_k_up = - get_update_k_result(&market, bn::U192::from(102 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - 1 - ); - assert_eq!(cost, 49450); //0.05 - - let update_k_down = - get_update_k_result(&market, bn::U192::from(79 * AMM_RESERVE_PRECISION), false).unwrap(); - let cost = adjust_k_cost(&mut market, &update_k_down).unwrap(); - assert_eq!(cost, -1407000); //0.05 - - // lp owns 50% of vAMM, same k - position.lp_shares = 50 * BASE_PRECISION_U64; - market.amm.user_lp_shares = 50 * AMM_RESERVE_PRECISION; - // cost to increase k is always positive when imbalanced - let cost = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert_eq!( - market.amm.base_asset_amount_with_amm, - (AMM_RESERVE_PRECISION / 10) as i128 - 1 - ); - assert_eq!(cost, 187800); //0.19 - - // lp owns 99% of vAMM, same k - position.lp_shares = 99 * BASE_PRECISION_U64; - market.amm.user_lp_shares = 99 * AMM_RESERVE_PRECISION; - let cost2 = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert!(cost2 > cost); - assert_eq!(cost2, 76804900); //216.45 - - // lp owns 100% of vAMM, same k - position.lp_shares = 100 * BASE_PRECISION_U64; - market.amm.user_lp_shares = 100 * AMM_RESERVE_PRECISION; - let cost3 = adjust_k_cost(&mut market, &update_k_up).unwrap(); - assert!(cost3 > cost); - assert!(cost3 > cost2); - assert_eq!(cost3, 216450200); - - // // todo: support this - // market.amm.base_asset_amount_with_amm = -(AMM_RESERVE_PRECISION as i128); - // let cost2 = adjust_k_cost(&mut market, &update_k_up).unwrap(); - // assert!(cost2 > cost); - // assert_eq!(cost2, 249999999999850000000001); -} - -#[test] -fn calculate_bid_ask_per_lp_token() { - let (bound1_s, bound2_s) = - calculate_bid_ask_bounds(MAX_CONCENTRATION_COEFFICIENT, 24704615072091).unwrap(); - - assert_eq!(bound1_s, 17468968372288); - assert_eq!(bound2_s, 34937266634951); - - let (bound1, bound2) = calculate_bid_ask_bounds( - MAX_CONCENTRATION_COEFFICIENT, - 24704615072091 + BASE_PRECISION, - ) - .unwrap(); - - assert_eq!(bound1 - bound1_s, 707113563); - assert_eq!(bound2 - bound2_s, 1414200000); - - let more_conc = - CONCENTRATION_PRECISION + (MAX_CONCENTRATION_COEFFICIENT - CONCENTRATION_PRECISION) / 20; - - let (bound1_s, bound2_s) = calculate_bid_ask_bounds(more_conc, 24704615072091).unwrap(); - - assert_eq!(bound1_s, 24203363415750); - assert_eq!(bound2_s, 25216247650234); - - let (bound1, bound2) = - calculate_bid_ask_bounds(more_conc, 24704615072091 + BASE_PRECISION).unwrap(); - - assert_eq!(bound1 - bound1_s, 979710202); - assert_eq!(bound2 - bound2_s, 1020710000); - - let (bound1_3, bound2_3) = - calculate_bid_ask_bounds(more_conc, 24704615072091 + 2 * BASE_PRECISION).unwrap(); - - assert_eq!(bound1_3 - bound1_s, 979710202 * 2); - assert_eq!(bound2_3 - bound2_s, 1020710000 * 2); -} diff --git a/programs/drift/src/math/funding.rs b/programs/drift/src/math/funding.rs index 5fc9d78a3a..c723cb37bb 100644 --- a/programs/drift/src/math/funding.rs +++ b/programs/drift/src/math/funding.rs @@ -27,10 +27,7 @@ pub fn calculate_funding_rate_long_short( ) -> DriftResult<(i128, i128, i128)> { // Calculate the funding payment owed by the net_market_position if funding is not capped // If the net market position owes funding payment, the protocol receives payment - let settled_net_market_position = market - .amm - .base_asset_amount_with_amm - .safe_add(market.amm.base_asset_amount_with_unsettled_lp)?; + let settled_net_market_position = market.amm.base_asset_amount_with_amm; let net_market_position_funding_payment = calculate_funding_payment_in_quote_precision(funding_rate, settled_net_market_position)?; diff --git a/programs/drift/src/math/funding/tests.rs b/programs/drift/src/math/funding/tests.rs index 704355bd07..3fb3c37514 100644 --- a/programs/drift/src/math/funding/tests.rs +++ b/programs/drift/src/math/funding/tests.rs @@ -251,246 +251,6 @@ fn capped_sym_funding_test() { assert_eq!(new_fees, 1012295); // made over $.50 } -#[test] -fn funding_unsettled_lps_amm_win_test() { - // more shorts than longs, positive funding - - // positive base_asset_amount_with_unsettled_lp = - // 1) lots of long users who have lp as counterparty - // 2) the lps should be short but its unsettled... - // 3) amm takes on the funding revenu/cost of those short LPs - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: -12295081967, //~12 - base_asset_amount_long: 12295081967, - base_asset_amount_short: -12295081967 * 2, - base_asset_amount_with_unsettled_lp: (AMM_RESERVE_PRECISION * 500) as i128, //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: (QUOTE_PRECISION as i128) / 2, - - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - - assert_eq!(balanced_funding, 41666666); - assert_eq!(market.amm.total_fee_minus_distributions, 500000); - - let (long_funding, short_funding, _) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - let settled_net_market_position = market - .amm - .base_asset_amount_with_amm - .checked_add(market.amm.base_asset_amount_with_unsettled_lp) - .unwrap(); - - let net_market_position_funding_payment = - calculate_funding_payment_in_quote_precision(balanced_funding, settled_net_market_position) - .unwrap(); - let uncapped_funding_pnl = -net_market_position_funding_payment; - - assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967); - assert_eq!(market.amm.base_asset_amount_with_unsettled_lp, 500000000000); - assert_eq!(settled_net_market_position, 487704918033); - assert_eq!(net_market_position_funding_payment, -20321037); - assert_eq!(uncapped_funding_pnl, 20321037); //protocol revenue - - assert_eq!(long_funding, balanced_funding); - assert_eq!(short_funding, balanced_funding); - - assert!(long_funding == short_funding); - - // making money off unsettled lps - assert_eq!(market.amm.total_fee_minus_distributions, 20821037); - - // more longs than shorts, positive funding, amm earns funding - market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: 12295081967, - base_asset_amount_long: 12295081967 * 2, - base_asset_amount_short: -12295081967, - base_asset_amount_with_unsettled_lp: (AMM_RESERVE_PRECISION * 500) as i128, //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: (QUOTE_PRECISION as i128) / 2, - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - assert_eq!(balanced_funding, 41666666); - - let (long_funding, short_funding, drift_pnl) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - assert_eq!(drift_pnl, 21345628); - assert_eq!(long_funding, balanced_funding); - assert_eq!(long_funding, short_funding); - let new_fees = market.amm.total_fee_minus_distributions; - assert!(new_fees > QUOTE_PRECISION as i128 / 2); - assert_eq!(new_fees, 21845628); // made more -} - -#[test] -fn funding_unsettled_lps_amm_lose_test() { - // more shorts than longs, positive funding - - // positive base_asset_amount_with_unsettled_lp = - // 1) lots of long users who have lp as counterparty - // 2) the lps should be short but its unsettled... - // 3) amm takes on the funding revenu/cost of those short LPs - - let mut market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: -12295081967, //~12 - base_asset_amount_long: 12295081967, - base_asset_amount_short: -12295081967 * 2, - base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: ((QUOTE_PRECISION * 99999) as i128), - - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - - assert_eq!(balanced_funding, 41666666); - assert_eq!(market.amm.total_fee_minus_distributions, 99999000000); - - let (long_funding, short_funding, _) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - let settled_net_market_position = market - .amm - .base_asset_amount_with_amm - .checked_add(market.amm.base_asset_amount_with_unsettled_lp) - .unwrap(); - - let net_market_position_funding_payment = - calculate_funding_payment_in_quote_precision(balanced_funding, settled_net_market_position) - .unwrap(); - let uncapped_funding_pnl = -net_market_position_funding_payment; - - assert_eq!(market.amm.base_asset_amount_with_amm, -12295081967); - assert_eq!( - market.amm.base_asset_amount_with_unsettled_lp, - -500000000000 - ); - assert_eq!(settled_net_market_position, -512295081967); - assert_eq!(net_market_position_funding_payment, 21345628); - assert_eq!(uncapped_funding_pnl, -21345628); //protocol loses $21 - - assert_eq!(long_funding, balanced_funding); - assert_eq!(short_funding, balanced_funding); - - assert!(long_funding == short_funding); - - // making money off unsettled lps - assert_eq!(market.amm.total_fee_minus_distributions, 99977654372); - - // more longs than shorts, positive funding, amm earns funding - market = PerpMarket { - amm: AMM { - base_asset_reserve: 512295081967, - quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, - sqrt_k: 500 * AMM_RESERVE_PRECISION, - peg_multiplier: 50000000, - base_asset_amount_with_amm: 12295081967, - base_asset_amount_long: 12295081967 * 2, - base_asset_amount_short: -12295081967, - base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers - total_exchange_fee: QUOTE_PRECISION / 2, - total_fee_minus_distributions: (QUOTE_PRECISION as i128) / 2, - last_mark_price_twap: 50 * PRICE_PRECISION_U64, - historical_oracle_data: HistoricalOracleData { - last_oracle_price_twap: (49 * PRICE_PRECISION) as i64, - - ..HistoricalOracleData::default() - }, - funding_period: 3600, - - ..AMM::default() - }, - ..PerpMarket::default() - }; - - let balanced_funding = calculate_funding_rate( - market.amm.last_mark_price_twap as u128, - market.amm.historical_oracle_data.last_oracle_price_twap as i128, - market.amm.funding_period, - ) - .unwrap(); - assert_eq!(balanced_funding, 41666666); - - let (long_funding, short_funding, drift_pnl) = - calculate_funding_rate_long_short(&mut market, balanced_funding).unwrap(); - - assert_eq!(drift_pnl, -20321037); - assert_eq!(long_funding, balanced_funding); - assert_eq!(short_funding, 90110989); - assert_eq!(long_funding < short_funding, true); - - let new_fees = market.amm.total_fee_minus_distributions; - assert_eq!(new_fees, 416667); // lost -} - #[test] fn max_funding_rates() { let now = 0_i64; @@ -616,10 +376,9 @@ fn unsettled_funding_pnl() { quote_asset_reserve: 488 * AMM_RESERVE_PRECISION, sqrt_k: 500 * AMM_RESERVE_PRECISION, peg_multiplier: 50000000, - base_asset_amount_with_amm: -12295081967, //~12 + base_asset_amount_with_amm: -12295081967 + -((AMM_RESERVE_PRECISION * 500) as i128), //~ 12 - 500 base_asset_amount_long: 12295081967, base_asset_amount_short: -12295081967 * 2, - base_asset_amount_with_unsettled_lp: -((AMM_RESERVE_PRECISION * 500) as i128), //wowsers total_exchange_fee: QUOTE_PRECISION / 2, total_fee_minus_distributions: ((QUOTE_PRECISION * 99999) as i128), diff --git a/programs/drift/src/math/lp.rs b/programs/drift/src/math/lp.rs deleted file mode 100644 index a65ae33adb..0000000000 --- a/programs/drift/src/math/lp.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::error::{DriftResult, ErrorCode}; -use crate::msg; -use crate::{ - validate, MARGIN_PRECISION_U128, PRICE_PRECISION, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, -}; -use std::u64; - -use crate::math::amm::calculate_market_open_bids_asks; -use crate::math::casting::Cast; -use crate::math::helpers; -use crate::math::margin::MarginRequirementType; -use crate::math::orders::{ - standardize_base_asset_amount, standardize_base_asset_amount_ceil, - standardize_base_asset_amount_with_remainder_i128, -}; -use crate::math::safe_math::SafeMath; - -use crate::state::perp_market::PerpMarket; -use crate::state::perp_market::AMM; -use crate::state::user::PerpPosition; - -#[cfg(test)] -mod tests; - -#[derive(Debug)] -pub struct LPMetrics { - pub base_asset_amount: i128, - pub quote_asset_amount: i128, - pub remainder_base_asset_amount: i128, -} - -pub fn calculate_settle_lp_metrics(amm: &AMM, position: &PerpPosition) -> DriftResult { - let (base_asset_amount, quote_asset_amount) = calculate_settled_lp_base_quote(amm, position)?; - - // stepsize it - let (standardized_base_asset_amount, remainder_base_asset_amount) = - standardize_base_asset_amount_with_remainder_i128( - base_asset_amount, - amm.order_step_size.cast()?, - )?; - - let lp_metrics = LPMetrics { - base_asset_amount: standardized_base_asset_amount, - quote_asset_amount, - remainder_base_asset_amount: remainder_base_asset_amount.cast()?, - }; - - Ok(lp_metrics) -} - -pub fn calculate_settled_lp_base_quote( - amm: &AMM, - position: &PerpPosition, -) -> DriftResult<(i128, i128)> { - let n_shares = position.lp_shares; - let base_unit: i128 = amm.get_per_lp_base_unit()?; - - validate!( - amm.per_lp_base == position.per_lp_base, - ErrorCode::InvalidPerpPositionDetected, - "calculate_settled_lp_base_quote :: position/market per_lp_base unequal {} != {}", - position.per_lp_base, - amm.per_lp_base - )?; - - let n_shares_i128 = n_shares.cast::()?; - - // give them slice of the damm market position - let amm_net_base_asset_amount_per_lp = amm - .base_asset_amount_per_lp - .safe_sub(position.last_base_asset_amount_per_lp.cast()?)?; - - let base_asset_amount = amm_net_base_asset_amount_per_lp - .cast::()? - .safe_mul(n_shares_i128)? - .safe_div(base_unit)?; - - let amm_net_quote_asset_amount_per_lp = amm - .quote_asset_amount_per_lp - .safe_sub(position.last_quote_asset_amount_per_lp.cast()?)?; - - let quote_asset_amount = amm_net_quote_asset_amount_per_lp - .cast::()? - .safe_mul(n_shares_i128)? - .safe_div(base_unit)?; - - Ok((base_asset_amount, quote_asset_amount)) -} - -pub fn calculate_lp_open_bids_asks( - market_position: &PerpPosition, - market: &PerpMarket, -) -> DriftResult<(i64, i64)> { - let total_lp_shares = market.amm.sqrt_k; - let lp_shares = market_position.lp_shares; - - let (max_bids, max_asks) = calculate_market_open_bids_asks(&market.amm)?; - let open_asks = helpers::get_proportion_i128(max_asks, lp_shares.cast()?, total_lp_shares)?; - let open_bids = helpers::get_proportion_i128(max_bids, lp_shares.cast()?, total_lp_shares)?; - - Ok((open_bids.cast()?, open_asks.cast()?)) -} - -pub fn calculate_lp_shares_to_burn_for_risk_reduction( - perp_position: &PerpPosition, - market: &PerpMarket, - oracle_price: i64, - quote_oracle_price: i64, - margin_shortage: u128, - user_custom_margin_ratio: u32, - user_high_leverage_mode: bool, -) -> DriftResult<(u64, u64)> { - let settled_lp_position = perp_position.simulate_settled_lp_position(market, oracle_price)?; - - let worse_case_base_asset_amount = - settled_lp_position.worst_case_base_asset_amount(oracle_price, market.contract_type)?; - - let open_orders_from_lp_shares = if worse_case_base_asset_amount >= 0 { - worse_case_base_asset_amount.safe_sub( - settled_lp_position - .base_asset_amount - .safe_add(perp_position.open_bids)? - .cast()?, - )? - } else { - worse_case_base_asset_amount.safe_sub( - settled_lp_position - .base_asset_amount - .safe_add(perp_position.open_asks)? - .cast()?, - )? - }; - - let margin_ratio = market - .get_margin_ratio( - worse_case_base_asset_amount.unsigned_abs(), - MarginRequirementType::Initial, - user_high_leverage_mode, - )? - .max(user_custom_margin_ratio); - - let base_asset_amount_to_cover = margin_shortage - .safe_mul(PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO)? - .safe_div( - oracle_price - .cast::()? - .safe_mul(quote_oracle_price.cast()?)? - .safe_div(PRICE_PRECISION)? - .safe_mul(margin_ratio.cast()?)? - .safe_div(MARGIN_PRECISION_U128)?, - )? - .cast::()?; - - let current_base_asset_amount = settled_lp_position.base_asset_amount.unsigned_abs(); - - // if closing position is enough to cover margin shortage, then only a small % of lp shares need to be burned - if base_asset_amount_to_cover < current_base_asset_amount { - let base_asset_amount_to_close = standardize_base_asset_amount_ceil( - base_asset_amount_to_cover, - market.amm.order_step_size, - )? - .min(current_base_asset_amount); - let lp_shares_to_burn = standardize_base_asset_amount( - settled_lp_position.lp_shares / 10, - market.amm.order_step_size, - )? - .max(market.amm.order_step_size); - return Ok((lp_shares_to_burn, base_asset_amount_to_close)); - } - - let base_asset_amount_to_cover = - base_asset_amount_to_cover.safe_sub(current_base_asset_amount)?; - - let percent_to_burn = base_asset_amount_to_cover - .cast::()? - .safe_mul(100)? - .safe_div_ceil(open_orders_from_lp_shares.unsigned_abs())?; - - let lp_shares_to_burn = settled_lp_position - .lp_shares - .cast::()? - .safe_mul(percent_to_burn.cast()?)? - .safe_div_ceil(100)? - .cast::()?; - - let standardized_lp_shares_to_burn = - standardize_base_asset_amount_ceil(lp_shares_to_burn, market.amm.order_step_size)? - .clamp(market.amm.order_step_size, settled_lp_position.lp_shares); - - Ok((standardized_lp_shares_to_burn, current_base_asset_amount)) -} diff --git a/programs/drift/src/math/lp/tests.rs b/programs/drift/src/math/lp/tests.rs deleted file mode 100644 index c55253e0a5..0000000000 --- a/programs/drift/src/math/lp/tests.rs +++ /dev/null @@ -1,451 +0,0 @@ -use crate::math::constants::AMM_RESERVE_PRECISION; -use crate::math::lp::*; -use crate::state::user::PerpPosition; - -mod calculate_get_proportion_u128 { - use crate::math::helpers::get_proportion_u128; - - use super::*; - - pub fn get_proportion_u128_safe( - value: u128, - numerator: u128, - denominator: u128, - ) -> DriftResult { - if numerator == 0 { - return Ok(0); - } - - let proportional_value = if numerator <= denominator { - let ratio = denominator.safe_mul(10000)?.safe_div(numerator)?; - value.safe_mul(10000)?.safe_div(ratio)? - } else { - value.safe_mul(numerator)?.safe_div(denominator)? - }; - - Ok(proportional_value) - } - - #[test] - fn test_safe() { - let sqrt_k = AMM_RESERVE_PRECISION * 10_123; - let max_reserve = sqrt_k * 14121 / 10000; - let max_asks = max_reserve - sqrt_k; - - // let ans1 = get_proportion_u128_safe(max_asks, sqrt_k - sqrt_k / 100, sqrt_k).unwrap(); - // let ans2 = get_proportion_u128(max_asks, sqrt_k - sqrt_k / 100, sqrt_k).unwrap(); - // assert_eq!(ans1, ans2); //fails - - let ans1 = get_proportion_u128_safe(max_asks, sqrt_k / 2, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, sqrt_k / 2, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - let ans1 = get_proportion_u128_safe(max_asks, AMM_RESERVE_PRECISION, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, AMM_RESERVE_PRECISION, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - let ans1 = get_proportion_u128_safe(max_asks, 0, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, 0, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - let ans1 = get_proportion_u128_safe(max_asks, 1325324, sqrt_k).unwrap(); - let ans2 = get_proportion_u128(max_asks, 1325324, sqrt_k).unwrap(); - assert_eq!(ans1, ans2); - - // let ans1 = get_proportion_u128(max_asks, sqrt_k, sqrt_k).unwrap(); - // assert_eq!(ans1, max_asks); - } -} - -mod calculate_lp_open_bids_asks { - use super::*; - - #[test] - fn test_simple_lp_bid_ask() { - let position = PerpPosition { - lp_shares: 100, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_reserve: 10, - max_base_asset_reserve: 100, - min_base_asset_reserve: 0, - sqrt_k: 200, - ..AMM::default_test() - }; - let market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - let (open_bids, open_asks) = calculate_lp_open_bids_asks(&position, &market).unwrap(); - - assert_eq!(open_bids, 10 * 100 / 200); - assert_eq!(open_asks, -90 * 100 / 200); - } - - #[test] - fn test_max_ask() { - let position = PerpPosition { - lp_shares: 100, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_reserve: 0, - max_base_asset_reserve: 100, - min_base_asset_reserve: 0, - sqrt_k: 200, - ..AMM::default_test() - }; - let market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - let (open_bids, open_asks) = calculate_lp_open_bids_asks(&position, &market).unwrap(); - - assert_eq!(open_bids, 0); // wont go anymore short - assert_eq!(open_asks, -100 * 100 / 200); - } - - #[test] - fn test_max_bid() { - let position = PerpPosition { - lp_shares: 100, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_reserve: 10, - max_base_asset_reserve: 10, - min_base_asset_reserve: 0, - sqrt_k: 200, - ..AMM::default_test() - }; - let market = PerpMarket { - amm, - ..PerpMarket::default_test() - }; - - let (open_bids, open_asks) = calculate_lp_open_bids_asks(&position, &market).unwrap(); - - assert_eq!(open_bids, 10 * 100 / 200); - assert_eq!(open_asks, 0); // no more long - } -} - -mod calculate_settled_lp_base_quote { - use crate::math::constants::BASE_PRECISION_U64; - - use super::*; - - #[test] - fn test_long_settle() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - ..AMM::default_test() - }; - - let (baa, qaa) = calculate_settled_lp_base_quote(&amm, &position).unwrap(); - - assert_eq!(baa, 10 * 100); - assert_eq!(qaa, -10 * 100); - } - - #[test] - fn test_short_settle() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: -10, - quote_asset_amount_per_lp: 10, - ..AMM::default_test() - }; - - let (baa, qaa) = calculate_settled_lp_base_quote(&amm, &position).unwrap(); - - assert_eq!(baa, -10 * 100); - assert_eq!(qaa, 10 * 100); - } -} - -mod calculate_settle_lp_metrics { - use crate::math::constants::BASE_PRECISION_U64; - - use super::*; - - #[test] - fn test_long_settle() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - order_step_size: 1, - ..AMM::default_test() - }; - - let lp_metrics = calculate_settle_lp_metrics(&amm, &position).unwrap(); - - assert_eq!(lp_metrics.base_asset_amount, 10 * 100); - assert_eq!(lp_metrics.quote_asset_amount, -10 * 100); - assert_eq!(lp_metrics.remainder_base_asset_amount, 0); - } - - #[test] - fn test_all_remainder() { - let position = PerpPosition { - lp_shares: 100 * BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - order_step_size: 50 * 100, - ..AMM::default_test() - }; - - let lp_metrics = calculate_settle_lp_metrics(&amm, &position).unwrap(); - - assert_eq!(lp_metrics.base_asset_amount, 0); - assert_eq!(lp_metrics.quote_asset_amount, -10 * 100); - assert_eq!(lp_metrics.remainder_base_asset_amount, 10 * 100); - } - - #[test] - fn test_portion_remainder() { - let position = PerpPosition { - lp_shares: BASE_PRECISION_U64, - ..PerpPosition::default() - }; - - let amm = AMM { - base_asset_amount_per_lp: 10, - quote_asset_amount_per_lp: -10, - order_step_size: 3, - ..AMM::default_test() - }; - - let lp_metrics = calculate_settle_lp_metrics(&amm, &position).unwrap(); - - assert_eq!(lp_metrics.base_asset_amount, 9); - assert_eq!(lp_metrics.quote_asset_amount, -10); - assert_eq!(lp_metrics.remainder_base_asset_amount, 1); - } -} - -mod calculate_lp_shares_to_burn_for_risk_reduction { - use crate::math::lp::calculate_lp_shares_to_burn_for_risk_reduction; - use crate::state::perp_market::PerpMarket; - use crate::state::user::User; - use crate::test_utils::create_account_info; - use crate::{PRICE_PRECISION_I64, QUOTE_PRECISION}; - use anchor_lang::prelude::AccountLoader; - use solana_program::pubkey::Pubkey; - use std::str::FromStr; - - #[test] - fn test() { - let user_str = String::from("n3Vf4++XOuwuqzjlmLoHfrMxu0bx1zK4CI3jhlcn84aSUBauaSLU4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARHJpZnQgTGlxdWlkaXR5IFByb3ZpZGVyICAgICAgICAbACHcCQAAAAAAAAAAAAAAAAAAAAAAAACcpMgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2ssqwBAAAAAATnHP3///+9awAIAAAAAKnr8AcAAAAAqufxBwAAAAAAAAAAAAAAAAAAAAAAAAAAuITI//////8AeTlTJwAAANxGF1tu/P//abUakBEAAACBFNL6BAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+hUCAAAAAAAAAAAAAAAAACC8EHuk9f//1uYrCQMAAAAAAAAACQAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANv7p2UAAAAAnKTIAgAAAAAAAAAAAAAAAAAAAAAAAAAAsprK//////8AAAAAAAAAAPeGAgAAAAAAAAAAAAAAAAAzkaIOAAAAAA8AAACIEwAAAQACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - let mut decoded_bytes = base64::decode(user_str).unwrap(); - let user_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let user_account_info = create_account_info(&key, true, &mut lamports, user_bytes, &owner); - - let user_loader: AccountLoader = AccountLoader::try_from(&user_account_info).unwrap(); - let mut user = user_loader.load_mut().unwrap(); - let position = &mut user.perp_positions[0]; - - let perp_market_str = String::from("Ct8MLGv1N/cU6tVVkVpIHdjrXil5+Blo7M7no01SEzFkvCN2nSnel3KwISF8o/5okioZqvmQEJy52E6a0AS00gJa1vUpMUQZgG2jAAAAAAAAAAAAAAAAAAMAAAAAAAAAiKOiAAAAAAATRqMAAAAAAEr2u2UAAAAA3EYXW278/////////////2m1GpARAAAAAAAAAAAAAACRgrV0qi0BAAAAAAAAAAAAAAAAAAAAAABFREBhQ1YEAAAAAAAAAAAA9sh+SuuHBwAAAAAAAAAAACaTDwAAAAAAAAAAAAAAAADvHx32D0IEAAAAAAAAAAAA67nFJa5vBAAAAAAAAAAAAHMxOUELtwUAAAAAAAAAAACqHV4AAAAAAAAAAAAAAAAApw4iE86DBwAAAAAAAAAAAADzSoISXwAAAAAAAAAAAAAAHtBmbKP/////////////CreY1F8CAAAAAAAAAAAAAPZZghQfAAAAAAAAAAAAAAAAQGNSv8YBAAAAAAAAAAAAUdkndDAAAAAAAAAAAAAAAEEeAcSS/v/////////////0bAXnbQEAAAAAAAAAAAAAPuj0I3f+/////////////6felr+KAQAAAAAAAAAAAABX2/mMhMQCAAAAAAAAAAAALukbAAAAAAAu6RsAAAAAAC7pGwAAAAAAqPUJAAAAAADkPmeWogAAAAAAAAAAAAAAsD8vhpIAAAAAAAAAAAAAACibCEwQAAAAAAAAAAAAAAAr/d/xbQAAAAAAAAAAAAAAwY+XFgAAAAAAAAAAAAAAAMyF/KFFAAAAAAAAAAAAAAA9rLKsAQAAAAAAAAAAAAAAPayyrAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+6JzGf04EAAAAAAAAAAAAtqLk+X6VBwAAAAAAAAAAAPDUDdGDVwQAAAAAAAAAAABeb5d+v4UHAAAAAAAAAAAAgG2jAAAAAAAAAAAAAAAAACJ6ogAAAAAAE0qkAAAAAAAaYqMAAAAAAIF1pAAAAAAArJmiDgAAAAAlBwAAAAAAAN5ukP7/////veq7ZQAAAAAQDgAAAAAAAADh9QUAAAAAZAAAAAAAAAAAZc0dAAAAAAAAAAAAAAAAiuqcc0QAAAA8R6NuAQAAAIyqSgkAAAAAt+27ZQAAAAATCAEAAAAAAPjJAAAAAAAASva7ZQAAAACUEQAAoIYBALQ2AADKCAAASQEAAH0AAAD0ATIAZMgEAQAAAAAEAAAAfRuiDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhv4EJ8hQEAAAAAAAAAAAAAAAAAAAAAADFNQk9OSy1QRVJQICAgICAgICAgICAgICAgICAgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG8VAwAAAAAA+x4AAAAAAACFAwAAAAAAACYCAADuAgAAqGEAAFDDAADECQAA3AUAAAAAAAAQJwAABwQAAA0GAAAEAAEAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let perp_market = perp_market_loader.load_mut().unwrap(); - - let oracle_price = 10 * PRICE_PRECISION_I64; - let quote_oracle_price = PRICE_PRECISION_I64; - - let margin_shortage = 40 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 168900000000); - assert_eq!(base_asset_amount, 12400000000); - - let margin_shortage = 20 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 8000000000); - - let margin_shortage = 5 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 2000000000); - - // flip existing position the other direction - position.base_asset_amount = -position.base_asset_amount; - - let margin_shortage = 40 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 168900000000); - assert_eq!(base_asset_amount, 12400000000); - - let margin_shortage = 20 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 8000000000); - - let margin_shortage = 5 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - 0, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 16800000000); - assert_eq!(base_asset_amount, 2000000000); - } - - #[test] - fn custom_margin_ratio() { - let user_str = String::from("n3Vf4++XOuwIrD1jL22rz6RZlEfmZHqxneDBS0Mflxjd93h2f2ldQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdGl0c29jY2VyICAgICAgICAgICAgICAgICAgICAgICDnqurCZBgAAAAAAAAAAAAAAAAAAAAAAADOM8akAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgO0UfBAAAAPeaGv//////SM9HIAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyRaK3v////8AAAAAAAAAAPeaGv//////SM9HIAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGq0wmUAAAAATspepQEAAACAlpgAAAAAAAAAAAAAAAAAGn3imQQAAAAAAAAAAAAAACMV2Pf/////AAAAAAAAAAB7Ro0QAAAAACYAAAAQJwAAAQADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - let mut decoded_bytes = base64::decode(user_str).unwrap(); - let user_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let user_account_info = create_account_info(&key, true, &mut lamports, user_bytes, &owner); - - let user_loader: AccountLoader = AccountLoader::try_from(&user_account_info).unwrap(); - let mut user = user_loader.load_mut().unwrap(); - let user_custom_margin_ratio = user.max_margin_ratio; - let position = &mut user.perp_positions[0]; - - let perp_market_str = String::from("Ct8MLGv1N/cV6vWLwJY+18dY2GsrmrNldgnISB7pmbcf7cn9S4FZ4PnAFyuhDfpNGQiNlPW/YdO1TVvXSDoyKpguE3PujqMbEGDwDQoAAAAAAAAAAAAAAAIAAAAAAAAAC7rzDQoAAABst/MNCgAAACK5wmUAAAAAceZN/////////////////3v2pRcAAAAAAAAAAAAAAADlXWMfRwMAAAAAAAAAAAAAAAAAAAAAAABkI6UNRQAAAAAAAAAAAAAAqfLzd0UAAAAAAAAAAAAAACaTDwAAAAAAAAAAAAAAAAAPPu6ERAAAAAAAAAAAAAAA9NX/YkcAAAAAAAAAAAAAAI0luEJFAAAAAAAAAAAAAAD9eI3+CQAAAAAAAAAAAAAAIZTqlkQAAAAAAAAAAAAAAIBENlMCAAAAAAAAAAAAAADgfcyL/v//////////////meiO4gAAAAAAAAAAAAAAAMfZc/z///////////////8AoHJOGAkAAAAAAAAAAAAAnURpyQAAAAAAAAAAAAAAAOYK1g3F//////////////9I7emQMwAAAAAAAAAAAAAAKoCi9MT//////////////3cTS98zAAAAAAAAAAAAAAAgO0UfBAAAAAAAAAAAAAAAGV4SEgAAAAAZXhISAAAAABleEhIAAAAAA0itQgAAAAAhNkO9CQAAAAAAAAAAAAAAMTlM9gcAAAAAAAAAAAAAAGJujdoBAAAAAAAAAAAAAADvEtDaAgAAAAAAAAAAAAAAOLU20gIAAAAAAAAAAAAAALu3wLsCAAAAAAAAAAAAAABdkcJWBgUAAAAAAAAAAAAANhdFnLwEAAAAAAAAAAAAAEDj5IkCAAAAAAAAAAAAAACCV2gFRQAAAAAAAAAAAAAAPWo+gEUAAAAAAAAAAAAAAIIDPw5FAAAAAAAAAAAAAAAAJ1l3RQAAAAAAAAAAAAAAEGDwDQoAAAAAAAAAAAAAAE05yg0KAAAAUbrzDQoAAADP+d4NCgAAAC3a3g0KAAAAGVONEAAAAAACAQAAAAAAAGDUKbz/////G7nCZQAAAAAQDgAAAAAAAKCGAQAAAAAAoIYBAAAAAABAQg8AAAAAAAAAAAAAAAAA3+0VvzsAAAAAAAAAAAAAACbWIwUKAAAAIbnCZQAAAABNyBIAAAAAAPtZAwAAAAAAIbnCZQAAAAAKAAAA6AMAAKQDAABEAAAAAAAAAP0pAABkADIAZMgAAQDKmjsAAAAA1jQGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0ezvzkkgAAAAAAAAAAAAAAAAAAAAAAAEJUQy1QRVJQICAgICAgICAgICAgICAgICAgICAgICAg6AMAAAAAAADoAwAAAAAAAOgDAAAAAAAA6AMAAAAAAAAiucJlAAAAABAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJsXAwAAAAAAIhAAAAAAAACHAQAAAAAAABAnAAAQJwAAECcAABAnAAD0AQAAkAEAAAAAAAABAAAAFwAAABsAAAABAAEAAgAAALX/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="); - let mut decoded_bytes = base64::decode(perp_market_str).unwrap(); - let perp_market_bytes = decoded_bytes.as_mut_slice(); - - let key = Pubkey::default(); - let owner = Pubkey::from_str("dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH").unwrap(); - let mut lamports = 0; - let perp_market_account_info = - create_account_info(&key, true, &mut lamports, perp_market_bytes, &owner); - - let perp_market_loader: AccountLoader = - AccountLoader::try_from(&perp_market_account_info).unwrap(); - let perp_market = perp_market_loader.load_mut().unwrap(); - - let oracle_price = 43174 * PRICE_PRECISION_I64; - let quote_oracle_price = PRICE_PRECISION_I64; - - let margin_shortage = 2077 * QUOTE_PRECISION; - - let (lp_shares_to_burn, base_asset_amount) = - calculate_lp_shares_to_burn_for_risk_reduction( - position, - &perp_market, - oracle_price, - quote_oracle_price, - margin_shortage, - user_custom_margin_ratio, - false, - ) - .unwrap(); - - assert_eq!(lp_shares_to_burn, 1770400000); - assert_eq!(base_asset_amount, 48200000); - assert_eq!(position.lp_shares, 17704500000); - } -} diff --git a/programs/drift/src/math/margin.rs b/programs/drift/src/math/margin.rs index 6aee60be04..f62bbb6122 100644 --- a/programs/drift/src/math/margin.rs +++ b/programs/drift/src/math/margin.rs @@ -119,8 +119,6 @@ pub fn calculate_perp_position_value_and_pnl( market_position, )?; - let market_position = market_position.simulate_settled_lp_position(market, valuation_price)?; - let (base_asset_value, unrealized_pnl) = calculate_base_asset_value_and_pnl_with_oracle_price(&market_position, valuation_price)?; @@ -149,12 +147,8 @@ pub fn calculate_perp_position_value_and_pnl( }; // add small margin requirement for every open order - margin_requirement = margin_requirement - .safe_add(market_position.margin_requirement_for_open_orders()?)? - .safe_add( - market_position - .margin_requirement_for_lp_shares(market.amm.order_step_size, valuation_price)?, - )?; + margin_requirement = + margin_requirement.safe_add(market_position.margin_requirement_for_open_orders()?)?; let unrealized_asset_weight = market.get_unrealized_asset_weight(total_unrealized_pnl, margin_requirement_type)?; @@ -584,8 +578,7 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( let has_perp_liability = market_position.base_asset_amount != 0 || market_position.quote_asset_amount < 0 - || market_position.has_open_order() - || market_position.is_lp(); + || market_position.has_open_order(); if has_perp_liability { calculation.add_perp_liability()?; @@ -978,9 +971,6 @@ pub fn calculate_user_equity( market_position, )?; - let market_position = - market_position.simulate_settled_lp_position(market, valuation_price)?; - let (_, unrealized_pnl) = calculate_base_asset_value_and_pnl_with_oracle_price( &market_position, valuation_price, diff --git a/programs/drift/src/math/margin/tests.rs b/programs/drift/src/math/margin/tests.rs index 33763abd05..7a256b65db 100644 --- a/programs/drift/src/math/margin/tests.rs +++ b/programs/drift/src/math/margin/tests.rs @@ -394,154 +394,6 @@ mod test { let ans = (0).nth_root(2); assert_eq!(ans, 0); } - - #[test] - fn test_lp_user_short() { - let mut market = PerpMarket { - market_index: 0, - amm: AMM { - base_asset_reserve: 5 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 5 * AMM_RESERVE_PRECISION, - sqrt_k: 5 * AMM_RESERVE_PRECISION, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, - max_base_asset_reserve: 10 * AMM_RESERVE_PRECISION, - ..AMM::default_test() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - imf_factor: 1000, // 1_000/1_000_000 = .001 - unrealized_pnl_initial_asset_weight: 10000, - unrealized_pnl_maintenance_asset_weight: 10000, - ..PerpMarket::default() - }; - - let position = PerpPosition { - lp_shares: market.amm.user_lp_shares as u64, - ..PerpPosition::default() - }; - - let oracle_price_data = OraclePriceData { - price: (2 * PRICE_PRECISION) as i64, - confidence: 0, - delay: 2, - has_sufficient_number_of_data_points: true, - }; - - let strict_oracle_price = StrictOraclePrice::test(QUOTE_PRECISION_I64); - let (pmr, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // make the market unbalanced - - let trade_size = 3 * AMM_RESERVE_PRECISION; - let (new_qar, new_bar) = calculate_swap_output( - trade_size, - market.amm.base_asset_reserve, - SwapDirection::Add, // user shorts - market.amm.sqrt_k, - ) - .unwrap(); - market.amm.quote_asset_reserve = new_qar; - market.amm.base_asset_reserve = new_bar; - - let (pmr2, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // larger margin req in more unbalanced market - assert!(pmr2 > pmr) - } - - #[test] - fn test_lp_user_long() { - let mut market = PerpMarket { - market_index: 0, - amm: AMM { - base_asset_reserve: 5 * AMM_RESERVE_PRECISION, - quote_asset_reserve: 5 * AMM_RESERVE_PRECISION, - sqrt_k: 5 * AMM_RESERVE_PRECISION, - user_lp_shares: 10 * AMM_RESERVE_PRECISION, - max_base_asset_reserve: 10 * AMM_RESERVE_PRECISION, - ..AMM::default_test() - }, - margin_ratio_initial: 1000, - margin_ratio_maintenance: 500, - imf_factor: 1000, // 1_000/1_000_000 = .001 - unrealized_pnl_initial_asset_weight: 10000, - unrealized_pnl_maintenance_asset_weight: 10000, - ..PerpMarket::default() - }; - - let position = PerpPosition { - lp_shares: market.amm.user_lp_shares as u64, - ..PerpPosition::default() - }; - - let oracle_price_data = OraclePriceData { - price: (2 * PRICE_PRECISION) as i64, - confidence: 0, - delay: 2, - has_sufficient_number_of_data_points: true, - }; - - let strict_oracle_price = StrictOraclePrice::test(QUOTE_PRECISION_I64); - let (pmr, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // make the market unbalanced - let trade_size = 3 * AMM_RESERVE_PRECISION; - let (new_qar, new_bar) = calculate_swap_output( - trade_size, - market.amm.base_asset_reserve, - SwapDirection::Remove, // user longs - market.amm.sqrt_k, - ) - .unwrap(); - market.amm.quote_asset_reserve = new_qar; - market.amm.base_asset_reserve = new_bar; - - let strict_oracle_price = StrictOraclePrice::test(QUOTE_PRECISION_I64); - let (pmr2, _, _, _, _) = calculate_perp_position_value_and_pnl( - &position, - &market, - &oracle_price_data, - &strict_oracle_price, - MarginRequirementType::Initial, - 0, - false, - false, - ) - .unwrap(); - - // larger margin req in more unbalanced market - assert!(pmr2 > pmr) - } } #[cfg(test)] diff --git a/programs/drift/src/math/mod.rs b/programs/drift/src/math/mod.rs index aa7ec7f196..89edbdafc5 100644 --- a/programs/drift/src/math/mod.rs +++ b/programs/drift/src/math/mod.rs @@ -16,7 +16,6 @@ pub mod funding; pub mod helpers; pub mod insurance; pub mod liquidation; -pub mod lp; pub mod margin; pub mod matching; pub mod oracle; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 3628d7f231..8f3aff9140 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -353,7 +353,6 @@ pub fn get_position_delta_for_fill( PositionDirection::Long => base_asset_amount.cast()?, PositionDirection::Short => -base_asset_amount.cast()?, }, - remainder_base_asset_amount: None, }) } diff --git a/programs/drift/src/math/position.rs b/programs/drift/src/math/position.rs index b16c7c1c34..998970480d 100644 --- a/programs/drift/src/math/position.rs +++ b/programs/drift/src/math/position.rs @@ -43,23 +43,17 @@ pub fn calculate_base_asset_value(base_asset_amount: i128, amm: &AMM) -> DriftRe let (base_asset_reserve, quote_asset_reserve) = (amm.base_asset_reserve, amm.quote_asset_reserve); - let amm_lp_shares = amm.sqrt_k.safe_sub(amm.user_lp_shares)?; - - let base_asset_reserve_proportion = - get_proportion_u128(base_asset_reserve, amm_lp_shares, amm.sqrt_k)?; - - let quote_asset_reserve_proportion = - get_proportion_u128(quote_asset_reserve, amm_lp_shares, amm.sqrt_k)?; + let amm_lp_shares = amm.sqrt_k; let (new_quote_asset_reserve, _new_base_asset_reserve) = amm::calculate_swap_output( base_asset_amount.unsigned_abs(), - base_asset_reserve_proportion, + base_asset_reserve, swap_direction, amm_lp_shares, )?; let base_asset_value = calculate_quote_asset_amount_swapped( - quote_asset_reserve_proportion, + quote_asset_reserve, new_quote_asset_reserve, swap_direction, amm.peg_multiplier, @@ -174,32 +168,19 @@ pub fn get_position_update_type( position: &PerpPosition, delta: &PositionDelta, ) -> DriftResult { - if position.base_asset_amount == 0 && position.remainder_base_asset_amount == 0 { + if position.base_asset_amount == 0 { return Ok(PositionUpdateType::Open); } - let position_base_with_remainder = if position.remainder_base_asset_amount != 0 { - position - .base_asset_amount - .safe_add(position.remainder_base_asset_amount.cast::()?)? - } else { - position.base_asset_amount - }; + let position_base = position.base_asset_amount; - let delta_base_with_remainder = - if let Some(remainder_base_asset_amount) = delta.remainder_base_asset_amount { - delta - .base_asset_amount - .safe_add(remainder_base_asset_amount.cast()?)? - } else { - delta.base_asset_amount - }; + let delta_base = delta.base_asset_amount; - if position_base_with_remainder.signum() == delta_base_with_remainder.signum() { + if position_base.signum() == delta_base.signum() { Ok(PositionUpdateType::Increase) - } else if position_base_with_remainder.abs() > delta_base_with_remainder.abs() { + } else if position_base.abs() > delta_base.abs() { Ok(PositionUpdateType::Reduce) - } else if position_base_with_remainder.abs() == delta_base_with_remainder.abs() { + } else if position_base.abs() == delta_base.abs() { Ok(PositionUpdateType::Close) } else { Ok(PositionUpdateType::Flip) @@ -209,8 +190,7 @@ pub fn get_position_update_type( pub fn get_new_position_amounts( position: &PerpPosition, delta: &PositionDelta, - market: &PerpMarket, -) -> DriftResult<(i64, i64, i64, i64)> { +) -> DriftResult<(i64, i64)> { let new_quote_asset_amount = position .quote_asset_amount .safe_add(delta.quote_asset_amount)?; @@ -219,48 +199,5 @@ pub fn get_new_position_amounts( .base_asset_amount .safe_add(delta.base_asset_amount)?; - let mut new_remainder_base_asset_amount = position - .remainder_base_asset_amount - .cast::()? - .safe_add( - delta - .remainder_base_asset_amount - .unwrap_or(0) - .cast::()?, - )?; - let mut new_settled_base_asset_amount = delta.base_asset_amount; - - if delta.remainder_base_asset_amount.is_some() { - if new_remainder_base_asset_amount.unsigned_abs() >= market.amm.order_step_size { - let (standardized_remainder_base_asset_amount, remainder_base_asset_amount) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - new_remainder_base_asset_amount.cast()?, - market.amm.order_step_size.cast()?, - )?; - - new_base_asset_amount = - new_base_asset_amount.safe_add(standardized_remainder_base_asset_amount.cast()?)?; - - new_settled_base_asset_amount = new_settled_base_asset_amount - .safe_add(standardized_remainder_base_asset_amount.cast()?)?; - - new_remainder_base_asset_amount = remainder_base_asset_amount.cast()?; - } else { - new_remainder_base_asset_amount = new_remainder_base_asset_amount.cast()?; - } - - validate!( - new_remainder_base_asset_amount.abs() <= i32::MAX as i64, - ErrorCode::InvalidPositionDelta, - "new_remainder_base_asset_amount={} > i32 max", - new_remainder_base_asset_amount - )?; - } - - Ok(( - new_base_asset_amount, - new_settled_base_asset_amount, - new_quote_asset_amount, - new_remainder_base_asset_amount, - )) + Ok((new_base_asset_amount, new_quote_asset_amount)) } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 398ee7d970..e2bec65a8a 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -137,23 +137,6 @@ impl ContractTier { } } -#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq, PartialOrd, Ord)] -pub enum AMMLiquiditySplit { - ProtocolOwned, - LPOwned, - Shared, -} - -impl AMMLiquiditySplit { - pub fn get_order_action_explanation(&self) -> OrderActionExplanation { - match &self { - AMMLiquiditySplit::ProtocolOwned => OrderActionExplanation::OrderFilledWithAMMJit, - AMMLiquiditySplit::LPOwned => OrderActionExplanation::OrderFilledWithLPJit, - AMMLiquiditySplit::Shared => OrderActionExplanation::OrderFilledWithAMMJitLPSplit, - } - } -} - #[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq, PartialOrd, Ord)] pub enum AMMAvailability { Immediate, @@ -337,7 +320,7 @@ impl PerpMarket { pub fn can_skip_auction_duration( &self, state: &State, - amm_lp_allowed_to_jit_make: bool, + amm_has_low_enough_inventory: bool, ) -> DriftResult { if state.amm_immediate_fill_paused()? { return Ok(false); @@ -345,7 +328,7 @@ impl PerpMarket { let amm_low_inventory_and_profitable = self.amm.net_revenue_since_last_funding >= DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT - && amm_lp_allowed_to_jit_make; + && amm_has_low_enough_inventory; let amm_oracle_no_latency = self.amm.oracle_source == OracleSource::Prelaunch || (self.amm.historical_oracle_data.last_oracle_delay == 0 && self.amm.oracle_source == OracleSource::PythLazer); @@ -595,29 +578,6 @@ impl PerpMarket { Ok(depth) } - pub fn update_market_with_counterparty( - &mut self, - delta: &PositionDelta, - new_settled_base_asset_amount: i64, - ) -> DriftResult { - // indicates that position delta is settling lp counterparty - if delta.remainder_base_asset_amount.is_some() { - // todo: name for this is confusing, but adding is correct as is - // definition: net position of users in the market that has the LP as a counterparty (which have NOT settled) - self.amm.base_asset_amount_with_unsettled_lp = self - .amm - .base_asset_amount_with_unsettled_lp - .safe_add(new_settled_base_asset_amount.cast()?)?; - - self.amm.quote_asset_amount_with_unsettled_lp = self - .amm - .quote_asset_amount_with_unsettled_lp - .safe_add(delta.quote_asset_amount.cast()?)?; - } - - Ok(()) - } - pub fn is_price_divergence_ok_for_settle_pnl(&self, oracle_price: i64) -> DriftResult { let oracle_divergence = oracle_price .safe_sub(self.amm.historical_oracle_data.last_oracle_price_twap_5min)? @@ -1359,17 +1319,13 @@ impl AMM { pub fn get_lower_bound_sqrt_k(self) -> DriftResult { Ok(self.sqrt_k.min( - self.user_lp_shares - .safe_add(self.user_lp_shares.safe_div(1000)?)? - .max(self.min_order_size.cast()?) - .max(self.base_asset_amount_with_amm.unsigned_abs().cast()?), + (self.min_order_size.cast::()?) + .max(self.base_asset_amount_with_amm.unsigned_abs()), )) } pub fn get_protocol_owned_position(self) -> DriftResult { - self.base_asset_amount_with_amm - .safe_add(self.base_asset_amount_with_unsettled_lp)? - .cast::() + self.base_asset_amount_with_amm.cast::() } pub fn get_max_reference_price_offset(self) -> DriftResult { @@ -1390,109 +1346,6 @@ impl AMM { Ok(max_offset) } - pub fn get_per_lp_base_unit(self) -> DriftResult { - let scalar: i128 = 10_i128.pow(self.per_lp_base.abs().cast()?); - - if self.per_lp_base > 0 { - AMM_RESERVE_PRECISION_I128.safe_mul(scalar) - } else { - AMM_RESERVE_PRECISION_I128.safe_div(scalar) - } - } - - pub fn calculate_lp_base_delta( - &self, - per_lp_delta_base: i128, - base_unit: i128, - ) -> DriftResult { - // calculate dedicated for user lp shares - let lp_delta_base = - get_proportion_i128(per_lp_delta_base, self.user_lp_shares, base_unit.cast()?)?; - - Ok(lp_delta_base) - } - - pub fn calculate_per_lp_delta( - &self, - delta: &PositionDelta, - fee_to_market: i128, - liquidity_split: AMMLiquiditySplit, - base_unit: i128, - ) -> DriftResult<(i128, i128, i128)> { - let total_lp_shares = if liquidity_split == AMMLiquiditySplit::LPOwned { - self.user_lp_shares - } else { - self.sqrt_k - }; - - // update Market per lp position - let per_lp_delta_base = get_proportion_i128( - delta.base_asset_amount.cast()?, - base_unit.cast()?, - total_lp_shares, //.safe_div_ceil(rebase_divisor.cast()?)?, - )?; - - let mut per_lp_delta_quote = get_proportion_i128( - delta.quote_asset_amount.cast()?, - base_unit.cast()?, - total_lp_shares, //.safe_div_ceil(rebase_divisor.cast()?)?, - )?; - - // user position delta is short => lp position delta is long - if per_lp_delta_base < 0 { - // add one => lp subtract 1 - per_lp_delta_quote = per_lp_delta_quote.safe_add(1)?; - } - - // 1/5 of fee auto goes to market - // the rest goes to lps/market proportional - let per_lp_fee: i128 = if fee_to_market > 0 { - get_proportion_i128( - fee_to_market, - LP_FEE_SLICE_NUMERATOR, - LP_FEE_SLICE_DENOMINATOR, - )? - .safe_mul(base_unit)? - .safe_div(total_lp_shares.cast::()?)? - } else { - 0 - }; - - Ok((per_lp_delta_base, per_lp_delta_quote, per_lp_fee)) - } - - pub fn get_target_base_asset_amount_per_lp(&self) -> DriftResult { - if self.target_base_asset_amount_per_lp == 0 { - return Ok(0_i128); - } - - let target_base_asset_amount_per_lp: i128 = if self.per_lp_base > 0 { - let rebase_divisor = 10_i128.pow(self.per_lp_base.abs().cast()?); - self.target_base_asset_amount_per_lp - .cast::()? - .safe_mul(rebase_divisor)? - } else if self.per_lp_base < 0 { - let rebase_divisor = 10_i128.pow(self.per_lp_base.abs().cast()?); - self.target_base_asset_amount_per_lp - .cast::()? - .safe_div(rebase_divisor)? - } else { - self.target_base_asset_amount_per_lp.cast::()? - }; - - Ok(target_base_asset_amount_per_lp) - } - - pub fn imbalanced_base_asset_amount_with_lp(&self) -> DriftResult { - let target_lp_gap = self - .base_asset_amount_per_lp - .safe_sub(self.get_target_base_asset_amount_per_lp()?)?; - - let base_unit = self.get_per_lp_base_unit()?.cast()?; - - get_proportion_i128(target_lp_gap, self.user_lp_shares, base_unit) - } - pub fn amm_wants_to_jit_make(&self, taker_direction: PositionDirection) -> DriftResult { let amm_wants_to_jit_make = match taker_direction { PositionDirection::Long => { @@ -1505,27 +1358,8 @@ impl AMM { Ok(amm_wants_to_jit_make && self.amm_jit_is_active()) } - pub fn amm_lp_wants_to_jit_make( - &self, - taker_direction: PositionDirection, - ) -> DriftResult { - if self.user_lp_shares == 0 { - return Ok(false); - } - - let amm_lp_wants_to_jit_make = match taker_direction { - PositionDirection::Long => { - self.base_asset_amount_per_lp > self.get_target_base_asset_amount_per_lp()? - } - PositionDirection::Short => { - self.base_asset_amount_per_lp < self.get_target_base_asset_amount_per_lp()? - } - }; - Ok(amm_lp_wants_to_jit_make && self.amm_lp_jit_is_active()) - } - - pub fn amm_lp_allowed_to_jit_make(&self, amm_wants_to_jit_make: bool) -> DriftResult { - // only allow lps to make when the amm inventory is below a certain level of available liquidity + pub fn amm_has_low_enough_inventory(&self, amm_wants_to_jit_make: bool) -> DriftResult { + // mark low inventory if below a certain level of available liquidity // i.e. 10% if amm_wants_to_jit_make { // inventory scale @@ -1535,12 +1369,7 @@ impl AMM { self.max_base_asset_reserve, )?; - let min_side_liquidity = max_bids.min(max_asks.abs()); - let protocol_owned_min_side_liquidity = get_proportion_i128( - min_side_liquidity, - self.sqrt_k.safe_sub(self.user_lp_shares)?, - self.sqrt_k, - )?; + let protocol_owned_min_side_liquidity = max_bids.min(max_asks.abs()); Ok(self.base_asset_amount_with_amm.abs() < protocol_owned_min_side_liquidity.safe_div(10)?) @@ -1553,10 +1382,6 @@ impl AMM { self.amm_jit_intensity > 0 } - pub fn amm_lp_jit_is_active(&self) -> bool { - self.amm_jit_intensity > 100 - } - pub fn reserve_price(&self) -> DriftResult { amm::calculate_price( self.quote_asset_reserve, @@ -1622,7 +1447,7 @@ impl AMM { .base_asset_amount_with_amm .unsigned_abs() .max(min_order_size_u128) - < self.sqrt_k.safe_sub(self.user_lp_shares)?) + < self.sqrt_k) && (min_order_size_u128 < max_bids.unsigned_abs().max(max_asks.unsigned_abs())); Ok(can_lower) diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 4bcb6fb2f4..87dd21bab4 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1,4 +1,3 @@ -use crate::controller::lp::apply_lp_rebase_to_perp_position; use crate::controller::position::{add_new_position, get_position_index, PositionDirection}; use crate::error::{DriftResult, ErrorCode}; use crate::math::auction::{calculate_auction_price, is_auction_complete}; @@ -7,14 +6,12 @@ use crate::math::constants::{ EPOCH_DURATION, FUEL_OVERFLOW_THRESHOLD_U32, FUEL_START_TS, OPEN_ORDER_MARGIN_REQUIREMENT, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, THIRTY_DAY, }; -use crate::math::lp::{calculate_lp_open_bids_asks, calculate_settle_lp_metrics}; use crate::math::margin::MarginRequirementType; use crate::math::orders::{ apply_protected_maker_limit_price_offset, standardize_base_asset_amount, standardize_price, }; use crate::math::position::{ - calculate_base_asset_value_and_pnl_with_oracle_price, - calculate_base_asset_value_with_oracle_price, calculate_perp_liability_value, + calculate_base_asset_value_and_pnl_with_oracle_price, calculate_perp_liability_value, }; use crate::math::safe_math::SafeMath; use crate::math::spot_balance::{ @@ -23,10 +20,10 @@ use crate::math::spot_balance::{ use crate::math::stats::calculate_rolling_sum; use crate::msg; use crate::state::oracle::StrictOraclePrice; -use crate::state::perp_market::{ContractType, PerpMarket}; +use crate::state::perp_market::ContractType; use crate::state::spot_market::{SpotBalance, SpotBalanceType, SpotMarket}; use crate::state::traits::Size; -use crate::{get_then_update_id, ID, PERCENTAGE_PRECISION_I64, QUOTE_PRECISION_U64}; +use crate::{get_then_update_id, ID, QUOTE_PRECISION_U64}; use crate::{math_error, SPOT_WEIGHT_PRECISION_I128}; use crate::{safe_increment, SPOT_WEIGHT_PRECISION}; use crate::{validate, MAX_PREDICTION_MARKET_PRICE}; @@ -978,10 +975,7 @@ impl PerpPosition { } pub fn is_available(&self) -> bool { - !self.is_open_position() - && !self.has_open_order() - && !self.has_unsettled_pnl() - && !self.is_lp() + !self.is_open_position() && !self.has_open_order() && !self.has_unsettled_pnl() } pub fn is_open_position(&self) -> bool { @@ -992,103 +986,12 @@ impl PerpPosition { self.open_orders != 0 || self.open_bids != 0 || self.open_asks != 0 } - pub fn margin_requirement_for_lp_shares( - &self, - order_step_size: u64, - valuation_price: i64, - ) -> DriftResult { - if !self.is_lp() { - return Ok(0); - } - Ok(QUOTE_PRECISION.max( - order_step_size - .cast::()? - .safe_mul(valuation_price.cast()?)? - .safe_div(PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO)?, - )) - } - pub fn margin_requirement_for_open_orders(&self) -> DriftResult { self.open_orders .cast::()? .safe_mul(OPEN_ORDER_MARGIN_REQUIREMENT) } - pub fn is_lp(&self) -> bool { - self.lp_shares > 0 - } - - pub fn simulate_settled_lp_position( - &self, - market: &PerpMarket, - valuation_price: i64, - ) -> DriftResult { - let mut settled_position = *self; - - if !settled_position.is_lp() { - return Ok(settled_position); - } - - apply_lp_rebase_to_perp_position(market, &mut settled_position)?; - - // compute lp metrics - let mut lp_metrics = calculate_settle_lp_metrics(&market.amm, &settled_position)?; - - // compute settled position - let base_asset_amount = settled_position - .base_asset_amount - .safe_add(lp_metrics.base_asset_amount.cast()?)?; - - let mut quote_asset_amount = settled_position - .quote_asset_amount - .safe_add(lp_metrics.quote_asset_amount.cast()?)?; - - let mut new_remainder_base_asset_amount = settled_position - .remainder_base_asset_amount - .cast::()? - .safe_add(lp_metrics.remainder_base_asset_amount.cast()?)?; - - if new_remainder_base_asset_amount.unsigned_abs() >= market.amm.order_step_size { - let (standardized_remainder_base_asset_amount, remainder_base_asset_amount) = - crate::math::orders::standardize_base_asset_amount_with_remainder_i128( - new_remainder_base_asset_amount.cast()?, - market.amm.order_step_size.cast()?, - )?; - - lp_metrics.base_asset_amount = lp_metrics - .base_asset_amount - .safe_add(standardized_remainder_base_asset_amount)?; - - new_remainder_base_asset_amount = remainder_base_asset_amount.cast()?; - } else { - new_remainder_base_asset_amount = new_remainder_base_asset_amount.cast()?; - } - - // dust position in baa/qaa - if new_remainder_base_asset_amount != 0 { - let dust_base_asset_value = calculate_base_asset_value_with_oracle_price( - new_remainder_base_asset_amount.cast()?, - valuation_price, - )? - .safe_add(1)?; - - quote_asset_amount = quote_asset_amount.safe_sub(dust_base_asset_value.cast()?)?; - } - - let (lp_bids, lp_asks) = calculate_lp_open_bids_asks(&settled_position, market)?; - - let open_bids = settled_position.open_bids.safe_add(lp_bids)?; - - let open_asks = settled_position.open_asks.safe_add(lp_asks)?; - - settled_position.base_asset_amount = base_asset_amount; - settled_position.quote_asset_amount = quote_asset_amount; - settled_position.open_bids = open_bids; - settled_position.open_asks = open_asks; - - Ok(settled_position) - } - pub fn has_unsettled_pnl(&self) -> bool { self.base_asset_amount == 0 && self.quote_asset_amount != 0 } @@ -1164,18 +1067,12 @@ impl PerpPosition { Ok(unrealized_pnl) } - pub fn get_base_asset_amount_with_remainder(&self) -> DriftResult { - if self.remainder_base_asset_amount != 0 { - self.base_asset_amount - .cast::()? - .safe_add(self.remainder_base_asset_amount.cast::()?) - } else { - self.base_asset_amount.cast::() - } + pub fn get_base_asset_amount(&self) -> DriftResult { + self.base_asset_amount.cast::() } - pub fn get_base_asset_amount_with_remainder_abs(&self) -> DriftResult { - Ok(self.get_base_asset_amount_with_remainder()?.abs()) + pub fn get_base_asset_amount_abs(&self) -> DriftResult { + Ok(self.get_base_asset_amount()?.abs()) } pub fn get_claimable_pnl(&self, oracle_price: i64, pnl_pool_excess: i128) -> DriftResult { @@ -1233,7 +1130,7 @@ use super::protected_maker_mode_config::ProtectedMakerParams; #[cfg(test)] impl PerpPosition { pub fn get_breakeven_price(&self) -> DriftResult { - let base_with_remainder = self.get_base_asset_amount_with_remainder()?; + let base_with_remainder = self.get_base_asset_amount()?; if base_with_remainder == 0 { return Ok(0); } @@ -1245,7 +1142,7 @@ impl PerpPosition { } pub fn get_entry_price(&self) -> DriftResult { - let base_with_remainder = self.get_base_asset_amount_with_remainder()?; + let base_with_remainder = self.get_base_asset_amount()?; if base_with_remainder == 0 { return Ok(0); } diff --git a/programs/drift/src/validation/perp_market.rs b/programs/drift/src/validation/perp_market.rs index e15008805a..e55257659f 100644 --- a/programs/drift/src/validation/perp_market.rs +++ b/programs/drift/src/validation/perp_market.rs @@ -32,19 +32,16 @@ pub fn validate_perp_market(market: &PerpMarket) -> DriftResult { )?; validate!( (market.amm.base_asset_amount_long + market.amm.base_asset_amount_short) - == market.amm.base_asset_amount_with_amm - + market.amm.base_asset_amount_with_unsettled_lp, + == market.amm.base_asset_amount_with_amm, ErrorCode::InvalidAmmDetected, "Market NET_BAA Error: market.amm.base_asset_amount_long={}, + market.amm.base_asset_amount_short={} != - market.amm.base_asset_amount_with_amm={} - + market.amm.base_asset_amount_with_unsettled_lp={}", + market.amm.base_asset_amount_with_amm={}", market.amm.base_asset_amount_long, market.amm.base_asset_amount_short, market.amm.base_asset_amount_with_amm, - market.amm.base_asset_amount_with_unsettled_lp, )?; validate!( @@ -84,15 +81,6 @@ pub fn validate_perp_market(market: &PerpMarket) -> DriftResult { market.amm.quote_asset_reserve )?; - validate!( - market.amm.sqrt_k >= market.amm.user_lp_shares, - ErrorCode::InvalidAmmDetected, - "market {} market.amm.sqrt_k < market.amm.user_lp_shares: {} < {}", - market.market_index, - market.amm.sqrt_k, - market.amm.user_lp_shares, - )?; - let invariant_sqrt_u192 = crate::bn::U192::from(market.amm.sqrt_k); let invariant = invariant_sqrt_u192.safe_mul(invariant_sqrt_u192)?; let quote_asset_reserve = invariant @@ -229,22 +217,6 @@ pub fn validate_perp_market(market: &PerpMarket) -> DriftResult { market.insurance_claim.revenue_withdraw_since_last_settle.unsigned_abs() )?; - validate!( - market.amm.base_asset_amount_per_lp < MAX_BASE_ASSET_AMOUNT_WITH_AMM as i128, - ErrorCode::InvalidAmmDetected, - "{} market.amm.base_asset_amount_per_lp too large: {}", - market.market_index, - market.amm.base_asset_amount_per_lp - )?; - - validate!( - market.amm.quote_asset_amount_per_lp < MAX_BASE_ASSET_AMOUNT_WITH_AMM as i128, - ErrorCode::InvalidAmmDetected, - "{} market.amm.quote_asset_amount_per_lp too large: {}", - market.market_index, - market.amm.quote_asset_amount_per_lp - )?; - Ok(()) } diff --git a/programs/drift/src/validation/position.rs b/programs/drift/src/validation/position.rs index e8f527de63..3d36698583 100644 --- a/programs/drift/src/validation/position.rs +++ b/programs/drift/src/validation/position.rs @@ -11,14 +11,6 @@ pub fn validate_perp_position_with_perp_market( position: &PerpPosition, market: &PerpMarket, ) -> DriftResult { - if position.lp_shares != 0 { - validate!( - position.per_lp_base == market.amm.per_lp_base, - ErrorCode::InvalidPerpPositionDetected, - "position/market per_lp_base unequal" - )?; - } - validate!( position.market_index == market.market_index, ErrorCode::InvalidPerpPositionDetected, diff --git a/sdk/src/accounts/webSocketAccountSubscriberV2.ts b/sdk/src/accounts/webSocketAccountSubscriberV2.ts index cb8a955135..c86f7bda06 100644 --- a/sdk/src/accounts/webSocketAccountSubscriberV2.ts +++ b/sdk/src/accounts/webSocketAccountSubscriberV2.ts @@ -15,8 +15,8 @@ import { Rpc, RpcSubscriptions, SolanaRpcSubscriptionsApi, - type Address, - type Commitment, + Address, + Commitment, } from 'gill'; import { PublicKey } from '@solana/web3.js'; import bs58 from 'bs58'; diff --git a/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts b/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts index 51e55ba649..31a68ce02e 100644 --- a/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts +++ b/sdk/src/accounts/webSocketProgramAccountsSubscriberV2.ts @@ -9,8 +9,8 @@ import { isAddress, Lamports, Slot, - type Address, - type Commitment as GillCommitment, + Address, + Commitment as GillCommitment, } from 'gill'; import bs58 from 'bs58'; diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 0d781291fa..65109372eb 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1389,96 +1389,6 @@ } ] }, - { - "name": "addPerpLpShares", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - } - ], - "args": [ - { - "name": "nShares", - "type": "u64" - }, - { - "name": "marketIndex", - "type": "u16" - } - ] - }, - { - "name": "removePerpLpShares", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - } - ], - "args": [ - { - "name": "sharesToBurn", - "type": "u64" - }, - { - "name": "marketIndex", - "type": "u16" - } - ] - }, - { - "name": "removePerpLpSharesInExpiringMarket", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - }, - { - "name": "signer", - "isMut": false, - "isSigner": true - } - ], - "args": [ - { - "name": "sharesToBurn", - "type": "u64" - }, - { - "name": "marketIndex", - "type": "u16" - } - ] - }, { "name": "updateUserName", "accounts": [ @@ -2291,27 +2201,6 @@ ], "args": [] }, - { - "name": "settleLp", - "accounts": [ - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "user", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "marketIndex", - "type": "u16" - } - ] - }, { "name": "settleExpiredMarket", "accounts": [ @@ -6003,58 +5892,6 @@ } ] }, - { - "name": "updatePerpMarketTargetBaseAssetAmountPerLp", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "perpMarket", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "targetBaseAssetAmountPerLp", - "type": "i32" - } - ] - }, - { - "name": "updatePerpMarketPerLpBase", - "accounts": [ - { - "name": "admin", - "isMut": false, - "isSigner": true - }, - { - "name": "state", - "isMut": false, - "isSigner": false - }, - { - "name": "perpMarket", - "isMut": true, - "isSigner": false - } - ], - "args": [ - { - "name": "perLpBase", - "type": "i8" - } - ] - }, { "name": "updateLpCooldownTime", "accounts": [ @@ -12713,23 +12550,6 @@ ] } }, - { - "name": "AMMLiquiditySplit", - "type": { - "kind": "enum", - "variants": [ - { - "name": "ProtocolOwned" - }, - { - "name": "LPOwned" - }, - { - "name": "Shared" - } - ] - } - }, { "name": "AMMAvailability", "type": { @@ -16113,4 +15933,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} +} \ No newline at end of file diff --git a/sdk/src/math/bankruptcy.ts b/sdk/src/math/bankruptcy.ts index 22cca5813b..a83789ba6c 100644 --- a/sdk/src/math/bankruptcy.ts +++ b/sdk/src/math/bankruptcy.ts @@ -21,8 +21,7 @@ export function isUserBankrupt(user: User): boolean { if ( !position.baseAssetAmount.eq(ZERO) || position.quoteAssetAmount.gt(ZERO) || - hasOpenOrders(position) || - position.lpShares.gt(ZERO) + hasOpenOrders(position) ) { return false; } diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 7cd2c60401..21bb3d40b4 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -22,14 +22,12 @@ import { } from './math/position'; import { AMM_RESERVE_PRECISION, - AMM_RESERVE_PRECISION_EXP, AMM_TO_QUOTE_PRECISION_RATIO, BASE_PRECISION, BN_MAX, DUST_POSITION_SIZE, FIVE_MINUTE, MARGIN_PRECISION, - ONE, OPEN_ORDER_MARGIN_REQUIREMENT, PRICE_PRECISION, QUOTE_PRECISION, @@ -84,7 +82,6 @@ import { getSpotLiabilityValue, getTokenAmount, } from './math/spotBalance'; -import { calculateMarketOpenBidAsk } from './math/amm'; import { calculateBaseAssetValueWithOracle, calculateCollateralDepositRequiredForTrade, @@ -227,6 +224,14 @@ export class User { return this.getPerpPositionForUserAccount(userAccount, marketIndex); } + public getPerpPositionOrEmpty(marketIndex: number): PerpPosition { + const userAccount = this.getUserAccount(); + return ( + this.getPerpPositionForUserAccount(userAccount, marketIndex) ?? + this.getEmptyPosition(marketIndex) + ); + } + public getPerpPositionAndSlot( marketIndex: number ): DataAndSlot { @@ -430,243 +435,12 @@ export class User { public getPerpBidAsks(marketIndex: number): [BN, BN] { const position = this.getPerpPosition(marketIndex); - const [lpOpenBids, lpOpenAsks] = this.getLPBidAsks(marketIndex); - - const totalOpenBids = lpOpenBids.add(position.openBids); - const totalOpenAsks = lpOpenAsks.add(position.openAsks); + const totalOpenBids = position.openBids; + const totalOpenAsks = position.openAsks; return [totalOpenBids, totalOpenAsks]; } - /** - * calculates the open bids and asks for an lp - * optionally pass in lpShares to see what bid/asks a user *would* take on - * @returns : lp open bids - * @returns : lp open asks - */ - public getLPBidAsks(marketIndex: number, lpShares?: BN): [BN, BN] { - const position = this.getPerpPosition(marketIndex); - - const lpSharesToCalc = lpShares ?? position?.lpShares; - - if (!lpSharesToCalc || lpSharesToCalc.eq(ZERO)) { - return [ZERO, ZERO]; - } - - const market = this.driftClient.getPerpMarketAccount(marketIndex); - const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk( - market.amm.baseAssetReserve, - market.amm.minBaseAssetReserve, - market.amm.maxBaseAssetReserve, - market.amm.orderStepSize - ); - - const lpOpenBids = marketOpenBids.mul(lpSharesToCalc).div(market.amm.sqrtK); - const lpOpenAsks = marketOpenAsks.mul(lpSharesToCalc).div(market.amm.sqrtK); - - return [lpOpenBids, lpOpenAsks]; - } - - /** - * calculates the market position if the lp position was settled - * @returns : the settled userPosition - * @returns : the dust base asset amount (ie, < stepsize) - * @returns : pnl from settle - */ - public getPerpPositionWithLPSettle( - marketIndex: number, - originalPosition?: PerpPosition, - burnLpShares = false, - includeRemainderInBaseAmount = false - ): [PerpPosition, BN, BN] { - originalPosition = - originalPosition ?? - this.getPerpPosition(marketIndex) ?? - this.getEmptyPosition(marketIndex); - - if (originalPosition.lpShares.eq(ZERO)) { - return [originalPosition, ZERO, ZERO]; - } - - const position = this.getClonedPosition(originalPosition); - const market = this.driftClient.getPerpMarketAccount(position.marketIndex); - - if (market.amm.perLpBase != position.perLpBase) { - // perLpBase = 1 => per 10 LP shares, perLpBase = -1 => per 0.1 LP shares - const expoDiff = market.amm.perLpBase - position.perLpBase; - const marketPerLpRebaseScalar = new BN(10 ** Math.abs(expoDiff)); - - if (expoDiff > 0) { - position.lastBaseAssetAmountPerLp = - position.lastBaseAssetAmountPerLp.mul(marketPerLpRebaseScalar); - position.lastQuoteAssetAmountPerLp = - position.lastQuoteAssetAmountPerLp.mul(marketPerLpRebaseScalar); - } else { - position.lastBaseAssetAmountPerLp = - position.lastBaseAssetAmountPerLp.div(marketPerLpRebaseScalar); - position.lastQuoteAssetAmountPerLp = - position.lastQuoteAssetAmountPerLp.div(marketPerLpRebaseScalar); - } - - position.perLpBase = position.perLpBase + expoDiff; - } - - const nShares = position.lpShares; - - // incorp unsettled funding on pre settled position - const quoteFundingPnl = calculateUnsettledFundingPnl(market, position); - - let baseUnit = AMM_RESERVE_PRECISION; - if (market.amm.perLpBase == position.perLpBase) { - if ( - position.perLpBase >= 0 && - position.perLpBase <= AMM_RESERVE_PRECISION_EXP.toNumber() - ) { - const marketPerLpRebase = new BN(10 ** market.amm.perLpBase); - baseUnit = baseUnit.mul(marketPerLpRebase); - } else if ( - position.perLpBase < 0 && - position.perLpBase >= -AMM_RESERVE_PRECISION_EXP.toNumber() - ) { - const marketPerLpRebase = new BN(10 ** Math.abs(market.amm.perLpBase)); - baseUnit = baseUnit.div(marketPerLpRebase); - } else { - throw 'cannot calc'; - } - } else { - throw 'market.amm.perLpBase != position.perLpBase'; - } - - const deltaBaa = market.amm.baseAssetAmountPerLp - .sub(position.lastBaseAssetAmountPerLp) - .mul(nShares) - .div(baseUnit); - const deltaQaa = market.amm.quoteAssetAmountPerLp - .sub(position.lastQuoteAssetAmountPerLp) - .mul(nShares) - .div(baseUnit); - - function sign(v: BN) { - return v.isNeg() ? new BN(-1) : new BN(1); - } - - function standardize(amount: BN, stepSize: BN) { - const remainder = amount.abs().mod(stepSize).mul(sign(amount)); - const standardizedAmount = amount.sub(remainder); - return [standardizedAmount, remainder]; - } - - const [standardizedBaa, remainderBaa] = standardize( - deltaBaa, - market.amm.orderStepSize - ); - - position.remainderBaseAssetAmount += remainderBaa.toNumber(); - - if ( - Math.abs(position.remainderBaseAssetAmount) > - market.amm.orderStepSize.toNumber() - ) { - const [newStandardizedBaa, newRemainderBaa] = standardize( - new BN(position.remainderBaseAssetAmount), - market.amm.orderStepSize - ); - position.baseAssetAmount = - position.baseAssetAmount.add(newStandardizedBaa); - position.remainderBaseAssetAmount = newRemainderBaa.toNumber(); - } - - let dustBaseAssetValue = ZERO; - if (burnLpShares && position.remainderBaseAssetAmount != 0) { - const oraclePriceData = this.driftClient.getOracleDataForPerpMarket( - position.marketIndex - ); - dustBaseAssetValue = new BN(Math.abs(position.remainderBaseAssetAmount)) - .mul(oraclePriceData.price) - .div(AMM_RESERVE_PRECISION) - .add(ONE); - } - - let updateType; - if (position.baseAssetAmount.eq(ZERO)) { - updateType = 'open'; - } else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) { - updateType = 'increase'; - } else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) { - updateType = 'reduce'; - } else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) { - updateType = 'close'; - } else { - updateType = 'flip'; - } - - let newQuoteEntry; - let pnl; - if (updateType == 'open' || updateType == 'increase') { - newQuoteEntry = position.quoteEntryAmount.add(deltaQaa); - pnl = ZERO; - } else if (updateType == 'reduce' || updateType == 'close') { - newQuoteEntry = position.quoteEntryAmount.sub( - position.quoteEntryAmount - .mul(deltaBaa.abs()) - .div(position.baseAssetAmount.abs()) - ); - pnl = position.quoteEntryAmount.sub(newQuoteEntry).add(deltaQaa); - } else { - newQuoteEntry = deltaQaa.sub( - deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()) - ); - pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry)); - } - position.quoteEntryAmount = newQuoteEntry; - position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa); - position.quoteAssetAmount = position.quoteAssetAmount - .add(deltaQaa) - .add(quoteFundingPnl) - .sub(dustBaseAssetValue); - position.quoteBreakEvenAmount = position.quoteBreakEvenAmount - .add(deltaQaa) - .add(quoteFundingPnl) - .sub(dustBaseAssetValue); - - // update open bids/asks - const [marketOpenBids, marketOpenAsks] = calculateMarketOpenBidAsk( - market.amm.baseAssetReserve, - market.amm.minBaseAssetReserve, - market.amm.maxBaseAssetReserve, - market.amm.orderStepSize - ); - const lpOpenBids = marketOpenBids - .mul(position.lpShares) - .div(market.amm.sqrtK); - const lpOpenAsks = marketOpenAsks - .mul(position.lpShares) - .div(market.amm.sqrtK); - position.openBids = lpOpenBids.add(position.openBids); - position.openAsks = lpOpenAsks.add(position.openAsks); - - // eliminate counting funding on settled position - if (position.baseAssetAmount.gt(ZERO)) { - position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong; - } else if (position.baseAssetAmount.lt(ZERO)) { - position.lastCumulativeFundingRate = - market.amm.cumulativeFundingRateShort; - } else { - position.lastCumulativeFundingRate = ZERO; - } - - const remainderBeforeRemoval = new BN(position.remainderBaseAssetAmount); - - if (includeRemainderInBaseAmount) { - position.baseAssetAmount = position.baseAssetAmount.add( - remainderBeforeRemoval - ); - position.remainderBaseAssetAmount = 0; - } - - return [position, remainderBeforeRemoval, pnl]; - } - /** * calculates Buying Power = free collateral / initial margin ratio * @returns : Precision QUOTE_PRECISION @@ -676,11 +450,7 @@ export class User { collateralBuffer = ZERO, enterHighLeverageMode = undefined ): BN { - const perpPosition = this.getPerpPositionWithLPSettle( - marketIndex, - undefined, - true - )[0]; + const perpPosition = this.getPerpPositionOrEmpty(marketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex); const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex); @@ -793,8 +563,7 @@ export class User { (pos) => !pos.baseAssetAmount.eq(ZERO) || !pos.quoteAssetAmount.eq(ZERO) || - !(pos.openOrders == 0) || - !pos.lpShares.eq(ZERO) + !(pos.openOrders == 0) ); } @@ -866,14 +635,6 @@ export class User { market.quoteSpotMarketIndex ); - if (perpPosition.lpShares.gt(ZERO)) { - perpPosition = this.getPerpPositionWithLPSettle( - perpPosition.marketIndex, - undefined, - !!withWeightMarginCategory - )[0]; - } - let positionUnrealizedPnl = calculatePositionPNL( market, perpPosition, @@ -1450,15 +1211,6 @@ export class User { perpPosition.marketIndex ); - if (perpPosition.lpShares.gt(ZERO)) { - // is an lp, clone so we dont mutate the position - perpPosition = this.getPerpPositionWithLPSettle( - market.marketIndex, - this.getClonedPosition(perpPosition), - !!marginCategory - )[0]; - } - let valuationPrice = this.getOracleDataForPerpMarket( market.marketIndex ).price; @@ -1537,19 +1289,6 @@ export class User { liabilityValue = liabilityValue.add( new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT) ); - - if (perpPosition.lpShares.gt(ZERO)) { - liabilityValue = liabilityValue.add( - BN.max( - QUOTE_PRECISION, - valuationPrice - .mul(market.amm.orderStepSize) - .mul(QUOTE_PRECISION) - .div(AMM_RESERVE_PRECISION) - .div(PRICE_PRECISION) - ) - ); - } } } @@ -1613,13 +1352,7 @@ export class User { oraclePriceData: Pick, includeOpenOrders = false ): BN { - const userPosition = - this.getPerpPositionWithLPSettle( - marketIndex, - undefined, - false, - true - )[0] || this.getEmptyPosition(marketIndex); + const userPosition = this.getPerpPositionOrEmpty(marketIndex); const market = this.driftClient.getPerpMarketAccount( userPosition.marketIndex ); @@ -1640,13 +1373,7 @@ export class User { oraclePriceData: OraclePriceData, includeOpenOrders = false ): BN { - const userPosition = - this.getPerpPositionWithLPSettle( - marketIndex, - undefined, - false, - true - )[0] || this.getEmptyPosition(marketIndex); + const userPosition = this.getPerpPositionOrEmpty(marketIndex); const market = this.driftClient.getPerpMarketAccount( userPosition.marketIndex ); @@ -2197,11 +1924,9 @@ export class User { const oraclePrice = this.driftClient.getOracleDataForSpotMarket(marketIndex).price; if (perpMarketWithSameOracle) { - const perpPosition = this.getPerpPositionWithLPSettle( - perpMarketWithSameOracle.marketIndex, - undefined, - true - )[0]; + const perpPosition = this.getPerpPositionOrEmpty( + perpMarketWithSameOracle.marketIndex + ); if (perpPosition) { let freeCollateralDeltaForPerp = this.calculateFreeCollateralDeltaForPerp( @@ -2287,9 +2012,7 @@ export class User { this.driftClient.getOracleDataForPerpMarket(marketIndex).price; const market = this.driftClient.getPerpMarketAccount(marketIndex); - const currentPerpPosition = - this.getPerpPositionWithLPSettle(marketIndex, undefined, true)[0] || - this.getEmptyPosition(marketIndex); + const currentPerpPosition = this.getPerpPositionOrEmpty(marketIndex); positionBaseSizeChange = standardizeBaseAssetAmount( positionBaseSizeChange, @@ -2587,12 +2310,7 @@ export class User { closeQuoteAmount: BN, estimatedEntryPrice: BN = ZERO ): BN { - const currentPosition = - this.getPerpPositionWithLPSettle( - positionMarketIndex, - undefined, - true - )[0] || this.getEmptyPosition(positionMarketIndex); + const currentPosition = this.getPerpPositionOrEmpty(positionMarketIndex); const closeBaseAmount = currentPosition.baseAssetAmount .mul(closeQuoteAmount) @@ -2658,9 +2376,7 @@ export class User { ): { tradeSize: BN; oppositeSideTradeSize: BN } { let tradeSize = ZERO; let oppositeSideTradeSize = ZERO; - const currentPosition = - this.getPerpPositionWithLPSettle(targetMarketIndex, undefined, true)[0] || - this.getEmptyPosition(targetMarketIndex); + const currentPosition = this.getPerpPositionOrEmpty(targetMarketIndex); const targetSide = isVariant(tradeSide, 'short') ? 'short' : 'long'; @@ -3433,9 +3149,7 @@ export class User { return newLeverage; } - const currentPosition = - this.getPerpPositionWithLPSettle(targetMarketIndex)[0] || - this.getEmptyPosition(targetMarketIndex); + const currentPosition = this.getPerpPositionOrEmpty(targetMarketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex); const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); @@ -3848,10 +3562,6 @@ export class User { oraclePriceData?: OraclePriceData; quoteOraclePriceData?: OraclePriceData; }): HealthComponent { - const settledLpPosition = this.getPerpPositionWithLPSettle( - perpPosition.marketIndex, - perpPosition - )[0]; const perpMarket = this.driftClient.getPerpMarketAccount( perpPosition.marketIndex ); @@ -3863,7 +3573,7 @@ export class User { worstCaseBaseAssetAmount: worstCaseBaseAmount, worstCaseLiabilityValue, } = calculateWorstCasePerpLiabilityValue( - settledLpPosition, + perpPosition, perpMarket, oraclePrice ); @@ -3892,19 +3602,6 @@ export class User { new BN(perpPosition.openOrders).mul(OPEN_ORDER_MARGIN_REQUIREMENT) ); - if (perpPosition.lpShares.gt(ZERO)) { - marginRequirement = marginRequirement.add( - BN.max( - QUOTE_PRECISION, - oraclePrice - .mul(perpMarket.amm.orderStepSize) - .mul(QUOTE_PRECISION) - .div(AMM_RESERVE_PRECISION) - .div(PRICE_PRECISION) - ) - ); - } - return { marketIndex: perpMarket.marketIndex, size: worstCaseBaseAmount, @@ -3952,14 +3649,9 @@ export class User { perpMarket.quoteSpotMarketIndex ); - const settledPerpPosition = this.getPerpPositionWithLPSettle( - perpPosition.marketIndex, - perpPosition - )[0]; - const positionUnrealizedPnl = calculatePositionPNL( perpMarket, - settledPerpPosition, + perpPosition, true, oraclePriceData ); @@ -4111,12 +3803,7 @@ export class User { liquidationBuffer?: BN, includeOpenOrders?: boolean ): BN { - const currentPerpPosition = - this.getPerpPositionWithLPSettle( - marketToIgnore, - undefined, - !!marginCategory - )[0] || this.getEmptyPosition(marketToIgnore); + const currentPerpPosition = this.getPerpPositionOrEmpty(marketToIgnore); const oracleData = this.getOracleDataForPerpMarket(marketToIgnore); diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index 5b03f289eb..e8ef72b4ea 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -37,14 +37,11 @@ test_files=( ifRebalance.ts insuranceFundStake.ts liquidateBorrowForPerpPnl.ts - liquidateMaxLps.ts liquidatePerp.ts - liquidatePerpAndLp.ts liquidatePerpWithFill.ts liquidatePerpPnlForDeposit.ts liquidateSpot.ts liquidateSpotSocialLoss.ts - liquidityProvider.ts marketOrder.ts marketOrderBaseAssetAmount.ts maxDeposit.ts @@ -60,8 +57,6 @@ test_files=( ordersWithSpread.ts pauseExchange.ts pauseDepositWithdraw.ts - perpLpJit.ts - perpLpRiskMitigation.ts phoenixTest.ts placeAndMakePerp.ts placeAndMakeSignedMsgBankrun.ts @@ -86,7 +81,6 @@ test_files=( surgePricing.ts switchboardTxCus.ts switchOracle.ts - tradingLP.ts triggerOrders.ts triggerSpotOrder.ts transferPerpPosition.ts diff --git a/tests/liquidateMaxLps.ts b/tests/liquidateMaxLps.ts deleted file mode 100644 index 90d4c72190..0000000000 --- a/tests/liquidateMaxLps.ts +++ /dev/null @@ -1,227 +0,0 @@ -import * as anchor from '@coral-xyz/anchor'; -import { - BASE_PRECISION, - BN, - OracleSource, - OracleGuardRails, - TestClient, - PRICE_PRECISION, - PositionDirection, - Wallet, - LIQUIDATION_PCT_PRECISION, -} from '../sdk/src'; - -import { Program } from '@coral-xyz/anchor'; - -import { Keypair, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js'; - -import { - mockUSDCMint, - mockUserUSDCAccount, - initializeQuoteSpotMarket, - mockOracleNoProgram, - setFeedPriceNoProgram, -} from './testHelpers'; -import { PERCENTAGE_PRECISION } from '../sdk'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -describe('max lp liq', () => { - const chProgram = anchor.workspace.Drift as Program; - - let driftClient: TestClient; - - let bankrunContextWrapper: BankrunContextWrapper; - - let bulkAccountLoader: TestBulkAccountLoader; - - let usdcMint; - let userUSDCAccount; - - const liquidatorKeyPair = new Keypair(); - let liquidatorUSDCAccount: Keypair; - let liquidatorDriftClient: TestClient; - - // ammInvariant == k == x * y - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( - mantissaSqrtScale - ); - const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( - mantissaSqrtScale - ); - - const usdcAmount = new BN(10 * 10 ** 6); - const nLpShares = new BN(10000000); - - let oracle: PublicKey; - const numMkts = 8; - - before(async () => { - const context = await startAnchor('', [], []); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - usdcMint = await mockUSDCMint(bankrunContextWrapper); - userUSDCAccount = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - bankrunContextWrapper - ); - - oracle = await mockOracleNoProgram(bankrunContextWrapper, 1); - - driftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: bankrunContextWrapper.provider.wallet, - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - perpMarketIndexes: [0], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos: [ - { - publicKey: oracle, - source: OracleSource.PYTH, - }, - ], - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - - await driftClient.updateInitialPctToLiquidate( - LIQUIDATION_PCT_PRECISION.toNumber() - ); - - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - const periodicity = new BN(0); - - for (let i = 0; i < numMkts; i++) { - await driftClient.initializePerpMarket( - i, - oracle, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - periodicity - ); - } - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - userUSDCAccount.publicKey - ); - - for (let i = 0; i < numMkts; i++) { - await driftClient.openPosition( - PositionDirection.LONG, - new BN(175) - .mul(BASE_PRECISION) - .div(new BN(10)) - .divn(numMkts * 4), - i, - new BN(0) - ); - - const txSig = await driftClient.addPerpLpShares( - nLpShares.divn(numMkts * 4), - i - ); - - bankrunContextWrapper.connection.printTxLogs(txSig); - } - - // provider.connection.requestAirdrop(liquidatorKeyPair.publicKey, 10 ** 9); - bankrunContextWrapper.fundKeypair(liquidatorKeyPair, LAMPORTS_PER_SOL); - liquidatorUSDCAccount = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - bankrunContextWrapper, - liquidatorKeyPair.publicKey - ); - liquidatorDriftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: new Wallet(liquidatorKeyPair), - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0, 1, 2, 3, 4, 5, 6, 7], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos: [ - { - publicKey: oracle, - source: OracleSource.PYTH, - }, - ], - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - await liquidatorDriftClient.subscribe(); - - await liquidatorDriftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - liquidatorUSDCAccount.publicKey - ); - }); - - after(async () => { - await driftClient.unsubscribe(); - await liquidatorDriftClient.unsubscribe(); - }); - - it('liquidate', async () => { - await setFeedPriceNoProgram(bankrunContextWrapper, 0.1, oracle); - - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: PERCENTAGE_PRECISION, - oracleTwap5MinPercentDivergence: PERCENTAGE_PRECISION.mul(new BN(10)), - }, - validity: { - slotsBeforeStaleForAmm: new BN(100), - slotsBeforeStaleForMargin: new BN(100), - confidenceIntervalMaxSize: new BN(100000), - tooVolatileRatio: new BN(11), // allow 11x change - }, - }; - - await driftClient.updateOracleGuardRails(oracleGuardRails); - - const txSig = await liquidatorDriftClient.liquidatePerp( - await driftClient.getUserAccountPublicKey(), - driftClient.getUserAccount(), - 0, - new BN(175).mul(BASE_PRECISION).div(new BN(10)).divn(numMkts), - undefined, - { - computeUnits: 300_000, - } - ); - - bankrunContextWrapper.connection.printTxLogs(txSig); - - const cus = - bankrunContextWrapper.connection.findComputeUnitConsumption(txSig); - console.log(cus); - }); -}); diff --git a/tests/liquidatePerp.ts b/tests/liquidatePerp.ts index ed4156bdbc..8a7dc6120c 100644 --- a/tests/liquidatePerp.ts +++ b/tests/liquidatePerp.ts @@ -61,7 +61,6 @@ describe('liquidate perp (no open orders)', () => { ); const usdcAmount = new BN(10 * 10 ** 6); - const nLpShares = ZERO; before(async () => { const context = await startAnchor('', [], []); @@ -205,8 +204,6 @@ describe('liquidate perp (no open orders)', () => { it('liquidate', async () => { const marketIndex = 0; - const lpShares = driftClient.getUserAccount().perpPositions[0].lpShares; - assert(lpShares.eq(nLpShares)); const driftClientUser = new User({ driftClient: driftClient, @@ -349,14 +346,6 @@ describe('liquidate perp (no open orders)', () => { assert(driftClient.getUserAccount().status === UserStatus.BEING_LIQUIDATED); assert(driftClient.getUserAccount().nextLiquidationId === 2); - // try to add liq when being liquidated -- should fail - try { - await driftClient.addPerpLpShares(nLpShares, 0); - assert(false); - } catch (err) { - assert(err.message.includes('0x17e5')); - } - const liquidationRecord = eventSubscriber.getEventsArray('LiquidationRecord')[0]; assert(liquidationRecord.liquidationId === 1); @@ -375,7 +364,6 @@ describe('liquidate perp (no open orders)', () => { assert( liquidationRecord.liquidatePerp.quoteAssetAmount.eq(new BN(1750000)) ); - assert(liquidationRecord.liquidatePerp.lpShares.eq(nLpShares)); assert(liquidationRecord.liquidatePerp.ifFee.eq(new BN(0))); assert(liquidationRecord.liquidatePerp.liquidatorFee.eq(new BN(0))); @@ -427,15 +415,6 @@ describe('liquidate perp (no open orders)', () => { .perpPositions[0].quoteAssetAmount.eq(new BN(-5767653)) ); - // try to add liq when bankrupt -- should fail - try { - await driftClient.addPerpLpShares(nLpShares, 0); - assert(false); - } catch (err) { - // cant add when bankrupt - assert(err.message.includes('0x17ed')); - } - await driftClient.updatePerpMarketContractTier(0, ContractTier.A); const tx1 = await driftClient.updatePerpMarketMaxImbalances( marketIndex, diff --git a/tests/liquidatePerpAndLp.ts b/tests/liquidatePerpAndLp.ts deleted file mode 100644 index 26edfcf242..0000000000 --- a/tests/liquidatePerpAndLp.ts +++ /dev/null @@ -1,527 +0,0 @@ -import * as anchor from '@coral-xyz/anchor'; -import { - BASE_PRECISION, - BN, - getLimitOrderParams, - isVariant, - OracleSource, - QUOTE_PRECISION, - ZERO, - OracleGuardRails, - ContractTier, - TestClient, - EventSubscriber, - PRICE_PRECISION, - PositionDirection, - Wallet, - LIQUIDATION_PCT_PRECISION, - User, -} from '../sdk/src'; -import { assert } from 'chai'; - -import { Program } from '@coral-xyz/anchor'; - -import { Keypair } from '@solana/web3.js'; - -import { - mockUSDCMint, - mockUserUSDCAccount, - initializeQuoteSpotMarket, - sleep, - mockOracleNoProgram, - setFeedPriceNoProgram, -} from './testHelpers'; -import { PERCENTAGE_PRECISION, UserStatus } from '../sdk'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -describe('liquidate perp and lp', () => { - const chProgram = anchor.workspace.Drift as Program; - - let driftClient: TestClient; - let eventSubscriber: EventSubscriber; - - let bankrunContextWrapper: BankrunContextWrapper; - - let bulkAccountLoader: TestBulkAccountLoader; - - let usdcMint; - let userUSDCAccount; - - const liquidatorKeyPair = new Keypair(); - let liquidatorUSDCAccount: Keypair; - let liquidatorDriftClient: TestClient; - - // ammInvariant == k == x * y - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const ammInitialQuoteAssetReserve = new anchor.BN(5 * 10 ** 13).mul( - mantissaSqrtScale - ); - const ammInitialBaseAssetReserve = new anchor.BN(5 * 10 ** 13).mul( - mantissaSqrtScale - ); - - const usdcAmount = new BN(11.32 * 10 ** 6); - const nLpShares = new BN(10000000); - - before(async () => { - const context = await startAnchor('', [], []); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - usdcMint = await mockUSDCMint(bankrunContextWrapper); - userUSDCAccount = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - bankrunContextWrapper - ); - - const oracle = await mockOracleNoProgram(bankrunContextWrapper, 1); - - driftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: bankrunContextWrapper.provider.wallet, - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos: [ - { - publicKey: oracle, - source: OracleSource.PYTH, - }, - ], - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - - await driftClient.updateInitialPctToLiquidate( - LIQUIDATION_PCT_PRECISION.toNumber() - ); - - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: PERCENTAGE_PRECISION, - oracleTwap5MinPercentDivergence: PERCENTAGE_PRECISION.muln(100), - }, - validity: { - slotsBeforeStaleForAmm: new BN(100), - slotsBeforeStaleForMargin: new BN(100), - confidenceIntervalMaxSize: new BN(100000), - tooVolatileRatio: new BN(11), // allow 11x change - }, - }; - - await driftClient.updateOracleGuardRails(oracleGuardRails); - - const periodicity = new BN(0); - - await driftClient.initializePerpMarket( - 0, - oracle, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - periodicity - ); - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - userUSDCAccount.publicKey - ); - - await driftClient.openPosition( - PositionDirection.LONG, - new BN(175).mul(BASE_PRECISION).div(new BN(10)), // 17.5 SOL - 0, - new BN(0) - ); - - await driftClient.addPerpLpShares(nLpShares, 0); - - for (let i = 0; i < 32; i++) { - await driftClient.placePerpOrder( - getLimitOrderParams({ - baseAssetAmount: BASE_PRECISION, - marketIndex: 0, - direction: PositionDirection.LONG, - price: PRICE_PRECISION, - }) - ); - } - - eventSubscriber = new EventSubscriber( - bankrunContextWrapper.connection.toConnection(), - chProgram - ); - - await eventSubscriber.subscribe(); - - bankrunContextWrapper.fundKeypair(liquidatorKeyPair, 10 ** 9); - liquidatorUSDCAccount = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - bankrunContextWrapper, - liquidatorKeyPair.publicKey - ); - liquidatorDriftClient = new TestClient({ - connection: bankrunContextWrapper.connection.toConnection(), - wallet: new Wallet(liquidatorKeyPair), - programID: chProgram.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos: [ - { - publicKey: oracle, - source: OracleSource.PYTH, - }, - ], - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - await liquidatorDriftClient.subscribe(); - - await liquidatorDriftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - liquidatorUSDCAccount.publicKey - ); - }); - - after(async () => { - await driftClient.unsubscribe(); - await liquidatorDriftClient.unsubscribe(); - await eventSubscriber.unsubscribe(); - }); - - it('liquidate', async () => { - const marketIndex = 0; - const lpShares = driftClient.getUserAccount().perpPositions[0].lpShares; - assert(lpShares.eq(nLpShares)); - - const driftClientUser = new User({ - driftClient: driftClient, - userAccountPublicKey: await driftClient.getUserAccountPublicKey(), - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - await driftClientUser.subscribe(); - - const mtc = driftClientUser.getTotalCollateral('Maintenance'); - const mmr = driftClientUser.getMaintenanceMarginRequirement(); - const pp = driftClientUser.getPerpPosition(0); - - const deltaValueToLiq = mtc.sub(mmr); // QUOTE_PRECISION - console.log('mtc:', mtc.toString()); - console.log('mmr:', mmr.toString()); - console.log('deltaValueToLiq:', deltaValueToLiq.toString()); - console.log('pp.base:', pp.baseAssetAmount.toString()); - - // const expectedLiqPrice = 0.521639; - const liqPrice = driftClientUser.liquidationPrice(0, ZERO); - console.log('liqPrice:', liqPrice.toString()); - const expectedLiqPrice2 = new BN('372792'); - console.log('expected liqPrice:', expectedLiqPrice2.toString()); - - assert(liqPrice.eq(expectedLiqPrice2)); - - const oracle = driftClient.getPerpMarketAccount(0).amm.oracle; - await setFeedPriceNoProgram(bankrunContextWrapper, 0.9, oracle); - await sleep(2000); - await driftClientUser.fetchAccounts(); - await driftClient.fetchAccounts(); - - const liqPriceAfterPxChange = driftClientUser.liquidationPrice(0, ZERO); - - console.log('liqPriceAfterPxChange:', liqPriceAfterPxChange.toString()); - const mtc0 = driftClientUser.getTotalCollateral('Maintenance'); - const mmr0 = driftClientUser.getMaintenanceMarginRequirement(); - const pp0 = driftClientUser.getPerpPosition(0); - - const deltaValueToLiq0 = mtc0.sub(mmr0); // QUOTE_PRECISION - console.log('mtc0:', mtc0.toString()); - console.log('mmr0:', mmr0.toString()); - console.log('deltaValueToLiq0:', deltaValueToLiq0.toString()); - console.log('pp.base0:', pp0.baseAssetAmount.toString()); - assert(liqPriceAfterPxChange.eq(expectedLiqPrice2)); - - await driftClient.settlePNL( - driftClientUser.userAccountPublicKey, - driftClientUser.getUserAccount(), - 0 - ); - - await sleep(2000); - await driftClientUser.fetchAccounts(); - await driftClient.fetchAccounts(); - - const liqPriceAfterSettlePnl = driftClientUser.liquidationPrice(0, ZERO); - - const mtc2 = driftClientUser.getTotalCollateral('Maintenance'); - const mmr2 = driftClientUser.getMaintenanceMarginRequirement(); - const pp2 = driftClientUser.getPerpPosition(0); - - const deltaValueToLiq2 = mtc2.sub(mmr2); // QUOTE_PRECISION - console.log('mtc2:', mtc2.toString()); - console.log('mmr2:', mmr2.toString()); - console.log('deltaValueToLiq2:', deltaValueToLiq2.toString()); - console.log('pp.base2:', pp2.baseAssetAmount.toString()); - - console.log('liqPriceAfterSettlePnl:', liqPriceAfterSettlePnl.toString()); - assert(liqPriceAfterSettlePnl.eq(expectedLiqPrice2)); - - await setFeedPriceNoProgram(bankrunContextWrapper, 1.1, oracle); - await driftClient.settlePNL( - driftClientUser.userAccountPublicKey, - driftClientUser.getUserAccount(), - 0 - ); - - const lpEventAfterSettle = eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log(eventSubscriber.getEventsArray('LPRecord')); - - assert(lpEventAfterSettle.nShares.eq(new BN(900000))); - - const liqPriceAfterRallySettlePnl = driftClientUser.liquidationPrice( - 0, - ZERO - ); - console.log( - 'liqPriceAfterRallySettlePnl:', - liqPriceAfterRallySettlePnl.toString() - ); - assert(liqPriceAfterRallySettlePnl.eq(expectedLiqPrice2)); - await driftClientUser.unsubscribe(); - - await setFeedPriceNoProgram(bankrunContextWrapper, 0.1, oracle); - - console.log( - driftClient.getUserAccount().perpPositions[0].lpShares.toString() - ); - - const txSig = await liquidatorDriftClient.liquidatePerp( - await driftClient.getUserAccountPublicKey(), - driftClient.getUserAccount(), - 0, - new BN(175).mul(BASE_PRECISION).div(new BN(10)) - ); - - bankrunContextWrapper.connection.printTxLogs(txSig); - - const lpEvent = eventSubscriber.getEventsArray('LPRecord')[0]; - assert(lpEvent.nShares.eq(new BN(8100000))); - - for (let i = 0; i < 32; i++) { - assert(!isVariant(driftClient.getUserAccount().orders[i].status, 'open')); - } - - assert( - liquidatorDriftClient - .getUserAccount() - .perpPositions[0].baseAssetAmount.eq(new BN(17500000000)) - ); - - assert(driftClient.getUserAccount().status === UserStatus.BEING_LIQUIDATED); - assert(driftClient.getUserAccount().nextLiquidationId === 2); - - // try to add liq when being liquidated -- should fail - try { - await driftClient.addPerpLpShares(nLpShares, 0); - assert(false); - } catch (err) { - assert(err.message.includes('0x17e5')); - } - - const liquidationRecord = - eventSubscriber.getEventsArray('LiquidationRecord')[0]; - assert(liquidationRecord.liquidationId === 1); - assert(isVariant(liquidationRecord.liquidationType, 'liquidatePerp')); - assert(liquidationRecord.liquidatePerp.marketIndex === 0); - assert(liquidationRecord.canceledOrderIds.length === 32); - assert( - liquidationRecord.liquidatePerp.oraclePrice.eq( - PRICE_PRECISION.div(new BN(10)) - ) - ); - assert( - liquidationRecord.liquidatePerp.baseAssetAmount.eq(new BN(-17500000000)) - ); - - assert( - liquidationRecord.liquidatePerp.quoteAssetAmount.eq(new BN(1750000)) - ); - assert(liquidationRecord.liquidatePerp.lpShares.eq(new BN(8100000))); - assert(liquidationRecord.liquidatePerp.ifFee.eq(new BN(0))); - assert(liquidationRecord.liquidatePerp.liquidatorFee.eq(new BN(0))); - - const fillRecord = eventSubscriber.getEventsArray('OrderActionRecord')[0]; - assert(isVariant(fillRecord.action, 'fill')); - assert(fillRecord.marketIndex === 0); - assert(isVariant(fillRecord.marketType, 'perp')); - assert(fillRecord.baseAssetAmountFilled.eq(new BN(17500000000))); - assert(fillRecord.quoteAssetAmountFilled.eq(new BN(1750000))); - assert(fillRecord.takerOrderBaseAssetAmount.eq(new BN(17500000000))); - assert( - fillRecord.takerOrderCumulativeBaseAssetAmountFilled.eq( - new BN(17500000000) - ) - ); - assert(fillRecord.takerFee.eq(new BN(0))); - assert(isVariant(fillRecord.takerOrderDirection, 'short')); - assert(fillRecord.makerOrderBaseAssetAmount.eq(new BN(17500000000))); - assert( - fillRecord.makerOrderCumulativeBaseAssetAmountFilled.eq( - new BN(17500000000) - ) - ); - console.log(fillRecord.makerFee.toString()); - assert(fillRecord.makerFee.eq(new BN(ZERO))); - assert(isVariant(fillRecord.makerOrderDirection, 'long')); - - await liquidatorDriftClient.liquidatePerpPnlForDeposit( - await driftClient.getUserAccountPublicKey(), - driftClient.getUserAccount(), - 0, - 0, - driftClient.getUserAccount().perpPositions[0].quoteAssetAmount - ); - - await driftClient.fetchAccounts(); - assert(driftClient.getUserAccount().status === UserStatus.BANKRUPT); - console.log( - driftClient.getUserAccount().perpPositions[0].quoteAssetAmount.toString() - ); - assert( - driftClient - .getUserAccount() - .perpPositions[0].quoteAssetAmount.eq(new BN(-4447653)) - ); - - // try to add liq when bankrupt -- should fail - try { - await driftClient.addPerpLpShares(nLpShares, 0); - assert(false); - } catch (err) { - // cant add when bankrupt - assert(err.message.includes('0x17ed')); - } - - await driftClient.updatePerpMarketContractTier(0, ContractTier.A); - const tx1 = await driftClient.updatePerpMarketMaxImbalances( - marketIndex, - new BN(40000).mul(QUOTE_PRECISION), - QUOTE_PRECISION, - QUOTE_PRECISION - ); - bankrunContextWrapper.connection.printTxLogs(tx1); - - await driftClient.fetchAccounts(); - const marketBeforeBankruptcy = - driftClient.getPerpMarketAccount(marketIndex); - assert( - marketBeforeBankruptcy.insuranceClaim.revenueWithdrawSinceLastSettle.eq( - ZERO - ) - ); - assert( - marketBeforeBankruptcy.insuranceClaim.quoteSettledInsurance.eq(ZERO) - ); - assert( - marketBeforeBankruptcy.insuranceClaim.quoteMaxInsurance.eq( - QUOTE_PRECISION - ) - ); - assert(marketBeforeBankruptcy.amm.totalSocialLoss.eq(ZERO)); - await liquidatorDriftClient.resolvePerpBankruptcy( - await driftClient.getUserAccountPublicKey(), - driftClient.getUserAccount(), - 0 - ); - - await driftClient.fetchAccounts(); - // all social loss - const marketAfterBankruptcy = driftClient.getPerpMarketAccount(marketIndex); - assert( - marketAfterBankruptcy.insuranceClaim.revenueWithdrawSinceLastSettle.eq( - ZERO - ) - ); - assert(marketAfterBankruptcy.insuranceClaim.quoteSettledInsurance.eq(ZERO)); - assert( - marketAfterBankruptcy.insuranceClaim.quoteMaxInsurance.eq(QUOTE_PRECISION) - ); - console.log( - 'marketAfterBankruptcy.amm.feePool.scaledBalance:', - marketAfterBankruptcy.amm.feePool.scaledBalance.toString() - ); - assert(marketAfterBankruptcy.amm.feePool.scaledBalance.eq(ZERO)); - console.log( - 'marketAfterBankruptcy.amm.totalSocialLoss:', - marketAfterBankruptcy.amm.totalSocialLoss.toString() - ); - assert(marketAfterBankruptcy.amm.totalSocialLoss.eq(new BN(4430007))); - - assert( - (driftClient.getUserAccount().status & - (UserStatus.BANKRUPT | UserStatus.BEING_LIQUIDATED)) === - 0 - ); - - const perpBankruptcyRecord = - eventSubscriber.getEventsArray('LiquidationRecord')[0]; - - console.log(eventSubscriber.getEventsArray('LiquidationRecord')); - - assert(isVariant(perpBankruptcyRecord.liquidationType, 'perpBankruptcy')); - // console.log(perpBankruptcyRecord); - assert(perpBankruptcyRecord.perpBankruptcy.marketIndex === 0); - console.log(perpBankruptcyRecord.perpBankruptcy.pnl.toString()); - console.log( - perpBankruptcyRecord.perpBankruptcy.cumulativeFundingRateDelta.toString() - ); - assert(perpBankruptcyRecord.perpBankruptcy.pnl.eq(new BN(-4447653))); - console.log( - perpBankruptcyRecord.perpBankruptcy.cumulativeFundingRateDelta.toString() - ); - assert( - perpBankruptcyRecord.perpBankruptcy.cumulativeFundingRateDelta.eq( - new BN(253144000) - ) - ); - - const market = driftClient.getPerpMarketAccount(0); - console.log( - market.amm.cumulativeFundingRateLong.toString(), - market.amm.cumulativeFundingRateShort.toString() - ); - assert(market.amm.cumulativeFundingRateLong.eq(new BN(253152333))); - assert(market.amm.cumulativeFundingRateShort.eq(new BN(-253135667))); - }); -}); diff --git a/tests/liquidityProvider.ts b/tests/liquidityProvider.ts deleted file mode 100644 index d2a895d310..0000000000 --- a/tests/liquidityProvider.ts +++ /dev/null @@ -1,1896 +0,0 @@ -import * as anchor from '@coral-xyz/anchor'; -import { assert } from 'chai'; - -import { Program } from '@coral-xyz/anchor'; - -import * as web3 from '@solana/web3.js'; - -import { - TestClient, - QUOTE_PRECISION, - AMM_RESERVE_PRECISION, - EventSubscriber, - PRICE_PRECISION, - PositionDirection, - ZERO, - BN, - calculateAmmReservesAfterSwap, - calculatePrice, - User, - OracleSource, - SwapDirection, - Wallet, - isVariant, - LPRecord, - BASE_PRECISION, - getLimitOrderParams, - OracleGuardRails, -} from '../sdk/src'; - -import { - initializeQuoteSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, - setFeedPriceNoProgram, - sleep, -} from './testHelpers'; -import { PerpPosition } from '../sdk'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -async function adjustOraclePostSwap(baa, swapDirection, market, context) { - const price = calculatePrice( - market.amm.baseAssetReserve, - market.amm.quoteAssetReserve, - market.amm.pegMultiplier - ); - - const [newQaa, newBaa] = calculateAmmReservesAfterSwap( - market.amm, - 'base', - baa.abs(), - swapDirection - ); - - const newPrice = calculatePrice(newBaa, newQaa, market.amm.pegMultiplier); - const _newPrice = newPrice.toNumber() / PRICE_PRECISION.toNumber(); - await setFeedPriceNoProgram(context, _newPrice, market.amm.oracle); - - console.log('price => new price', price.toString(), newPrice.toString()); - - return _newPrice; -} - -async function createNewUser( - program, - provider, - usdcMint, - usdcAmount, - oracleInfos, - wallet, - bulkAccountLoader -): Promise<[TestClient, User]> { - let walletFlag = true; - if (wallet == undefined) { - const kp = new web3.Keypair(); - await provider.fundKeypair(kp, 10 ** 9); - wallet = new Wallet(kp); - walletFlag = false; - } - - console.log('wallet:', walletFlag); - const usdcAta = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - provider, - wallet.publicKey - ); - - const driftClient = new TestClient({ - connection: provider.connection, - wallet: wallet, - programID: program.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0, 1], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos, - accountSubscription: bulkAccountLoader - ? { - type: 'polling', - accountLoader: bulkAccountLoader, - } - : { - type: 'websocket', - }, - }); - - if (walletFlag) { - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - } else { - await driftClient.subscribe(); - } - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - usdcAta.publicKey - ); - - const driftClientUser = new User({ - driftClient, - userAccountPublicKey: await driftClient.getUserAccountPublicKey(), - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - driftClientUser.subscribe(); - - return [driftClient, driftClientUser]; -} - -async function fullClosePosition(driftClient, userPosition) { - console.log('=> closing:', userPosition.baseAssetAmount.toString()); - let position = (await driftClient.getUserAccount()).perpPositions[0]; - let sig; - let flag = true; - while (flag) { - sig = await driftClient.closePosition(0); - await driftClient.fetchAccounts(); - position = (await driftClient.getUserAccount()).perpPositions[0]; - if (position.baseAssetAmount.eq(ZERO)) { - flag = false; - } - } - - return sig; -} - -describe('liquidity providing', () => { - const chProgram = anchor.workspace.Drift as Program; - - let bankrunContextWrapper: BankrunContextWrapper; - - let bulkAccountLoader: TestBulkAccountLoader; - - async function _viewLogs(txsig) { - const tx = await bankrunContextWrapper.connection.getTransaction(txsig, { - commitment: 'confirmed', - }); - console.log('tx logs', tx.meta.logMessages); - } - async function delay(time) { - await new Promise((resolve) => setTimeout(resolve, time)); - } - - // ammInvariant == k == x * y - const ammInitialBaseAssetReserve = new BN(300).mul(BASE_PRECISION); - const ammInitialQuoteAssetReserve = new BN(300).mul(BASE_PRECISION); - - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const stableAmmInitialQuoteAssetReserve = - BASE_PRECISION.mul(mantissaSqrtScale); - const stableAmmInitialBaseAssetReserve = - BASE_PRECISION.mul(mantissaSqrtScale); - - const usdcAmount = new BN(1_000_000_000 * 1e6); - - let driftClient: TestClient; - let eventSubscriber: EventSubscriber; - - let usdcMint: web3.Keypair; - - let driftClientUser: User; - let traderDriftClient: TestClient; - let traderDriftClientUser: User; - - let poorDriftClient: TestClient; - let poorDriftClientUser: User; - - let solusdc; - let solusdc2; - - before(async () => { - const context = await startAnchor('', [], []); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - usdcMint = await mockUSDCMint(bankrunContextWrapper); - - eventSubscriber = new EventSubscriber( - bankrunContextWrapper.connection, - chProgram - ); - await eventSubscriber.subscribe(); - - solusdc2 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - const oracleInfos = [ - { publicKey: solusdc, source: OracleSource.PYTH }, - { publicKey: solusdc2, source: OracleSource.PYTH }, - ]; - [driftClient, driftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - bankrunContextWrapper.provider.wallet, - bulkAccountLoader - ); - // used for trading / taking on baa - await driftClient.initializePerpMarket( - 0, - solusdc, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - new BN(60 * 60) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpMarketMaxFillReserveFraction(0, 1); - - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: new BN(1000000), - oracleTwap5MinPercentDivergence: new BN(1000000), - }, - validity: { - slotsBeforeStaleForAmm: new BN(10), - slotsBeforeStaleForMargin: new BN(10), - confidenceIntervalMaxSize: new BN(100), - tooVolatileRatio: new BN(100), - }, - }; - await driftClient.updateOracleGuardRails(oracleGuardRails); - - // await driftClient.updateMarketBaseAssetAmountStepSize( - // new BN(0), - // new BN(1) - // ); - - // second market -- used for funding .. - await driftClient.initializePerpMarket( - 1, - solusdc2, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - [traderDriftClient, traderDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - undefined, - bulkAccountLoader - ); - [poorDriftClient, poorDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - QUOTE_PRECISION, - oracleInfos, - undefined, - bulkAccountLoader - ); - }); - - after(async () => { - await eventSubscriber.unsubscribe(); - - await driftClient.unsubscribe(); - await driftClientUser.unsubscribe(); - - await traderDriftClient.unsubscribe(); - await traderDriftClientUser.unsubscribe(); - - await poorDriftClient.unsubscribe(); - await poorDriftClientUser.unsubscribe(); - }); - - const lpCooldown = 1; - - it('burn with standardized baa', async () => { - console.log('adding liquidity...'); - const initMarginReq = driftClientUser.getInitialMarginRequirement(); - assert(initMarginReq.eq(ZERO)); - - let market = driftClient.getPerpMarketAccount(0); - const lpAmount = new BN(100 * BASE_PRECISION.toNumber()); // 100 / (100 + 300) = 1/4 - const _sig = await driftClient.addPerpLpShares( - lpAmount, - market.marketIndex - ); - - await driftClient.fetchAccounts(); - - const addLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - assert(isVariant(addLiquidityRecord.action, 'addLiquidity')); - assert(addLiquidityRecord.nShares.eq(lpAmount)); - assert(addLiquidityRecord.marketIndex === 0); - assert( - addLiquidityRecord.user.equals( - await driftClient.getUserAccountPublicKey() - ) - ); - - const [bids, asks] = driftClientUser.getLPBidAsks(0); - console.log( - 'bar, min_bar, max_bar:', - market.amm.baseAssetReserve.toString(), - market.amm.minBaseAssetReserve.toString(), - market.amm.maxBaseAssetReserve.toString() - ); - console.log('LP open bids/asks:', bids.toString(), asks.toString()); - assert(bids.eq(new BN(41419999989))); - assert(asks.eq(new BN(-29288643749))); - - await driftClient.placePerpOrder( - getLimitOrderParams({ - baseAssetAmount: BASE_PRECISION, - marketIndex: 0, - direction: PositionDirection.LONG, // ++ bids - price: PRICE_PRECISION, - }) - ); - await driftClient.placePerpOrder( - getLimitOrderParams({ - baseAssetAmount: BASE_PRECISION, - marketIndex: 0, - direction: PositionDirection.SHORT, // ++ asks - price: PRICE_PRECISION.mul(new BN(100)), - }) - ); - - await driftClient.fetchAccounts(); - const [bids2, asks2] = driftClientUser.getPerpBidAsks(0); - assert(bids2.eq(bids.add(BASE_PRECISION))); - assert(asks2.eq(asks.sub(BASE_PRECISION))); - - await driftClient.cancelOrders(); - - await driftClient.fetchAccounts(); - const position3 = driftClientUser.getPerpPosition(0); - assert(position3.openOrders == 0); - assert(position3.openAsks.eq(ZERO)); - assert(position3.openBids.eq(ZERO)); - - const newInitMarginReq = driftClientUser.getInitialMarginRequirement(); - console.log(initMarginReq.toString(), '->', newInitMarginReq.toString()); - assert(newInitMarginReq.eq(new BN(9283999))); // 8284008 + $1 - - // ensure margin calcs didnt modify user position - const _position = driftClientUser.getPerpPosition(0); - assert(_position.openAsks.eq(ZERO)); - assert(_position.openBids.eq(ZERO)); - - const stepSize = new BN(1 * BASE_PRECISION.toNumber()); - await driftClient.updatePerpMarketStepSizeAndTickSize( - 0, - stepSize, - driftClient.getPerpMarketAccount(0).amm.orderTickSize - ); - - let user = await driftClientUser.getUserAccount(); - console.log('lpUser lpShares:', user.perpPositions[0].lpShares.toString()); - console.log( - 'lpUser baa:', - user.perpPositions[0].baseAssetAmount.toString() - ); - - assert(user.perpPositions[0].lpShares.eq(new BN('100000000000'))); - assert(user.perpPositions[0].baseAssetAmount.eq(ZERO)); - // some user goes long (lp should get a short) - console.log('user trading...'); - - market = driftClient.getPerpMarketAccount(0); - assert(market.amm.sqrtK.eq(new BN('400000000000'))); - - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - - const [newQaa, _newBaa] = calculateAmmReservesAfterSwap( - market.amm, - 'base', - tradeSize.abs(), - SwapDirection.ADD - ); - const quoteAmount = newQaa.sub(market.amm.quoteAssetReserve); - const lpQuoteAmount = quoteAmount.mul(lpAmount).div(market.amm.sqrtK); - console.log( - lpQuoteAmount.mul(QUOTE_PRECISION).div(AMM_RESERVE_PRECISION).toString() - ); - - const newPrice = await adjustOraclePostSwap( - tradeSize, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - const sig = await traderDriftClient.openPosition( - PositionDirection.SHORT, - tradeSize, - market.marketIndex, - new BN((newPrice * PRICE_PRECISION.toNumber() * 99) / 100) - ); - await _viewLogs(sig); - - // amm gets 33 (3/4 * 50 = 37.5) - // lp gets stepSize (1/4 * 50 = 12.5 => 10 with remainder 2.5) - // 2.5 / 12.5 = 0.2 - - await traderDriftClient.fetchAccounts(); - const traderUserAccount = await traderDriftClient.getUserAccount(); - const position = traderUserAccount.perpPositions[0]; - console.log( - 'trader position:', - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString() - ); - - assert(position.baseAssetAmount.eq(new BN('-5000000000'))); - - await driftClient.fetchAccounts(); - const marketNetBaa = - driftClient.getPerpMarketAccount(0).amm.baseAssetAmountWithAmm; - - console.log('removing liquidity...'); - const _txSig = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - market.marketIndex - ); - await _viewLogs(_txSig); - - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - assert(isVariant(settleLiquidityRecord.action, 'settleLiquidity')); - assert(settleLiquidityRecord.marketIndex === 0); - assert( - settleLiquidityRecord.user.equals( - await driftClient.getUserAccountPublicKey() - ) - ); - - // net baa doesnt change on settle - await driftClient.fetchAccounts(); - assert( - driftClient - .getPerpMarketAccount(0) - .amm.baseAssetAmountWithAmm.eq(marketNetBaa) - ); - - const marketAfter = driftClient.getPerpMarketAccount(0); - assert( - marketAfter.amm.baseAssetAmountWithUnsettledLp.eq(new BN('-250000000')) - ); - assert(marketAfter.amm.baseAssetAmountWithAmm.eq(new BN('-3750000000'))); - - user = await driftClientUser.getUserAccount(); - const lpPosition = user.perpPositions[0]; - - assert( - settleLiquidityRecord.deltaBaseAssetAmount.eq(lpPosition.baseAssetAmount) - ); - assert( - settleLiquidityRecord.deltaQuoteAssetAmount.eq( - lpPosition.quoteAssetAmount - ) - ); - - console.log( - 'lp tokens, baa, qaa:', - lpPosition.lpShares.toString(), - lpPosition.baseAssetAmount.toString(), - lpPosition.quoteAssetAmount.toString(), - // lpPosition.unsettledPnl.toString(), - lpPosition.lastBaseAssetAmountPerLp.toString(), - lpPosition.lastQuoteAssetAmountPerLp.toString() - ); - - // assert(lpPosition.lpShares.eq(new BN(0))); - await driftClient.fetchAccounts(); - assert(user.perpPositions[0].baseAssetAmount.eq(new BN(1000000000))); // lp is long - console.log( - '=> net baa:', - driftClient.getPerpMarketAccount(0).amm.baseAssetAmountWithAmm.toString() - ); - assert(user.perpPositions[0].quoteAssetAmount.eq(new BN(-1233700))); - // assert(user.perpPositions[0].unsettledPnl.eq(new BN(900))); - // remainder goes into the last - assert(user.perpPositions[0].lastBaseAssetAmountPerLp.eq(new BN(12500000))); - assert(user.perpPositions[0].lastQuoteAssetAmountPerLp.eq(new BN(-12337))); - - market = await driftClient.getPerpMarketAccount(0); - console.log( - market.amm.quoteAssetAmountPerLp.toString(), - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN(12500000))); - assert(market.amm.quoteAssetAmountPerLp.eq(new BN(-12337))); - console.log(user.perpPositions[0].remainderBaseAssetAmount.toString()); // lp remainder - assert(user.perpPositions[0].remainderBaseAssetAmount != 0); // lp remainder - assert(user.perpPositions[0].remainderBaseAssetAmount == 250000000); // lp remainder - - // remove - console.log('removing liquidity...'); - await driftClient.removePerpLpShares(0); - - await driftClient.fetchAccounts(); - - const removeLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - assert(isVariant(removeLiquidityRecord.action, 'removeLiquidity')); - assert(removeLiquidityRecord.nShares.eq(lpAmount)); - assert(removeLiquidityRecord.marketIndex === 0); - assert( - removeLiquidityRecord.user.equals( - await driftClient.getUserAccountPublicKey() - ) - ); - console.log( - 'removeLiquidityRecord.deltaQuoteAssetAmount', - removeLiquidityRecord.deltaQuoteAssetAmount.toString() - ); - assert(removeLiquidityRecord.deltaBaseAssetAmount.eq(ZERO)); - assert(removeLiquidityRecord.deltaQuoteAssetAmount.eq(new BN('-243866'))); // show pnl from burn in record - console.log('closing trader ...'); - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - await fullClosePosition( - traderDriftClient, - traderDriftClient.getUserAccount().perpPositions[0] - ); - const traderUserAccount2 = - traderDriftClient.getUserAccount().perpPositions[0]; - - console.log( - traderUserAccount2.lpShares.toString(), - traderUserAccount2.baseAssetAmount.toString(), - traderUserAccount2.quoteAssetAmount.toString() - ); - - console.log('closing lp ...'); - console.log( - user.perpPositions[0].baseAssetAmount - .div(new BN(BASE_PRECISION.toNumber())) - .toString() - ); - await adjustOraclePostSwap( - user.perpPositions[0].baseAssetAmount, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - - const _ttxsig = await fullClosePosition(driftClient, user.perpPositions[0]); - // await _viewLogs(ttxsig); - - await driftClient.updatePerpMarketStepSizeAndTickSize( - 0, - new BN(1), - market.amm.orderTickSize - ); - - const user2 = await driftClientUser.getUserAccount(); - const position2 = user2.perpPositions[0]; - console.log( - position2.lpShares.toString(), - position2.baseAssetAmount.toString(), - position2.quoteAssetAmount.toString() - ); - - await driftClient.fetchAccounts(); - console.log( - '=> net baa:', - driftClient.getPerpMarketAccount(0).amm.baseAssetAmountWithAmm.toString() - ); - assert( - driftClient.getPerpMarketAccount(0).amm.baseAssetAmountWithAmm.eq(ZERO) - ); - - console.log('done!'); - }); - - it('settles lp', async () => { - console.log('adding liquidity...'); - - const market = driftClient.getPerpMarketAccount(0); - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - await delay(lpCooldown + 1000); - - let user = await driftClientUser.getUserAccount(); - console.log(user.perpPositions[0].lpShares.toString()); - - // some user goes long (lp should get a short) - console.log('user trading...'); - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - - const trader = await traderDriftClient.getUserAccount(); - console.log( - 'trader size', - trader.perpPositions[0].baseAssetAmount.toString() - ); - - const [settledLPPosition, _, sdkPnl] = - driftClientUser.getPerpPositionWithLPSettle(0); - - console.log('settling...'); - try { - const _txsigg = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - user = await await driftClientUser.getUserAccount(); - const position = user.perpPositions[0]; - - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log( - 'settle pnl vs sdk', - settleLiquidityRecord.pnl.toString(), - sdkPnl.toString() - ); - console.log( - 'deltaBaseAssetAmount:', - settleLiquidityRecord.deltaBaseAssetAmount.toString() - ); - console.log( - 'deltaQuoteAssetAmount:', - settleLiquidityRecord.deltaQuoteAssetAmount.toString() - ); - - assert(settleLiquidityRecord.pnl.toString() === sdkPnl.toString()); - - // gets a short on settle - console.log( - 'simulated settle position:', - settledLPPosition.baseAssetAmount.toString(), - settledLPPosition.quoteAssetAmount.toString(), - settledLPPosition.quoteEntryAmount.toString() - ); - - // gets a short on settle - console.log( - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString(), - position.quoteEntryAmount.toString(), - position.remainderBaseAssetAmount.toString() - ); - - assert(settledLPPosition.baseAssetAmount.eq(position.baseAssetAmount)); - assert(settledLPPosition.quoteAssetAmount.eq(position.quoteAssetAmount)); - assert(settledLPPosition.quoteEntryAmount.eq(position.quoteEntryAmount)); - assert( - settledLPPosition.remainderBaseAssetAmount === - position.remainderBaseAssetAmount - ); - - console.log( - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString() - ); - assert(position.baseAssetAmount.lt(ZERO)); - assert(position.quoteAssetAmount.gt(ZERO)); - assert(position.lpShares.gt(ZERO)); - - console.log('removing liquidity...'); - const _txSig = await driftClient.removePerpLpShares(market.marketIndex); - await _viewLogs(_txSig); - - user = await driftClientUser.getUserAccount(); - const lpPosition = user.perpPositions[0]; - const lpTokenAmount = lpPosition.lpShares; - assert(lpTokenAmount.eq(ZERO)); - - console.log( - 'lp position:', - lpPosition.baseAssetAmount.toString(), - lpPosition.quoteAssetAmount.toString() - ); - - console.log('closing trader ...'); - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await fullClosePosition( - traderDriftClient, - trader.perpPositions[0] - ); - await _viewLogs(_txsig); - - const traderPosition = (await traderDriftClient.getUserAccount()) - .perpPositions[0]; - console.log( - 'trader position:', - traderPosition.baseAssetAmount.toString(), - traderPosition.quoteAssetAmount.toString() - ); - - console.log('closing lp ...'); - const market2 = driftClient.getPerpMarketAccount(0); - await adjustOraclePostSwap( - user.perpPositions[0].baseAssetAmount, - SwapDirection.ADD, - market2, - bankrunContextWrapper - ); - await fullClosePosition(driftClient, user.perpPositions[0]); - - await driftClient.fetchAccounts(); - console.log( - '=> net baa:', - driftClient.getPerpMarketAccount(0).amm.baseAssetAmountWithAmm.toString() - ); - assert( - driftClient.getPerpMarketAccount(0).amm.baseAssetAmountWithAmm.eq(ZERO) - ); - - console.log('done!'); - }); - - it('provides and removes liquidity', async () => { - let market = driftClient.getPerpMarketAccount(0); - const prevSqrtK = market.amm.sqrtK; - const prevbar = market.amm.baseAssetReserve; - const prevqar = market.amm.quoteAssetReserve; - const prevQaa = - driftClient.getUserAccount().perpPositions[0].quoteAssetAmount; - - console.log('adding liquidity...'); - try { - const _txsig = await driftClient.addPerpLpShares( - new BN(100 * AMM_RESERVE_PRECISION.toNumber()), - market.marketIndex - ); - } catch (e) { - console.error(e); - } - await delay(lpCooldown + 1000); - - market = driftClient.getPerpMarketAccount(0); - console.log( - 'sqrtK:', - prevSqrtK.toString(), - '->', - market.amm.sqrtK.toString() - ); - console.log( - 'baseAssetReserve:', - prevbar.toString(), - '->', - market.amm.baseAssetReserve.toString() - ); - console.log( - 'quoteAssetReserve:', - prevqar.toString(), - '->', - market.amm.quoteAssetReserve.toString() - ); - - // k increases = more liquidity - assert(prevSqrtK.lt(market.amm.sqrtK)); - assert(prevqar.lt(market.amm.quoteAssetReserve)); - assert(prevbar.lt(market.amm.baseAssetReserve)); - - const lpShares = (await driftClientUser.getUserAccount()).perpPositions[0] - .lpShares; - console.log('lpShares:', lpShares.toString()); - assert(lpShares.gt(ZERO)); - - console.log('removing liquidity...'); - const _txSig = await driftClient.removePerpLpShares(market.marketIndex); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - const user = await driftClientUser.getUserAccount(); - const lpTokenAmount = user.perpPositions[0].lpShares; - console.log('lp token amount:', lpTokenAmount.toString()); - assert(lpTokenAmount.eq(ZERO)); - // dont round down for no change - assert(user.perpPositions[0].quoteAssetAmount.eq(prevQaa)); - - console.log('asset reserves:'); - console.log(prevSqrtK.toString(), market.amm.sqrtK.toString()); - console.log(prevbar.toString(), market.amm.baseAssetReserve.toString()); - console.log(prevqar.toString(), market.amm.quoteAssetReserve.toString()); - - const errThreshold = new BN(500); - assert(prevSqrtK.eq(market.amm.sqrtK)); - assert( - prevbar.sub(market.amm.baseAssetReserve).abs().lte(errThreshold), - prevbar.sub(market.amm.baseAssetReserve).abs().toString() - ); - assert( - prevqar.sub(market.amm.quoteAssetReserve).abs().lte(errThreshold), - prevqar.sub(market.amm.quoteAssetReserve).abs().toString() - ); - assert(prevSqrtK.eq(market.amm.sqrtK)); - }); - - it('mints too many lp tokens', async () => { - console.log('adding liquidity...'); - const market = driftClient.getPerpMarketAccount(0); - try { - const _sig = await poorDriftClient.addPerpLpShares( - market.amm.sqrtK.mul(new BN(5)), - market.marketIndex - ); - _viewLogs(_sig); - assert(false); - } catch (e) { - console.error(e.message); - assert(e.message.includes('0x1773')); // insufficient collateral - } - }); - - return; - - it('provides lp, users shorts, removes lp, lp has long', async () => { - await driftClient.fetchAccounts(); - await traderDriftClient.fetchAccounts(); - console.log('adding liquidity...'); - - const traderUserAccount3 = await driftClient.getUserAccount(); - const position3 = traderUserAccount3.perpPositions[0]; - console.log( - 'lp position:', - position3.baseAssetAmount.toString(), - position3.quoteAssetAmount.toString() - ); - - const traderUserAccount0 = await traderDriftClient.getUserAccount(); - const position0 = traderUserAccount0.perpPositions[0]; - console.log( - 'trader position:', - position0.baseAssetAmount.toString(), - position0.quoteAssetAmount.toString() - ); - assert(position0.baseAssetAmount.eq(new BN('0'))); - - const market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.netBaseAssetAmount:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('0'))); - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - // await delay(lpCooldown + 1000); - - let user = await driftClientUser.getUserAccount(); - console.log('lpUser lpShares:', user.perpPositions[0].lpShares.toString()); - console.log( - 'lpUser baa:', - user.perpPositions[0].baseAssetAmount.toString() - ); - - // some user goes long (lp should get a short) - console.log('user trading...'); - const tradeSize = new BN(40 * BASE_PRECISION.toNumber()); - const _newPrice = await adjustOraclePostSwap( - tradeSize, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - try { - const _txsig = await traderDriftClient.openPosition( - PositionDirection.SHORT, - tradeSize, - market.marketIndex - // new BN(newPrice * PRICE_PRECISION.toNumber()) - ); - } catch (e) { - console.error(e); - } - - await traderDriftClient.fetchAccounts(); - const market1 = driftClient.getPerpMarketAccount(0); - console.log( - 'market1.amm.netBaseAssetAmount:', - market1.amm.baseAssetAmountWithAmm.toString() - ); - const ammLpRatio = - market1.amm.userLpShares.toNumber() / market1.amm.sqrtK.toNumber(); - - console.log('amm ratio:', ammLpRatio, '(', 40 * ammLpRatio, ')'); - - assert(market1.amm.baseAssetAmountWithAmm.eq(new BN('-30000000000'))); - - const traderUserAccount = await traderDriftClient.getUserAccount(); - // console.log(traderUserAccount); - const position = traderUserAccount.perpPositions[0]; - console.log( - 'trader position:', - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString() - ); - - console.log('removing liquidity...'); - const _txSig = await driftClient.removePerpLpShares(market.marketIndex); - await _viewLogs(_txSig); - - user = await driftClientUser.getUserAccount(); - const lpPosition = user.perpPositions[0]; - const lpTokenAmount = lpPosition.lpShares; - - console.log( - 'lp tokens', - lpTokenAmount.toString(), - 'baa, qaa', - lpPosition.baseAssetAmount.toString(), - lpPosition.quoteAssetAmount.toString() - // lpPosition.unsettledPnl.toString() - ); - - const removeLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - assert(isVariant(removeLiquidityRecord.action, 'removeLiquidity')); - assert( - removeLiquidityRecord.deltaBaseAssetAmount.eq( - lpPosition.baseAssetAmount.sub(position3.baseAssetAmount) - ) - ); - assert( - removeLiquidityRecord.deltaQuoteAssetAmount.eq( - lpPosition.quoteAssetAmount.sub(position3.quoteAssetAmount) - ) - ); - - assert(lpTokenAmount.eq(new BN(0))); - console.log(user.perpPositions[0].baseAssetAmount.toString()); - console.log(user.perpPositions[0].quoteAssetAmount.toString()); - assert(user.perpPositions[0].baseAssetAmount.eq(new BN('10000000000'))); // lp is long - assert(user.perpPositions[0].quoteAssetAmount.eq(new BN(-9550985))); - - console.log('closing trader ...'); - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - await fullClosePosition( - traderDriftClient, - traderUserAccount.perpPositions[0] - ); - - console.log('closing lp ...'); - console.log( - user.perpPositions[0].baseAssetAmount - .div(new BN(BASE_PRECISION.toNumber())) - .toString() - ); - await adjustOraclePostSwap( - user.perpPositions[0].baseAssetAmount, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - await fullClosePosition(driftClient, user.perpPositions[0]); - - const user2 = await driftClientUser.getUserAccount(); - const position2 = user2.perpPositions[0]; - console.log( - position2.lpShares.toString(), - position2.baseAssetAmount.toString(), - position2.quoteAssetAmount.toString() - ); - - console.log('done!'); - }); - - it('provides lp, users longs, removes lp, lp has short', async () => { - const market = driftClient.getPerpMarketAccount(0); - - console.log('adding liquidity...'); - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - // await delay(lpCooldown + 1000); - - // some user goes long (lp should get a short) - console.log('user trading...'); - const tradeSize = new BN(40 * BASE_PRECISION.toNumber()); - const _newPrice0 = await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(newPrice0 * PRICE_PRECISION.toNumber()) - ); - - const position = (await traderDriftClient.getUserAccount()) - .perpPositions[0]; - console.log( - 'trader position:', - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString() - ); - - console.log('removing liquidity...'); - const _txSig = await driftClient.removePerpLpShares(market.marketIndex); - await _viewLogs(_txSig); - - await driftClientUser.fetchAccounts(); - const user = await driftClientUser.getUserAccount(); - const lpPosition = user.perpPositions[0]; - const lpTokenAmount = lpPosition.lpShares; - - console.log('lp tokens', lpTokenAmount.toString()); - console.log( - 'baa, qaa, qea', - lpPosition.baseAssetAmount.toString(), - lpPosition.quoteAssetAmount.toString(), - lpPosition.quoteEntryAmount.toString() - - // lpPosition.unsettledPnl.toString() - ); - - assert(lpTokenAmount.eq(ZERO)); - assert(user.perpPositions[0].baseAssetAmount.eq(new BN('-10000000000'))); // lp is short - assert(user.perpPositions[0].quoteAssetAmount.eq(new BN('11940540'))); - assert(user.perpPositions[0].quoteEntryAmount.eq(new BN('11139500'))); - - console.log('closing trader...'); - await adjustOraclePostSwap( - tradeSize, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - await fullClosePosition(traderDriftClient, position); - - console.log('closing lp ...'); - await adjustOraclePostSwap( - user.perpPositions[0].baseAssetAmount, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - await fullClosePosition(driftClient, lpPosition); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - const user2 = await driftClientUser.getUserAccount(); - const lpPosition2 = user2.perpPositions[0]; - - console.log('lp tokens', lpPosition2.lpShares.toString()); - console.log( - 'lp position for market', - lpPosition2.marketIndex, - ':\n', - 'baa, qaa, qea', - lpPosition2.baseAssetAmount.toString(), - lpPosition2.quoteAssetAmount.toString(), - lpPosition2.quoteEntryAmount.toString() - ); - assert(lpPosition2.baseAssetAmount.eq(ZERO)); - - console.log('done!'); - }); - - it('lp burns a partial position', async () => { - const market = driftClient.getPerpMarketAccount(0); - - console.log('adding liquidity...'); - await driftClient.addPerpLpShares( - new BN(100).mul(AMM_RESERVE_PRECISION), - market.marketIndex - ); - // await delay(lpCooldown + 1000); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - const user0 = await driftClient.getUserAccount(); - const position0 = user0.perpPositions[0]; - console.log( - 'assert LP has 0 position in market index', - market.marketIndex, - ':', - position0.baseAssetAmount.toString(), - position0.quoteAssetAmount.toString() - ); - console.log(position0.lpShares.toString()); - - const baa0 = position0.baseAssetAmount; - assert(baa0.eq(ZERO)); - - console.log('user trading...'); - const tradeSize = new BN(40 * BASE_PRECISION.toNumber()); - const _newPrice = await adjustOraclePostSwap( - tradeSize, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - await traderDriftClient.openPosition( - PositionDirection.SHORT, - tradeSize, - market.marketIndex - // new BN(newPrice * PRICE_PRECISION.toNumber()) - ); - - console.log('removing liquidity...'); - let user = await driftClient.getUserAccount(); - let position = user.perpPositions[0]; - - const fullShares = position.lpShares; - const halfShares = position.lpShares.div(new BN(2)); - const otherHalfShares = fullShares.sub(halfShares); - - try { - const _txSig = await driftClient.removePerpLpShares( - market.marketIndex, - halfShares - ); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - user = await driftClient.getUserAccount(); - position = user.perpPositions[0]; - console.log( - 'lp first half burn:', - user.perpPositions[0].baseAssetAmount.toString(), - user.perpPositions[0].quoteAssetAmount.toString(), - user.perpPositions[0].lpShares.toString() - ); - - const baa = user.perpPositions[0].baseAssetAmount; - const qaa = user.perpPositions[0].quoteAssetAmount; - assert(baa.eq(new BN(10000000000))); - assert(qaa.eq(new BN(-6860662))); - - console.log('removing the other half of liquidity'); - await driftClient.removePerpLpShares(market.marketIndex, otherHalfShares); - - await driftClient.fetchAccounts(); - - user = await driftClient.getUserAccount(); - console.log( - 'lp second half burn:', - user.perpPositions[0].baseAssetAmount.toString(), - user.perpPositions[0].quoteAssetAmount.toString(), - user.perpPositions[0].lpShares.toString() - ); - // lp is already settled so full burn baa is already in baa - assert(user.perpPositions[0].lpShares.eq(ZERO)); - - console.log('closing trader ...'); - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - // await traderDriftClient.closePosition(new BN(0)); - const trader = await traderDriftClient.getUserAccount(); - const _txsig = await fullClosePosition( - traderDriftClient, - trader.perpPositions[0] - ); - - console.log('closing lp ...'); - await adjustOraclePostSwap( - baa, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - await fullClosePosition(driftClient, user.perpPositions[0]); - }); - - it('settles lp with pnl', async () => { - console.log('adding liquidity...'); - - const market = driftClient.getPerpMarketAccount(0); - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - await delay(lpCooldown + 1000); - - let user = await driftClientUser.getUserAccount(); - console.log(user.perpPositions[0].lpShares.toString()); - - // lp goes long - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await driftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - - // some user goes long (lp should get a short + pnl for closing long on settle) - console.log('user trading...'); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - - const trader = await traderDriftClient.getUserAccount(); - console.log( - 'trader size', - trader.perpPositions[0].baseAssetAmount.toString() - ); - - await driftClientUser.fetchAccounts(); - const sdkPnl = driftClientUser.getPerpPositionWithLPSettle(0)[2]; - - console.log('settling...'); - try { - const _txsigg = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - user = await await driftClientUser.getUserAccount(); - - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log( - 'settle pnl vs sdk', - settleLiquidityRecord.pnl.toString(), - sdkPnl.toString() - ); - assert(settleLiquidityRecord.pnl.eq(sdkPnl)); - }); - - it('update per lp base (0->1)', async () => { - //ensure non-zero for test - - await driftClient.updatePerpMarketTargetBaseAssetAmountPerLp(0, 169); - - await driftClient.fetchAccounts(); - const marketBefore = driftClient.getPerpMarketAccount(0); - - const txSig1 = await driftClient.updatePerpMarketPerLpBase(0, 1); - await _viewLogs(txSig1); - - await sleep(1400); // todo? - await driftClient.fetchAccounts(); - const marketAfter = driftClient.getPerpMarketAccount(0); - - assert( - marketAfter.amm.baseAssetAmountPerLp.eq( - marketBefore.amm.baseAssetAmountPerLp.mul(new BN(10)) - ) - ); - - assert( - marketAfter.amm.quoteAssetAmountPerLp.eq( - marketBefore.amm.quoteAssetAmountPerLp.mul(new BN(10)) - ) - ); - console.log(marketAfter.amm.targetBaseAssetAmountPerLp); - console.log(marketBefore.amm.targetBaseAssetAmountPerLp); - - assert( - marketAfter.amm.targetBaseAssetAmountPerLp == - marketBefore.amm.targetBaseAssetAmountPerLp - ); - - assert(marketBefore.amm.perLpBase == 0); - console.log('marketAfter.amm.perLpBase:', marketAfter.amm.perLpBase); - assert(marketAfter.amm.perLpBase == 1); - }); - - it('settle lp position after perLpBase change', async () => { - // some user goes long (lp should get a short + pnl for closing long on settle) - - const market = driftClient.getPerpMarketAccount(0); - console.log( - 'baseAssetAmountWithUnsettledLp:', - market.amm.baseAssetAmountWithUnsettledLp.toString() - ); - assert(market.amm.baseAssetAmountWithUnsettledLp.eq(ZERO)); - // await delay(lpCooldown + 1000); - - const user = await driftClientUser.getUserAccount(); - console.log(user.perpPositions[0].lpShares.toString()); - console.log(user.perpPositions[0].perLpBase); - - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - - console.log('user trading...'); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - - const marketAfter0 = driftClient.getPerpMarketAccount(0); - - console.log( - 'baseAssetAmountWithUnsettledLp:', - marketAfter0.amm.baseAssetAmountWithUnsettledLp.toString() - ); - assert( - marketAfter0.amm.baseAssetAmountWithUnsettledLp.eq(new BN('1250000000')) - ); - - const netValueBefore = await driftClient.getUser().getNetSpotMarketValue(); - const posBefore0: PerpPosition = await driftClient - .getUser() - .getPerpPosition(0); - assert(posBefore0.perLpBase == 0); - - const posBefore: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - // console.log(posBefore); - assert(posBefore.perLpBase == 1); // properly sets it - - const _txSig = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - market.marketIndex - ); - await _viewLogs(_txSig); - await driftClient.fetchAccounts(); - const marketAfter1 = driftClient.getPerpMarketAccount(0); - - console.log( - 'baseAssetAmountWithUnsettledLp:', - marketAfter1.amm.baseAssetAmountWithUnsettledLp.toString() - ); - assert(marketAfter1.amm.baseAssetAmountWithUnsettledLp.eq(new BN('0'))); - - const posAfter0: PerpPosition = await driftClient - .getUser() - .getPerpPosition(0); - assert(posAfter0.perLpBase == 1); - - const posAfter: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - - assert(posAfter.perLpBase == 1); - assert( - posAfter0.lastBaseAssetAmountPerLp.gt(posBefore0.lastBaseAssetAmountPerLp) - ); - // console.log(posAfter.lastBaseAssetAmountPerLp.toString()); - // console.log(posBefore.lastBaseAssetAmountPerLp.toString()); - - assert(posAfter.lastBaseAssetAmountPerLp.eq(new BN('625000000'))); - assert(posBefore.lastBaseAssetAmountPerLp.eq(new BN('750000000'))); - - const netValueAfter = await driftClient.getUser().getNetSpotMarketValue(); - - assert(netValueBefore.eq(netValueAfter)); - - const marketAfter2 = driftClient.getPerpMarketAccount(0); - - console.log( - 'baseAssetAmountWithUnsettledLp:', - marketAfter2.amm.baseAssetAmountWithUnsettledLp.toString() - ); - assert(marketAfter2.amm.baseAssetAmountWithUnsettledLp.eq(new BN('0'))); - }); - - it('add back lp shares from 0, after rebase', async () => { - const leShares = driftClientUser.getPerpPosition(0).lpShares; - await driftClient.removePerpLpShares(0, leShares); - await driftClient.fetchAccounts(); - - await driftClient.updatePerpMarketPerLpBase(0, 2); // update from 1->2 - - const posBeforeReadd: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - console.log(posBeforeReadd.baseAssetAmount.toString()); - console.log(posBeforeReadd.quoteAssetAmount.toString()); - console.log(posBeforeReadd.lastBaseAssetAmountPerLp.toString()); - console.log(posBeforeReadd.lastQuoteAssetAmountPerLp.toString()); - console.log( - posBeforeReadd.lpShares.toString(), - posBeforeReadd.perLpBase.toString() - ); - - await driftClient.addPerpLpShares(leShares, 0); // lmao why is this different param order - - const posBefore: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - console.log('posBefore'); - console.log(posBefore.baseAssetAmount.toString()); - console.log(posBefore.quoteAssetAmount.toString()); - console.log(posBefore.lastBaseAssetAmountPerLp.toString()); - console.log(posBefore.lastQuoteAssetAmountPerLp.toString()); - console.log(posBefore.lpShares.toString(), posBefore.perLpBase.toString()); - - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - const market = driftClient.getPerpMarketAccount(0); - - console.log('user trading...'); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.SHORT, - tradeSize, - market.marketIndex - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - const posAfter: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - console.log('posAfter'); - console.log(posAfter.baseAssetAmount.toString()); - console.log(posAfter.quoteAssetAmount.toString()); - console.log(posAfter.lastBaseAssetAmountPerLp.toString()); - console.log(posAfter.lastQuoteAssetAmountPerLp.toString()); - console.log(posAfter.perLpBase.toString()); - - const _txSig = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - market.marketIndex - ); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - const posAfterSettle: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - console.log('posAfterSettle'); - console.log(posAfterSettle.baseAssetAmount.toString()); - console.log(posAfterSettle.quoteAssetAmount.toString()); - console.log(posAfterSettle.lastBaseAssetAmountPerLp.toString()); - console.log(posAfterSettle.lastQuoteAssetAmountPerLp.toString()); - console.log(posAfterSettle.perLpBase.toString()); - - assert(posAfterSettle.baseAssetAmount.eq(posAfter.baseAssetAmount)); - assert(posAfterSettle.quoteAssetAmount.eq(posAfter.quoteAssetAmount)); - }); - - it('settled at negative rebase value', async () => { - await driftClient.updatePerpMarketPerLpBase(0, 1); - await driftClient.updatePerpMarketPerLpBase(0, 0); - await driftClient.updatePerpMarketPerLpBase(0, -1); - await driftClient.updatePerpMarketPerLpBase(0, -2); - await driftClient.updatePerpMarketPerLpBase(0, -3); - - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - const market = driftClient.getPerpMarketAccount(0); - - console.log('user trading...'); - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.SHORT, - tradeSize, - market.marketIndex - ); - await _viewLogs(_txsig); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - const posAfter: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - console.log('posAfter'); - console.log(posAfter.baseAssetAmount.toString()); - console.log(posAfter.quoteAssetAmount.toString()); - console.log(posAfter.lastBaseAssetAmountPerLp.toString()); - console.log(posAfter.lastQuoteAssetAmountPerLp.toString()); - console.log(posAfter.perLpBase.toString()); - - const _txSig = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - market.marketIndex - ); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - const posAfterSettle: PerpPosition = await driftClient - .getUser() - .getPerpPositionWithLPSettle(0)[0]; - console.log('posAfterSettle'); - console.log(posAfterSettle.baseAssetAmount.toString()); - console.log(posAfterSettle.quoteAssetAmount.toString()); - console.log(posAfterSettle.lastBaseAssetAmountPerLp.toString()); - console.log(posAfterSettle.lastQuoteAssetAmountPerLp.toString()); - console.log(posAfterSettle.perLpBase.toString()); - - assert(posAfterSettle.baseAssetAmount.eq(posAfter.baseAssetAmount)); - assert(posAfterSettle.quoteAssetAmount.eq(posAfter.quoteAssetAmount)); - }); - - it('permissionless lp burn', async () => { - return; - const lpAmount = new BN(1 * BASE_PRECISION.toNumber()); - const _sig = await driftClient.addPerpLpShares(lpAmount, 0); - - const time = bankrunContextWrapper.connection.getTime(); - const _2sig = await driftClient.updatePerpMarketExpiry(0, new BN(time + 5)); - - await sleep(5000); - - await driftClient.fetchAccounts(); - const market = driftClient.getPerpMarketAccount(0); - console.log(market.status); - - await traderDriftClient.removePerpLpSharesInExpiringMarket( - 0, - await driftClient.getUserAccountPublicKey() - ); - - await driftClientUser.fetchAccounts(); - const position = driftClientUser.getPerpPosition(0); - console.log(position); - // assert(position.lpShares.eq(ZERO)); - }); - - it('lp gets paid in funding (todo)', async () => { - const market = driftClient.getPerpMarketAccount(1); - const marketIndex = market.marketIndex; - - console.log('adding liquidity to market ', marketIndex, '...'); - try { - const _sig = await driftClient.addPerpLpShares( - new BN(100_000).mul(new BN(BASE_PRECISION.toNumber())), - marketIndex - ); - } catch (e) { - console.error(e); - } - await delay(lpCooldown + 1000); - - console.log('user trading...'); - // const trader0 = await traderDriftClient.getUserAccount(); - const tradeSize = new BN(100).mul(AMM_RESERVE_PRECISION); - - const newPrice = await adjustOraclePostSwap( - tradeSize, - SwapDirection.ADD, - market, - bankrunContextWrapper - ); - console.log('market', marketIndex, 'post trade price:', newPrice); - try { - const _txig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - marketIndex, - new BN(newPrice * PRICE_PRECISION.toNumber()) - ); - } catch (e) { - console.error(e); - } - - console.log('updating funding rates'); - const _txsig = await driftClient.updateFundingRate(marketIndex, solusdc2); - - console.log('removing liquidity...'); - try { - const _txSig = await driftClient.removePerpLpShares(marketIndex); - _viewLogs(_txSig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - - const user = driftClientUser.getUserAccount(); - // const feePayment = new BN(1300000); - // const fundingPayment = new BN(900000); - - // dont get paid in fees bc the sqrtk is so big that fees dont get given to the lps - // TODO - // assert(user.perpPositions[1].unsettledPnl.eq(fundingPayment.add(feePayment))); - const position1 = user.perpPositions[1]; - console.log( - 'lp position:', - position1.baseAssetAmount.toString(), - position1.quoteAssetAmount.toString(), - 'vs step size:', - market.amm.orderStepSize.toString() - ); - assert(user.perpPositions[1].baseAssetAmount.eq(ZERO)); // lp has no position - assert( - user.perpPositions[1].baseAssetAmount.abs().lt(market.amm.orderStepSize) - ); - // const trader = traderDriftClient.getUserAccount(); - // await adjustOraclePostSwap( - // trader.perpPositions[1].baseAssetAmount, - // SwapDirection.ADD, - // market - // ); - // await traderDriftClient.closePosition(market.marketIndex); // close lp position - - // console.log('closing lp ...'); - // console.log(user.perpPositions[1].baseAssetAmount.toString()); - // await adjustOraclePostSwap( - // user.perpPositions[1].baseAssetAmount, - // SwapDirection.REMOVE, - // market - // ); - }); - - // // TODO - // it('provides and removes liquidity too fast', async () => { - // const market = driftClient.getPerpMarketAccount(0); - - // const lpShares = new BN(100 * AMM_RESERVE_PRECISION); - // const addLpIx = await driftClient.getAddLiquidityIx( - // lpShares, - // market.marketIndex - // ); - // const removeLpIx = await driftClient.getRemoveLiquidityIx( - // market.marketIndex, - // lpShares - // ); - - // const tx = new web3.Transaction().add(addLpIx).add(removeLpIx); - // try { - // await provider.sendAll([{ tx }]); - // assert(false); - // } catch (e) { - // console.error(e); - // assert(e.message.includes('0x17ce')); - // } - // }); - - // it('removes liquidity when market position is small', async () => { - // console.log('adding liquidity...'); - // await driftClient.addLiquidity(usdcAmount, new BN(0)); - // - // console.log('user trading...'); - // await traderDriftClient.openPosition( - // PositionDirection.LONG, - // new BN(1 * 1e6), - // new BN(0) - // ); - // - // console.log('removing liquidity...'); - // await driftClient.removeLiquidity(new BN(0)); - // - // const user = driftClient.getUserAccount(); - // const position = user.perpPositions[0]; - // - // // small loss - // assert(position.unsettledPnl.lt(ZERO)); - // // no position - // assert(position.baseAssetAmount.eq(ZERO)); - // assert(position.quoteAssetAmount.eq(ZERO)); - // }); - // - // uncomment when settle fcn is ready - - /* it('adds additional liquidity to an already open lp', async () => { - console.log('adding liquidity...'); - const lp_amount = new BN(300 * 1e6); - const _txSig = await driftClient.addLiquidity(lp_amount, new BN(0)); - - console.log( - 'tx logs', - (await connection.getTransaction(txsig, { commitment: 'confirmed' })).meta - .logMessages - ); - - const init_user = driftClientUser.getUserAccount(); - await driftClient.addLiquidity(lp_amount, new BN(0)); - const user = driftClientUser.getUserAccount(); - - const init_tokens = init_user.perpPositions[0].lpTokens; - const tokens = user.perpPositions[0].lpTokens; - console.log(init_tokens.toString(), tokens.toString()); - assert(init_tokens.lt(tokens)); - - await driftClient.removeLiquidity(new BN(0)); - }); */ - - /* it('settles an lps position', async () => { - console.log('adding liquidity...'); - await driftClient.addLiquidity(usdcAmount, new BN(0)); - - let user = driftClient.getUserAccount(); - const baa = user.perpPositions[0].baseAssetAmount; - const qaa = user.perpPositions[0].quoteAssetAmount; - const upnl = user.perpPositions[0].unsettledPnl; - - console.log('user trading...'); - await traderDriftClient.openPosition( - PositionDirection.SHORT, - new BN(115 * 1e5), - new BN(0) - ); - - console.log('settling...'); - await traderDriftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - new BN(0) - ); - - user = driftClient.getUserAccount(); - const position = user.perpPositions[0]; - const post_baa = position.baseAssetAmount; - const post_qaa = position.quoteAssetAmount; - const post_upnl = position.unsettledPnl; - - // they got the market position + upnl - console.log(baa.toString(), post_baa.toString()); - console.log(qaa.toString(), post_qaa.toString()); - console.log(upnl.toString(), post_upnl.toString()); - assert(!post_baa.eq(baa)); - assert(post_qaa.gt(qaa)); - assert(!post_upnl.eq(upnl)); - - // other sht was updated - const market = driftClient.getPerpMarketAccount(new BN(0)); - assert(market.amm.netBaseAssetAmount.eq(position.lastNetBaseAssetAmount)); - assert( - market.amm.totalFeeMinusDistributions.eq( - position.lastTotalFeeMinusDistributions - ) - ); - - const _txSig = await driftClient.removeLiquidity(new BN(0)); - - console.log('done!'); - }); */ - - /* it('simulates a settle via sdk', async () => { - const userPosition2 = driftClient.getUserAccount().perpPositions[0]; - console.log( - userPosition2.baseAssetAmount.toString(), - userPosition2.quoteAssetAmount.toString(), - userPosition2.unsettledPnl.toString() - ); - - console.log('add lp ...'); - await driftClient.addLiquidity(usdcAmount, new BN(0)); - - console.log('user trading...'); - await traderDriftClient.openPosition( - PositionDirection.SHORT, - new BN(115 * 1e5), - new BN(0) - ); - - const [settledPosition, result, _] = driftClientUser.getPerpPositionWithLPSettle( - new BN(0) - ); - - console.log('settling...'); - const _txSig = await traderDriftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - new BN(0) - ); - console.log( - 'tx logs', - (await connection.getTransaction(txsig, { commitment: 'confirmed' })).meta - .logMessages - ); - const userPosition = driftClient.getUserAccount().perpPositions[0]; - - console.log( - userPosition.baseAssetAmount.toString(), - settledPosition.baseAssetAmount.toString(), - - userPosition.quoteAssetAmount.toString(), - settledPosition.quoteAssetAmount.toString(), - - userPosition.unsettledPnl.toString(), - settledPosition.unsettledPnl.toString() - ); - assert(result == SettleResult.RECIEVED_MARKET_POSITION); - assert(userPosition.baseAssetAmount.eq(settledPosition.baseAssetAmount)); - assert(userPosition.quoteAssetAmount.eq(settledPosition.quoteAssetAmount)); - assert(userPosition.unsettledPnl.eq(settledPosition.unsettledPnl)); - }); */ -}); diff --git a/tests/perpLpJit.ts b/tests/perpLpJit.ts deleted file mode 100644 index fce1546a06..0000000000 --- a/tests/perpLpJit.ts +++ /dev/null @@ -1,1250 +0,0 @@ -import * as web3 from '@solana/web3.js'; -import * as anchor from '@coral-xyz/anchor'; -import { Program } from '@coral-xyz/anchor'; -import { assert } from 'chai'; - -import { - TestClient, - QUOTE_PRECISION, - EventSubscriber, - PRICE_PRECISION, - PositionDirection, - ZERO, - BN, - calculateAmmReservesAfterSwap, - calculatePrice, - User, - OracleSource, - SwapDirection, - Wallet, - LPRecord, - BASE_PRECISION, - getLimitOrderParams, - OracleGuardRails, - PostOnlyParams, - isVariant, - calculateBidAskPrice, -} from '../sdk/src'; - -import { - initializeQuoteSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, - setFeedPriceNoProgram, - sleep, - // sleep, -} from './testHelpers'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -let lastOrderRecordsLength = 0; - -async function adjustOraclePostSwap(baa, swapDirection, market, context) { - const price = calculatePrice( - market.amm.baseAssetReserve, - market.amm.quoteAssetReserve, - market.amm.pegMultiplier - ); - - const [newQaa, newBaa] = calculateAmmReservesAfterSwap( - market.amm, - 'base', - baa.abs(), - swapDirection - ); - - const newPrice = calculatePrice(newBaa, newQaa, market.amm.pegMultiplier); - const _newPrice = newPrice.toNumber() / PRICE_PRECISION.toNumber(); - await setFeedPriceNoProgram(context, _newPrice, market.amm.oracle); - - console.log('price => new price', price.toString(), newPrice.toString()); - - return _newPrice; -} - -async function createNewUser( - program, - context, - usdcMint, - usdcAmount, - oracleInfos, - wallet, - bulkAccountLoader -) { - let walletFlag = true; - if (wallet == undefined) { - const kp = new web3.Keypair(); - await context.fundKeypair(kp, 10 ** 9); - wallet = new Wallet(kp); - walletFlag = false; - } - - console.log('wallet:', walletFlag); - const usdcAta = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - context, - wallet.publicKey - ); - - const driftClient = new TestClient({ - connection: context.connection.toConnection(), - wallet: wallet, - programID: program.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0, 1, 2, 3], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos, - accountSubscription: bulkAccountLoader - ? { - type: 'polling', - accountLoader: bulkAccountLoader, - } - : { - type: 'websocket', - }, - }); - - if (walletFlag) { - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - } else { - await driftClient.subscribe(); - } - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - usdcAta.publicKey - ); - - const driftClientUser = new User({ - driftClient, - userAccountPublicKey: await driftClient.getUserAccountPublicKey(), - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - driftClientUser.subscribe(); - - return [driftClient, driftClientUser]; -} - -describe('lp jit', () => { - const chProgram = anchor.workspace.Drift as Program; - - async function _viewLogs(txsig) { - bankrunContextWrapper.printTxLogs(txsig); - } - async function delay(time) { - await new Promise((resolve) => setTimeout(resolve, time)); - } - - // ammInvariant == k == x * y - const ammInitialBaseAssetReserve = new BN(300).mul(BASE_PRECISION); - const ammInitialQuoteAssetReserve = new BN(300).mul(BASE_PRECISION); - - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const stableAmmInitialQuoteAssetReserve = - BASE_PRECISION.mul(mantissaSqrtScale); - const stableAmmInitialBaseAssetReserve = - BASE_PRECISION.mul(mantissaSqrtScale); - - const usdcAmount = new BN(1_000_000_000 * 1e6); // 1 milli - - let driftClient: TestClient; - let eventSubscriber: EventSubscriber; - - let bulkAccountLoader: TestBulkAccountLoader; - - let bankrunContextWrapper: BankrunContextWrapper; - - let usdcMint: web3.Keypair; - - let driftClientUser: User; - let traderDriftClient: TestClient; - let traderDriftClientUser: User; - - let poorDriftClient: TestClient; - let poorDriftClientUser: User; - - let solusdc; - let solusdc2; - let solusdc3; - let btcusdc; - - before(async () => { - const context = await startAnchor('', [], []); - - 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); - - solusdc3 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc2 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - btcusdc = await mockOracleNoProgram(bankrunContextWrapper, 26069, -7); - - const oracleInfos = [ - { publicKey: solusdc, source: OracleSource.PYTH }, - { publicKey: solusdc2, source: OracleSource.PYTH }, - { publicKey: solusdc3, source: OracleSource.PYTH }, - { publicKey: btcusdc, source: OracleSource.PYTH }, - ]; - - // @ts-ignore - [driftClient, driftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - bankrunContextWrapper.provider.wallet, - bulkAccountLoader - ); - // used for trading / taking on baa - await driftClient.initializePerpMarket( - 0, - solusdc, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - new BN(60 * 60) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpMarketMaxFillReserveFraction(0, 1); - - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: new BN(1000000), - oracleTwap5MinPercentDivergence: new BN(1000000), - }, - validity: { - slotsBeforeStaleForAmm: new BN(10), - slotsBeforeStaleForMargin: new BN(10), - confidenceIntervalMaxSize: new BN(100), - tooVolatileRatio: new BN(100), - }, - }; - await driftClient.updateOracleGuardRails(oracleGuardRails); - - // await driftClient.updateMarketBaseAssetAmountStepSize( - // new BN(0), - // new BN(1) - // ); - - // second market -- used for funding .. - await driftClient.initializePerpMarket( - 1, - solusdc2, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - // third market - await driftClient.initializePerpMarket( - 2, - solusdc3, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - - // third market - await driftClient.initializePerpMarket( - 3, - btcusdc, - stableAmmInitialBaseAssetReserve.div(new BN(1000)), - stableAmmInitialQuoteAssetReserve.div(new BN(1000)), - new BN(0), - new BN(26690 * 1000) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - // @ts-ignore - [traderDriftClient, traderDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - undefined, - bulkAccountLoader - ); - - // @ts-ignore - [poorDriftClient, poorDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - QUOTE_PRECISION.mul(new BN(10000)), - oracleInfos, - undefined, - bulkAccountLoader - ); - }); - - after(async () => { - await eventSubscriber.unsubscribe(); - - await driftClient.unsubscribe(); - await driftClientUser.unsubscribe(); - - await traderDriftClient.unsubscribe(); - await traderDriftClientUser.unsubscribe(); - - await poorDriftClient.unsubscribe(); - await poorDriftClientUser.unsubscribe(); - }); - - const lpCooldown = 1; - it('perp jit check (amm jit intensity = 0)', async () => { - const marketIndex = 0; - console.log('adding liquidity...'); - await driftClient.updatePerpMarketTargetBaseAssetAmountPerLp( - 0, - BASE_PRECISION.toNumber() - ); - sleep(1200); - await driftClient.fetchAccounts(); - let market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString(), - 'target:', - market.amm.targetBaseAssetAmountPerLp - ); - assert(market.amm.sqrtK.eq(new BN('300000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - // assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - await delay(lpCooldown + 1000); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('400000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - - let user = await driftClientUser.getUserAccount(); - assert(user.perpPositions[0].lpShares.toString() == '100000000000'); // 10 * 1e9 - - // lp goes long - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await driftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-12500000'))); - - // some user goes long (lp should get a short + pnl for closing long on settle) - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-25000000'))); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('7500000000'))); - - // add jit maker going other way - const takerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount: tradeSize, - price: new BN(0.9 * PRICE_PRECISION.toNumber()), - auctionStartPrice: new BN(0.99 * PRICE_PRECISION.toNumber()), - auctionEndPrice: new BN(0.929 * PRICE_PRECISION.toNumber()), - auctionDuration: 10, - userOrderId: 1, - postOnly: PostOnlyParams.NONE, - }); - await traderDriftClient.placePerpOrder(takerOrderParams); - await traderDriftClient.fetchAccounts(); - const order = traderDriftClientUser.getOrderByUserOrderId(1); - assert(!order.postOnly); - - const makerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.LONG, - baseAssetAmount: tradeSize, - price: new BN(1.011 * PRICE_PRECISION.toNumber()), - userOrderId: 1, - postOnly: PostOnlyParams.MUST_POST_ONLY, - bitFlags: true, - }); - - const txSig = await poorDriftClient.placeAndMakePerpOrder( - makerOrderParams, - { - taker: await traderDriftClient.getUserAccountPublicKey(), - order: traderDriftClient.getOrderByUserId(1), - takerUserAccount: traderDriftClient.getUserAccount(), - takerStats: traderDriftClient.getUserStatsAccountPublicKey(), - } - ); - await _viewLogs(txSig); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-12500000'))); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('3750000000'))); - console.log( - 'market.amm.baseAssetAmountWithUnsettledLp:', - market.amm.baseAssetAmountWithUnsettledLp.toString() - ); - - assert(market.amm.baseAssetAmountWithUnsettledLp.eq(new BN('1250000000'))); - - const trader = await traderDriftClient.getUserAccount(); - console.log( - 'trader size', - trader.perpPositions[0].baseAssetAmount.toString() - ); - - await driftClientUser.fetchAccounts(); - const sdkPnl = driftClientUser.getPerpPositionWithLPSettle(0)[2]; - - console.log('settling...'); - try { - const _txsigg = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - user = await await driftClientUser.getUserAccount(); - - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log( - 'settle pnl vs sdk', - settleLiquidityRecord.pnl.toString(), - sdkPnl.toString() - ); - assert(settleLiquidityRecord.pnl.eq(sdkPnl)); - }); - it('perp jit check (amm jit intensity = 100)', async () => { - const marketIndex = 1; - await driftClient.updateAmmJitIntensity(marketIndex, 100); - - console.log('adding liquidity...'); - await driftClient.updatePerpMarketTargetBaseAssetAmountPerLp( - marketIndex, - BASE_PRECISION.toNumber() - ); - await delay(lpCooldown + 1000); - - await driftClient.fetchAccounts(); - let market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('1000000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - await delay(lpCooldown + 1000); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('1100000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - await driftClientUser.fetchAccounts(); - - let user = await driftClientUser.getUserAccount(); - assert(user.perpPositions[0].lpShares.toString() == '100000000000'); // 10 * 1e9 - - // lp goes long - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await driftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-4545454'))); - - // some user goes long (lp should get a short + pnl for closing long on settle) - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-9090908'))); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('9090909200'))); - - // add jit maker going other way - const takerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount: tradeSize, - price: new BN(0.9 * PRICE_PRECISION.toNumber()), - auctionStartPrice: new BN(0.99 * PRICE_PRECISION.toNumber()), - auctionEndPrice: new BN(0.929 * PRICE_PRECISION.toNumber()), - auctionDuration: 10, - userOrderId: 1, - postOnly: PostOnlyParams.NONE, - }); - await traderDriftClient.placePerpOrder(takerOrderParams); - await traderDriftClient.fetchAccounts(); - const order = traderDriftClient.getUser().getOrderByUserOrderId(1); - assert(!order.postOnly); - - const makerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.LONG, - baseAssetAmount: tradeSize, - price: new BN(1.011 * PRICE_PRECISION.toNumber()), - userOrderId: 1, - postOnly: PostOnlyParams.MUST_POST_ONLY, - bitFlags: true, - }); - - const txSig = await poorDriftClient.placeAndMakePerpOrder( - makerOrderParams, - { - taker: await traderDriftClient.getUserAccountPublicKey(), - order: traderDriftClient.getOrderByUserId(1), - takerUserAccount: traderDriftClient.getUserAccount(), - takerStats: traderDriftClient.getUserStatsAccountPublicKey(), - } - ); - await _viewLogs(txSig); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-5455090'))); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('5204991000'))); - console.log( - 'market.amm.baseAssetAmountWithUnsettledLp:', - market.amm.baseAssetAmountWithUnsettledLp.toString() - ); - - assert(market.amm.baseAssetAmountWithUnsettledLp.eq(new BN('545509000'))); - - const trader = await traderDriftClient.getUserAccount(); - console.log( - 'trader size', - trader.perpPositions[0].baseAssetAmount.toString() - ); - - await driftClientUser.fetchAccounts(); - const sdkPnl = driftClientUser.getPerpPositionWithLPSettle(0)[2]; - - console.log('settling...'); - try { - const _txsigg = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - await driftClientUser.fetchAccounts(); - user = await driftClientUser.getUserAccount(); - - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log( - 'settle pnl vs sdk', - settleLiquidityRecord.pnl.toString(), - sdkPnl.toString() - ); - // assert(settleLiquidityRecord.pnl.eq(sdkPnl)); //TODO - }); - it('perp jit check (amm jit intensity = 200)', async () => { - const marketIndex = 2; - - await driftClient.updateAmmJitIntensity(marketIndex, 200); - - console.log('adding liquidity...'); - await driftClient.updatePerpMarketTargetBaseAssetAmountPerLp( - marketIndex, - BASE_PRECISION.toNumber() - ); - sleep(1200); - - await driftClient.fetchAccounts(); - let market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('1000000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert( - market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber(), - `targetBaseAssetAmountPerLp: ${ - market.amm.targetBaseAssetAmountPerLp - } != ${BASE_PRECISION.toNumber()}` - ); - - const _sig = await driftClient.addPerpLpShares( - new BN(100 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - await delay(lpCooldown + 1000); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('1100000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - await driftClientUser.fetchAccounts(); - - let user = await driftClientUser.getUserAccount(); - assert(user.perpPositions[0].lpShares.toString() == '100000000000'); // 10 * 1e9 - - // lp goes long - const tradeSize = new BN(5 * BASE_PRECISION.toNumber()); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await driftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-4545454'))); - - // some user goes long (lp should get a short + pnl for closing long on settle) - // try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - // } catch (e) { - // console.log(e); - // } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-9090908'))); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('9090909200'))); - - // const trader = await traderDriftClient.getUserAccount(); - // console.log( - // 'trader size', - // trader.perpPositions[0].baseAssetAmount.toString() - // ); - - for (let i = 0; i < 10; i++) { - // add jit maker going other way - const takerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount: tradeSize, - price: new BN(0.9 * PRICE_PRECISION.toNumber()), - auctionStartPrice: new BN(0.99 * PRICE_PRECISION.toNumber()), - auctionEndPrice: new BN(0.929 * PRICE_PRECISION.toNumber()), - auctionDuration: 10, - userOrderId: 1, - postOnly: PostOnlyParams.NONE, - }); - await traderDriftClient.placePerpOrder(takerOrderParams); - await traderDriftClient.fetchAccounts(); - // console.log(takerOrderParams); - const order = traderDriftClient.getUser().getOrderByUserOrderId(1); - // console.log(order); - - assert(!order.postOnly); - - const makerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.LONG, - baseAssetAmount: tradeSize, - price: new BN(1.011 * PRICE_PRECISION.toNumber()), - userOrderId: 1, - postOnly: PostOnlyParams.MUST_POST_ONLY, - bitFlags: true, - }); - // console.log('maker:', makerOrderParams); - - const txSig = await poorDriftClient.placeAndMakePerpOrder( - makerOrderParams, - { - taker: await traderDriftClient.getUserAccountPublicKey(), - order: traderDriftClient.getOrderByUserId(1), - takerUserAccount: traderDriftClient.getUserAccount(), - takerStats: traderDriftClient.getUserStatsAccountPublicKey(), - } - ); - await _viewLogs(txSig); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - console.log( - 'market.amm.baseAssetAmountWithUnsettledLp:', - market.amm.baseAssetAmountWithUnsettledLp.toString() - ); - - if (i == 0) { - assert(market.amm.baseAssetAmountPerLp.eq(new BN('-5227727'))); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('5227727300'))); - assert( - market.amm.baseAssetAmountWithUnsettledLp.eq(new BN('522772700')) - ); - } - } - market = driftClient.getPerpMarketAccount(marketIndex); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('12499904'))); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('90400'))); - assert(market.amm.baseAssetAmountWithUnsettledLp.eq(new BN('-1249990400'))); - - const trader = await traderDriftClient.getUserAccount(); - console.log( - 'trader size', - trader.perpPositions[0].baseAssetAmount.toString() - ); - - await driftClientUser.fetchAccounts(); - const sdkPnl = driftClientUser.getPerpPositionWithLPSettle(0)[2]; - - console.log('settling...'); - try { - const _txsigg = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - user = await driftClientUser.getUserAccount(); - const orderRecords = eventSubscriber.getEventsArray('OrderActionRecord'); - - const matchOrderRecord = orderRecords[1]; - assert( - isVariant(matchOrderRecord.actionExplanation, 'orderFilledWithMatchJit') - ); - assert(matchOrderRecord.baseAssetAmountFilled.toString(), '3750000000'); - assert(matchOrderRecord.quoteAssetAmountFilled.toString(), '3791212'); - - const jitOrderRecord = orderRecords[2]; - assert(isVariant(jitOrderRecord.actionExplanation, 'orderFilledWithLpJit')); - assert(jitOrderRecord.baseAssetAmountFilled.toString(), '1250000000'); - assert(jitOrderRecord.quoteAssetAmountFilled.toString(), '1263738'); - - // console.log('len of orderRecords', orderRecords.length); - lastOrderRecordsLength = orderRecords.length; - - // Convert the array to a JSON string - // const fs = require('fs'); - // // Custom replacer function to convert BN values to numerical representation - // const replacer = (key, value) => { - // if (value instanceof BN) { - // return value.toString(10); // Convert BN to base-10 string - // } - // return value; - // }; - // const jsonOrderRecords = JSON.stringify(orderRecords, replacer); - - // // Write the JSON string to a file - // fs.writeFile('orderRecords.json', jsonOrderRecords, 'utf8', (err) => { - // if (err) { - // console.error('Error writing to JSON file:', err); - // return; - // } - // console.log('orderRecords successfully written to orderRecords.json'); - // }); - - // assert(orderRecords) - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log( - 'settle pnl vs sdk', - settleLiquidityRecord.pnl.toString(), - sdkPnl.toString() - ); - // assert(settleLiquidityRecord.pnl.eq(sdkPnl)); - }); - it('perp jit check BTC inout (amm jit intensity = 200)', async () => { - const marketIndex = 3; - - await driftClient.updateAmmJitIntensity(marketIndex, 200); - await driftClient.updatePerpMarketCurveUpdateIntensity(marketIndex, 100); - await driftClient.updatePerpMarketMaxSpread(marketIndex, 100000); - await driftClient.updatePerpMarketBaseSpread(marketIndex, 10000); - sleep(1200); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - let market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('1000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert(market.amm.targetBaseAssetAmountPerLp == 0); - - console.log('adding liquidity...'); - const _sig = await driftClient.addPerpLpShares( - BASE_PRECISION, - market.marketIndex - ); - await delay(lpCooldown + 1000); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('2000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - let [bid, ask] = calculateBidAskPrice( - driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getMMOracleDataForPerpMarket(marketIndex) - ); - console.log(bid.toString(), '/', ask.toString()); - console.log('bid:', bid.toString()); - console.log('ask:', ask.toString()); - - let perpy = await driftClientUser.getPerpPosition(marketIndex); - - assert(perpy.lpShares.toString() == '1000000000'); // 1e9 - console.log( - 'user.perpPositions[0].baseAssetAmount:', - perpy.baseAssetAmount.toString() - ); - assert(perpy.baseAssetAmount.toString() == '0'); // no fills - - // trader goes long - const tradeSize = BASE_PRECISION.div(new BN(20)); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - perpy = await driftClientUser.getPerpPosition(marketIndex); - assert(perpy.baseAssetAmount.toString() == '0'); // unsettled - - await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - marketIndex - ); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - perpy = await driftClientUser.getPerpPosition(marketIndex); - console.log('perpy.baseAssetAmount:', perpy.baseAssetAmount.toString()); - assert(perpy.baseAssetAmount.toString() == '-10000000'); // settled - - [bid, ask] = calculateBidAskPrice( - driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getMMOracleDataForPerpMarket(marketIndex) - ); - console.log(bid.toString(), '/', ask.toString()); - console.log('bid:', bid.toString()); - console.log('ask:', ask.toString()); - - const takerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount: tradeSize, - price: new BN(26000 * PRICE_PRECISION.toNumber()), - auctionStartPrice: new BN(26400.99 * PRICE_PRECISION.toNumber()), - auctionEndPrice: new BN(26000.929 * PRICE_PRECISION.toNumber()), - auctionDuration: 10, - userOrderId: 1, - postOnly: PostOnlyParams.NONE, - }); - await traderDriftClient.placePerpOrder(takerOrderParams); - await traderDriftClient.fetchAccounts(); - // console.log(takerOrderParams); - // const order = traderDriftClientUser.getOrderByUserOrderId(1); - - const makerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.LONG, - baseAssetAmount: tradeSize, - price: new BN(26488.88 * PRICE_PRECISION.toNumber()), - userOrderId: 1, - postOnly: PostOnlyParams.MUST_POST_ONLY, - bitFlags: true, - }); - - [bid, ask] = calculateBidAskPrice( - driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getMMOracleDataForPerpMarket(marketIndex) - ); - console.log(bid.toString(), '/', ask.toString()); - console.log('bid:', bid.toString()); - console.log('ask:', ask.toString()); - - await poorDriftClient.placeAndMakePerpOrder(makerOrderParams, { - taker: await traderDriftClient.getUserAccountPublicKey(), - order: traderDriftClient.getOrderByUserId(1), - takerUserAccount: traderDriftClient.getUserAccount(), - takerStats: traderDriftClient.getUserStatsAccountPublicKey(), - }); - - await driftClient.fetchAccounts(); - const marketAfter = driftClient.getPerpMarketAccount(marketIndex); - const orderRecords = eventSubscriber.getEventsArray('OrderActionRecord'); - - console.log('len of orderRecords', orderRecords.length); - assert(orderRecords.length - lastOrderRecordsLength == 7); - lastOrderRecordsLength = orderRecords.length; - // Convert the array to a JSON string - - // console.log(marketAfter); - console.log(marketAfter.amm.baseAssetAmountPerLp.toString()); - console.log(marketAfter.amm.quoteAssetAmountPerLp.toString()); - console.log(marketAfter.amm.baseAssetAmountWithUnsettledLp.toString()); - console.log(marketAfter.amm.baseAssetAmountWithAmm.toString()); - - assert(marketAfter.amm.baseAssetAmountPerLp.eq(new BN(-5000000))); - assert(marketAfter.amm.quoteAssetAmountPerLp.eq(new BN(144606790 - 1))); - assert(marketAfter.amm.baseAssetAmountWithUnsettledLp.eq(new BN(-5000000))); - assert(marketAfter.amm.baseAssetAmountWithAmm.eq(new BN(5000000))); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - const perpPos = driftClientUser.getPerpPosition(marketIndex); - console.log(perpPos.baseAssetAmount.toString()); - assert(perpPos.baseAssetAmount.toString() == '-10000000'); - - const [settledPos, dustPos, lpPnl] = - driftClientUser.getPerpPositionWithLPSettle( - marketIndex, - undefined, - false, - true - ); - // console.log('settlePos:', settledPos); - console.log('dustPos:', dustPos.toString()); - console.log('lpPnl:', lpPnl.toString()); - - assert(dustPos.toString() == '0'); - assert(lpPnl.toString() == '6134171'); - - const _sig2 = await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - marketIndex - ); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - const perpPosAfter = driftClientUser.getPerpPosition(marketIndex); - console.log( - 'perpPosAfter.baseAssetAmount:', - perpPosAfter.baseAssetAmount.toString() - ); - assert(perpPosAfter.baseAssetAmount.toString() == '-5000000'); - assert(perpPosAfter.baseAssetAmount.eq(settledPos.baseAssetAmount)); - - const takerOrderParams2 = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount: tradeSize.mul(new BN(20)), - price: new BN(26000 * PRICE_PRECISION.toNumber()), - auctionStartPrice: new BN(26400.99 * PRICE_PRECISION.toNumber()), - auctionEndPrice: new BN(26000.929 * PRICE_PRECISION.toNumber()), - auctionDuration: 10, - userOrderId: 1, - postOnly: PostOnlyParams.NONE, - }); - await traderDriftClient.placePerpOrder(takerOrderParams2); - await traderDriftClient.fetchAccounts(); - // console.log(takerOrderParams); - // const order = traderDriftClientUser.getOrderByUserOrderId(1); - - const makerOrderParams2 = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.LONG, - baseAssetAmount: tradeSize.mul(new BN(20)), - price: new BN(26488.88 * PRICE_PRECISION.toNumber()), - userOrderId: 1, - postOnly: PostOnlyParams.MUST_POST_ONLY, - bitFlags: true, - }); - - [bid, ask] = calculateBidAskPrice( - driftClient.getPerpMarketAccount(marketIndex).amm, - driftClient.getMMOracleDataForPerpMarket(marketIndex) - ); - console.log(bid.toString(), '/', ask.toString()); - console.log('bid:', bid.toString()); - console.log('ask:', ask.toString()); - - await poorDriftClient.placeAndMakePerpOrder(makerOrderParams2, { - taker: await traderDriftClient.getUserAccountPublicKey(), - order: traderDriftClient.getOrderByUserId(1), - takerUserAccount: traderDriftClient.getUserAccount(), - takerStats: traderDriftClient.getUserStatsAccountPublicKey(), - }); - const marketAfter2 = driftClient.getPerpMarketAccount(marketIndex); - - console.log(marketAfter2.amm.baseAssetAmountPerLp.toString()); - console.log(marketAfter2.amm.quoteAssetAmountPerLp.toString()); - console.log(marketAfter2.amm.baseAssetAmountWithUnsettledLp.toString()); - console.log(marketAfter2.amm.baseAssetAmountWithAmm.toString()); - - assert(marketAfter2.amm.baseAssetAmountPerLp.eq(new BN(-2500000))); - assert(marketAfter2.amm.quoteAssetAmountPerLp.eq(new BN(78437566))); - assert( - marketAfter2.amm.baseAssetAmountWithUnsettledLp.eq(new BN(-2500000)) - ); - assert(marketAfter2.amm.baseAssetAmountWithAmm.eq(new BN(2500000))); - - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - const perpPos2 = driftClientUser.getPerpPosition(marketIndex); - console.log(perpPos2.baseAssetAmount.toString()); - assert(perpPos2.baseAssetAmount.toString() == '-5000000'); - - const [settledPos2, dustPos2, lpPnl2] = - driftClientUser.getPerpPositionWithLPSettle( - marketIndex, - undefined, - false, - true - ); - // console.log('settlePos:', settledPos2); - console.log('dustPos:', dustPos2.toString()); - console.log('lpPnl:', lpPnl2.toString()); - - assert(dustPos2.toString() == '0'); - assert(lpPnl2.toString() == '3067086'); - - await driftClient.settleLP( - await driftClient.getUserAccountPublicKey(), - marketIndex - ); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - const perpPosAfter2 = driftClientUser.getPerpPosition(marketIndex); - console.log( - 'perpPosAfter2.baseAssetAmount:', - perpPosAfter2.baseAssetAmount.toString() - ); - assert(perpPosAfter2.baseAssetAmount.toString() == '-2500000'); - assert(perpPosAfter2.baseAssetAmount.eq(settledPos2.baseAssetAmount)); - - const orderRecords2 = eventSubscriber.getEventsArray('OrderActionRecord'); - console.log('len of orderRecords', orderRecords2.length); - // assert(orderRecords.length - lastOrderRecordsLength == 7); - lastOrderRecordsLength = orderRecords2.length; - - // const fs = require('fs'); - // // Custom replacer function to convert BN values to numerical representation - // const replacer = (key, value) => { - // if (value instanceof BN) { - // return value.toString(10); // Convert BN to base-10 string - // } - // return value; - // }; - // const jsonOrderRecords2 = JSON.stringify(orderRecords2, replacer); - - // // Write the JSON string to a file - // fs.writeFile('orderRecords.json', jsonOrderRecords2, 'utf8', (err) => { - // if (err) { - // console.error('Error writing to JSON file:', err); - // return; - // } - // console.log('orderRecords successfully written to orderRecords.json'); - // }); - }); -}); diff --git a/tests/perpLpRiskMitigation.ts b/tests/perpLpRiskMitigation.ts deleted file mode 100644 index 6b9a0a84c8..0000000000 --- a/tests/perpLpRiskMitigation.ts +++ /dev/null @@ -1,537 +0,0 @@ -import * as web3 from '@solana/web3.js'; -import * as anchor from '@coral-xyz/anchor'; -import { Program } from '@coral-xyz/anchor'; -import { assert } from 'chai'; - -import { - TestClient, - QUOTE_PRECISION, - EventSubscriber, - PRICE_PRECISION, - PositionDirection, - ZERO, - BN, - calculateAmmReservesAfterSwap, - calculatePrice, - User, - OracleSource, - SwapDirection, - Wallet, - LPRecord, - BASE_PRECISION, - OracleGuardRails, - isVariant, - MARGIN_PRECISION, - SettlePnlMode, -} from '../sdk/src'; - -import { - initializeQuoteSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, - setFeedPriceNoProgram, - sleep, - // sleep, -} from './testHelpers'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -async function adjustOraclePostSwap(baa, swapDirection, market, context) { - const price = calculatePrice( - market.amm.baseAssetReserve, - market.amm.quoteAssetReserve, - market.amm.pegMultiplier - ); - - const [newQaa, newBaa] = calculateAmmReservesAfterSwap( - market.amm, - 'base', - baa.abs(), - swapDirection - ); - - const newPrice = calculatePrice(newBaa, newQaa, market.amm.pegMultiplier); - const _newPrice = newPrice.toNumber() / PRICE_PRECISION.toNumber(); - await setFeedPriceNoProgram(context, _newPrice, market.amm.oracle); - - console.log('price => new price', price.toString(), newPrice.toString()); - - return _newPrice; -} - -async function createNewUser( - program, - context: BankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - wallet, - bulkAccountLoader -): Promise<[TestClient, User]> { - let walletFlag = true; - if (wallet == undefined) { - const kp = new web3.Keypair(); - await context.fundKeypair(kp, 10 ** 9); - wallet = new Wallet(kp); - walletFlag = false; - } - - console.log('wallet:', walletFlag); - const usdcAta = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - context, - wallet.publicKey - ); - - const driftClient = new TestClient({ - connection: context.connection.toConnection(), - wallet: wallet, - programID: program.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0, 1, 2, 3], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos, - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - if (walletFlag) { - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - } else { - await driftClient.subscribe(); - } - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - usdcAta.publicKey - ); - - const driftClientUser = new User({ - driftClient, - userAccountPublicKey: await driftClient.getUserAccountPublicKey(), - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - driftClientUser.subscribe(); - - return [driftClient, driftClientUser]; -} - -describe('lp risk mitigation', () => { - const chProgram = anchor.workspace.Drift as Program; - - async function _viewLogs(txsig) { - bankrunContextWrapper.printTxLogs(txsig); - } - async function delay(time) { - await new Promise((resolve) => setTimeout(resolve, time)); - } - - // ammInvariant == k == x * y - const ammInitialBaseAssetReserve = new BN(10000).mul(BASE_PRECISION); - const ammInitialQuoteAssetReserve = new BN(10000).mul(BASE_PRECISION); - - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const stableAmmInitialQuoteAssetReserve = - BASE_PRECISION.mul(mantissaSqrtScale); - const stableAmmInitialBaseAssetReserve = - BASE_PRECISION.mul(mantissaSqrtScale); - - const usdcAmount = new BN(5000 * 1e6); // 2000 bucks - - let driftClient: TestClient; - let eventSubscriber: EventSubscriber; - - let bulkAccountLoader: TestBulkAccountLoader; - - let bankrunContextWrapper: BankrunContextWrapper; - - let usdcMint: web3.Keypair; - - let driftClientUser: User; - let traderDriftClient: TestClient; - let traderDriftClientUser: User; - - let poorDriftClient: TestClient; - let poorDriftClientUser: User; - - let solusdc; - let solusdc2; - let solusdc3; - let btcusdc; - - before(async () => { - const context = await startAnchor('', [], []); - - 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); - - solusdc3 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc2 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - btcusdc = await mockOracleNoProgram(bankrunContextWrapper, 26069, -7); - - const oracleInfos = [ - { publicKey: solusdc, source: OracleSource.PYTH }, - { publicKey: solusdc2, source: OracleSource.PYTH }, - { publicKey: solusdc3, source: OracleSource.PYTH }, - { publicKey: btcusdc, source: OracleSource.PYTH }, - ]; - - [driftClient, driftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - bankrunContextWrapper.provider.wallet, - bulkAccountLoader - ); - // used for trading / taking on baa - await driftClient.initializePerpMarket( - 0, - solusdc, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - new BN(60 * 60) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpMarketMaxFillReserveFraction(0, 1); - - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: new BN(1000000), - oracleTwap5MinPercentDivergence: new BN(1000000), - }, - validity: { - slotsBeforeStaleForAmm: new BN(10), - slotsBeforeStaleForMargin: new BN(10), - confidenceIntervalMaxSize: new BN(100), - tooVolatileRatio: new BN(100), - }, - }; - await driftClient.updateOracleGuardRails(oracleGuardRails); - - // await driftClient.updateMarketBaseAssetAmountStepSize( - // new BN(0), - // new BN(1) - // ); - - // second market -- used for funding .. - await driftClient.initializePerpMarket( - 1, - solusdc2, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - // third market - await driftClient.initializePerpMarket( - 2, - solusdc3, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - - // third market - await driftClient.initializePerpMarket( - 3, - btcusdc, - stableAmmInitialBaseAssetReserve.div(new BN(1000)), - stableAmmInitialQuoteAssetReserve.div(new BN(1000)), - new BN(0), - new BN(26690 * 1000) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpAuctionDuration(new BN(0)); - - [traderDriftClient, traderDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - undefined, - bulkAccountLoader - ); - await traderDriftClient.updateUserAdvancedLp([ - { - advancedLp: true, - subAccountId: 0, - }, - ]); - [poorDriftClient, poorDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - QUOTE_PRECISION.mul(new BN(10000)), - oracleInfos, - undefined, - bulkAccountLoader - ); - await poorDriftClient.updateUserAdvancedLp([ - { - advancedLp: true, - subAccountId: 0, - }, - ]); - }); - - after(async () => { - await eventSubscriber.unsubscribe(); - - await driftClient.unsubscribe(); - await driftClientUser.unsubscribe(); - - await traderDriftClient.unsubscribe(); - await traderDriftClientUser.unsubscribe(); - - await poorDriftClient.unsubscribe(); - await poorDriftClientUser.unsubscribe(); - }); - - const lpCooldown = 1; - it('perp risk mitigation', async () => { - const marketIndex = 0; - console.log('adding liquidity...'); - await driftClient.updatePerpMarketTargetBaseAssetAmountPerLp( - marketIndex, - BASE_PRECISION.toNumber() - ); - sleep(1200); - await driftClient.fetchAccounts(); - let market = driftClient.getPerpMarketAccount(marketIndex); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString(), - 'target:', - market.amm.targetBaseAssetAmountPerLp - ); - assert(market.amm.sqrtK.eq(new BN('10000000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - // assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - - const _sig = await driftClient.addPerpLpShares( - new BN(1000 * BASE_PRECISION.toNumber()), - market.marketIndex - ); - await delay(lpCooldown + 1000); - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.sqrtK:', - market.amm.userLpShares.toString(), - '/', - market.amm.sqrtK.toString() - ); - assert(market.amm.sqrtK.eq(new BN('11000000000000'))); - assert(market.amm.baseAssetAmountPerLp.eq(ZERO)); - assert(market.amm.targetBaseAssetAmountPerLp == BASE_PRECISION.toNumber()); - - let user = await driftClientUser.getUserAccount(); - assert(user.perpPositions[0].lpShares.toString() == '1000000000000'); // 1000 * 1e9 - - // lp goes short - const tradeSize = new BN(500 * BASE_PRECISION.toNumber()); - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await driftClient.openPosition( - PositionDirection.SHORT, - tradeSize, - market.marketIndex, - new BN(0.1 * PRICE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('45454545'))); - await driftClientUser.fetchAccounts(); - await driftClient.accountSubscriber.setSpotOracleMap(); - - console.log( - 'driftClientUser.getFreeCollateral()=', - driftClientUser.getFreeCollateral().toString() - ); - assert(driftClientUser.getFreeCollateral().eq(new BN('4761073597'))); - // some user goes long (lp should get more short) - try { - await adjustOraclePostSwap( - tradeSize, - SwapDirection.REMOVE, - market, - bankrunContextWrapper - ); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - // new BN(100 * BASE_PRECISION.toNumber()) - ); - await _viewLogs(_txsig); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - market = driftClient.getPerpMarketAccount(0); - console.log( - 'market.amm.baseAssetAmountPerLp:', - market.amm.baseAssetAmountPerLp.toString() - ); - assert(market.amm.baseAssetAmountPerLp.eq(new BN('0'))); - console.log( - 'market.amm.baseAssetAmountWithAmm:', - market.amm.baseAssetAmountWithAmm.toString() - ); - assert(market.amm.baseAssetAmountWithAmm.eq(new BN('0'))); - - const trader = await traderDriftClient.getUserAccount(); - console.log( - 'trader size', - trader.perpPositions[0].baseAssetAmount.toString() - ); - - await driftClientUser.fetchAccounts(); - const [userPos, dustBase, sdkPnl] = - driftClientUser.getPerpPositionWithLPSettle(0); - - console.log('baseAssetAmount:', userPos.baseAssetAmount.toString()); - console.log('dustBase:', dustBase.toString()); - - console.log('settling...'); - try { - const _txsigg = await driftClient.settlePNL( - await driftClient.getUserAccountPublicKey(), - await driftClient.getUserAccount(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - user = driftClientUser.getUserAccount(); - - const settleLiquidityRecord: LPRecord = - eventSubscriber.getEventsArray('LPRecord')[0]; - - console.log( - 'settle pnl vs sdk', - settleLiquidityRecord.pnl.toString(), - sdkPnl.toString() - ); - assert(settleLiquidityRecord.pnl.eq(sdkPnl)); - - const perpLiqPrice = driftClientUser.liquidationPrice(0); - console.log('perpLiqPrice:', perpLiqPrice.toString()); - - await setFeedPriceNoProgram(bankrunContextWrapper, 8, solusdc); - console.log('settling...'); - try { - const _txsigg = await driftClient.settlePNL( - await driftClient.getUserAccountPublicKey(), - await driftClient.getUserAccount(), - 0 - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - - await driftClient.updateUserCustomMarginRatio([ - { - marginRatio: MARGIN_PRECISION.toNumber(), - subAccountId: 0, - }, - ]); - - await sleep(1000); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - console.log( - 'driftClientUser.getUserAccount().openOrders=', - driftClientUser.getUserAccount().openOrders - ); - assert(driftClientUser.getUserAccount().openOrders == 0); - - console.log('settling after margin ratio update...'); - try { - const _txsigg = await driftClient.settleMultiplePNLs( - await driftClient.getUserAccountPublicKey(), - await driftClient.getUserAccount(), - [0], - SettlePnlMode.TRY_SETTLE - ); - await _viewLogs(_txsigg); - } catch (e) { - console.log(e); - } - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - const afterReduceOrdersAccount = driftClientUser.getUserAccount(); - assert(afterReduceOrdersAccount.openOrders == 1); - - const leOrder = afterReduceOrdersAccount.orders[0]; - console.log(leOrder); - assert(leOrder.auctionDuration == 80); - assert(leOrder.auctionStartPrice.lt(leOrder.auctionEndPrice)); - assert(leOrder.auctionEndPrice.gt(ZERO)); - assert(leOrder.reduceOnly); - assert(!leOrder.postOnly); - assert(leOrder.marketIndex == 0); - assert(leOrder.baseAssetAmount.eq(new BN('500000000000'))); - assert(isVariant(leOrder.direction, 'long')); - assert(isVariant(leOrder.existingPositionDirection, 'short')); - - const afterReduceShares = - afterReduceOrdersAccount.perpPositions[0].lpShares; - - console.log('afterReduceShares=', afterReduceShares.toString()); - assert(afterReduceShares.lt(new BN(1000 * BASE_PRECISION.toNumber()))); - assert(afterReduceShares.eq(new BN('400000000000'))); - }); -}); diff --git a/tests/switchboardTxCus.ts b/tests/switchboardTxCus.ts index fab886314d..40e6a33277 100644 --- a/tests/switchboardTxCus.ts +++ b/tests/switchboardTxCus.ts @@ -60,7 +60,6 @@ describe('switchboard place orders cus', () => { ); const usdcAmount = new BN(10 * 10 ** 6); - const nLpShares = new BN(10000000); let oracle: PublicKey; const numMkts = 8; @@ -152,8 +151,6 @@ describe('switchboard place orders cus', () => { i, new BN(0) ); - - await driftClient.addPerpLpShares(nLpShares.divn(numMkts * 4), i); } await bankrunContextWrapper.fundKeypair(traderKeyPair, 10 ** 9); diff --git a/tests/tradingLP.ts b/tests/tradingLP.ts deleted file mode 100644 index 565178183e..0000000000 --- a/tests/tradingLP.ts +++ /dev/null @@ -1,281 +0,0 @@ -import * as anchor from '@coral-xyz/anchor'; -import { assert } from 'chai'; -import { BN, User, OracleSource, Wallet, MARGIN_PRECISION } from '../sdk'; - -import { Program } from '@coral-xyz/anchor'; - -import * as web3 from '@solana/web3.js'; - -import { - TestClient, - PRICE_PRECISION, - PositionDirection, - ZERO, - OracleGuardRails, -} from '../sdk/src'; - -import { - initializeQuoteSpotMarket, - mockOracleNoProgram, - mockUSDCMint, - mockUserUSDCAccount, -} from './testHelpers'; -import { startAnchor } from 'solana-bankrun'; -import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; -import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; - -async function createNewUser( - program, - context: BankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - wallet, - bulkAccountLoader -) { - let walletFlag = true; - if (wallet == undefined) { - const kp = new web3.Keypair(); - await context.fundKeypair(kp, 10 ** 9); - wallet = new Wallet(kp); - walletFlag = false; - } - - console.log('wallet:', walletFlag); - const usdcAta = await mockUserUSDCAccount( - usdcMint, - usdcAmount, - context, - wallet.publicKey - ); - - const driftClient = new TestClient({ - connection: context.connection.toConnection(), - wallet: wallet, - programID: program.programId, - opts: { - commitment: 'confirmed', - }, - activeSubAccountId: 0, - perpMarketIndexes: [0, 1], - spotMarketIndexes: [0], - subAccountIds: [], - oracleInfos, - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - - if (walletFlag) { - await driftClient.initialize(usdcMint.publicKey, true); - await driftClient.subscribe(); - await initializeQuoteSpotMarket(driftClient, usdcMint.publicKey); - } else { - await driftClient.subscribe(); - } - - await driftClient.initializeUserAccountAndDepositCollateral( - usdcAmount, - usdcAta.publicKey - ); - - const driftClientUser = new User({ - // @ts-ignore - driftClient, - userAccountPublicKey: await driftClient.getUserAccountPublicKey(), - accountSubscription: { - type: 'polling', - accountLoader: bulkAccountLoader, - }, - }); - driftClientUser.subscribe(); - - return [driftClient, driftClientUser]; -} - -describe('trading liquidity providing', () => { - const chProgram = anchor.workspace.Drift as Program; - - // ammInvariant == k == x * y - const ammInitialBaseAssetReserve = new BN(300).mul(new BN(1e13)); - const ammInitialQuoteAssetReserve = new BN(300).mul(new BN(1e13)); - - const mantissaSqrtScale = new BN(Math.sqrt(PRICE_PRECISION.toNumber())); - const stableAmmInitialQuoteAssetReserve = new anchor.BN(1 * 10 ** 13).mul( - mantissaSqrtScale - ); - const stableAmmInitialBaseAssetReserve = new anchor.BN(1 * 10 ** 13).mul( - mantissaSqrtScale - ); - - const usdcAmount = new BN(1_000_000_000 * 1e6); - - let driftClient: TestClient; - - let bulkAccountLoader: TestBulkAccountLoader; - - let bankrunContextWrapper: BankrunContextWrapper; - - let usdcMint: web3.Keypair; - - let driftClientUser: User; - let traderDriftClient: TestClient; - let traderDriftClientUser: User; - - let solusdc; - let solusdc2; - - before(async () => { - const context = await startAnchor('', [], []); - - bankrunContextWrapper = new BankrunContextWrapper(context); - - bulkAccountLoader = new TestBulkAccountLoader( - bankrunContextWrapper.connection, - 'processed', - 1 - ); - - usdcMint = await mockUSDCMint(bankrunContextWrapper); - - solusdc2 = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - solusdc = await mockOracleNoProgram(bankrunContextWrapper, 1, -7); // make invalid - const oracleInfos = [ - { publicKey: solusdc, source: OracleSource.PYTH }, - { publicKey: solusdc2, source: OracleSource.PYTH }, - ]; - [driftClient, driftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - bankrunContextWrapper.provider.wallet, - bulkAccountLoader - ); - // used for trading / taking on baa - await driftClient.initializePerpMarket( - 0, - solusdc, - ammInitialBaseAssetReserve, - ammInitialQuoteAssetReserve, - new BN(60 * 60) - ); - await driftClient.updateLpCooldownTime(new BN(0)); - await driftClient.updatePerpMarketMaxFillReserveFraction(0, 1); - await driftClient.updatePerpMarketStepSizeAndTickSize( - 0, - new BN(1), - new BN(1) - ); - const oracleGuardRails: OracleGuardRails = { - priceDivergence: { - markOraclePercentDivergence: new BN(1000000), - oracleTwap5MinPercentDivergence: new BN(1000000), - }, - validity: { - slotsBeforeStaleForAmm: new BN(10), - slotsBeforeStaleForMargin: new BN(10), - confidenceIntervalMaxSize: new BN(100), - tooVolatileRatio: new BN(100), - }, - }; - await driftClient.updateOracleGuardRails(oracleGuardRails); - - // second market -- used for funding .. - await driftClient.initializePerpMarket( - 1, - solusdc2, - stableAmmInitialBaseAssetReserve, - stableAmmInitialQuoteAssetReserve, - new BN(0) - ); - await driftClient.updatePerpAuctionDuration(new BN(0)); - await driftClient.updatePerpMarketMarginRatio( - 0, - MARGIN_PRECISION.toNumber() / 2, - MARGIN_PRECISION.toNumber() / 4 - ); - - [traderDriftClient, traderDriftClientUser] = await createNewUser( - chProgram, - bankrunContextWrapper, - usdcMint, - usdcAmount, - oracleInfos, - undefined, - bulkAccountLoader - ); - }); - - after(async () => { - await driftClient.unsubscribe(); - await driftClientUser.unsubscribe(); - - await traderDriftClient.unsubscribe(); - await traderDriftClientUser.unsubscribe(); - }); - - it('lp trades with short', async () => { - let market = driftClient.getPerpMarketAccount(0); - - console.log('adding liquidity...'); - const _sig = await driftClient.addPerpLpShares( - new BN(100 * 1e13), - market.marketIndex - ); - - // some user goes long (lp should get a short) - console.log('user trading...'); - const tradeSize = new BN(40 * 1e13); - const _txsig = await traderDriftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - ); - - await traderDriftClient.fetchAccounts(); - const position = traderDriftClient.getUserAccount().perpPositions[0]; - console.log( - 'trader position:', - position.baseAssetAmount.toString(), - position.quoteAssetAmount.toString() - ); - assert(position.baseAssetAmount.gt(ZERO)); - - // settle says the lp would take on a short - const lpPosition = driftClientUser.getPerpPositionWithLPSettle(0)[0]; - console.log( - 'sdk settled lp position:', - lpPosition.baseAssetAmount.toString(), - lpPosition.quoteAssetAmount.toString() - ); - assert(lpPosition.baseAssetAmount.lt(ZERO)); - assert(lpPosition.quoteAssetAmount.gt(ZERO)); - - // lp trades a big long - await driftClient.openPosition( - PositionDirection.LONG, - tradeSize, - market.marketIndex - ); - await driftClient.fetchAccounts(); - await driftClientUser.fetchAccounts(); - - // lp now has a long - const newLpPosition = driftClientUser.getUserAccount().perpPositions[0]; - console.log( - 'lp position:', - newLpPosition.baseAssetAmount.toString(), - newLpPosition.quoteAssetAmount.toString() - ); - assert(newLpPosition.baseAssetAmount.gt(ZERO)); - assert(newLpPosition.quoteAssetAmount.lt(ZERO)); - // is still an lp - assert(newLpPosition.lpShares.gt(ZERO)); - market = driftClient.getPerpMarketAccount(0); - - console.log('done!'); - }); -}); From 829f5526d4ef3e89f5ffd74f5c88e44490b58c59 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 19:40:43 +0000 Subject: [PATCH 152/216] sdk: release v2.137.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 756b9c2950..59e18f3b3d 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.0 \ No newline at end of file +2.137.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 95e2053984..1d8bb437c9 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.0", + "version": "2.137.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 77d76ebb1b9876e7be89d30da457deff427c7714 Mon Sep 17 00:00:00 2001 From: wphan Date: Thu, 4 Sep 2025 19:24:19 -0700 Subject: [PATCH 153/216] bump From 6b4fe1a2cd6f2595993f09b676e32151ee55aede Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 8 Sep 2025 12:24:57 -0700 Subject: [PATCH 154/216] program: add swift message padding (#1845) * program: add updated swift message * add tests * update tests * CHANGELOG --------- Co-authored-by: Chris Heaney --- CHANGELOG.md | 1 + .../drift/src/validation/sig_verification.rs | 105 ++++++---- .../src/validation/sig_verification/tests.rs | 184 ++++++++++++++++++ 3 files changed, 251 insertions(+), 39 deletions(-) create mode 100644 programs/drift/src/validation/sig_verification/tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d77bbb0f4..eb573c0cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: add padding to swift messages ([#1845](https://github.com/drift-labs/protocol-v2/pull/1845)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) ### Fixes diff --git a/programs/drift/src/validation/sig_verification.rs b/programs/drift/src/validation/sig_verification.rs index 3349c7d5d7..da3893e66d 100644 --- a/programs/drift/src/validation/sig_verification.rs +++ b/programs/drift/src/validation/sig_verification.rs @@ -14,6 +14,9 @@ use solana_program::program_memory::sol_memcmp; use solana_program::sysvar; use std::convert::TryInto; +#[cfg(test)] +mod tests; + const ED25519_PROGRAM_INPUT_HEADER_LEN: usize = 2; const SIGNATURE_LEN: u16 = 64; @@ -45,6 +48,7 @@ pub struct Ed25519SignatureOffsets { pub message_instruction_index: u16, } +#[derive(Debug)] pub struct VerifiedMessage { pub signed_msg_order_params: OrderParams, pub sub_account_id: Option, @@ -60,6 +64,67 @@ fn slice_eq(a: &[u8], b: &[u8]) -> bool { a.len() == b.len() && sol_memcmp(a, b, a.len()) == 0 } +pub fn deserialize_into_verified_message( + payload: Vec, + signature: &[u8; 64], + is_delegate_signer: bool, +) -> Result { + if is_delegate_signer { + if payload.len() < 8 { + return Err(SignatureVerificationError::InvalidMessageDataSize.into()); + } + let min_len: usize = std::mem::size_of::(); + let mut owned = payload; + if owned.len() < min_len { + owned.resize(min_len, 0); + } + let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize( + &mut &owned[8..], // 8 byte manual discriminator + ) + .map_err(|_| { + msg!("Invalid message encoding for is_delegate_signer = true"); + SignatureVerificationError::InvalidMessageDataSize + })?; + + return Ok(VerifiedMessage { + signed_msg_order_params: deserialized.signed_msg_order_params, + sub_account_id: None, + delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey), + slot: deserialized.slot, + uuid: deserialized.uuid, + take_profit_order_params: deserialized.take_profit_order_params, + stop_loss_order_params: deserialized.stop_loss_order_params, + signature: *signature, + }); + } else { + if payload.len() < 8 { + return Err(SignatureVerificationError::InvalidMessageDataSize.into()); + } + let min_len: usize = std::mem::size_of::(); + let mut owned = payload; + if owned.len() < min_len { + owned.resize(min_len, 0); + } + let deserialized = SignedMsgOrderParamsMessage::deserialize( + &mut &owned[8..], // 8 byte manual discriminator + ) + .map_err(|_| { + msg!("Invalid delegate message encoding for with is_delegate_signer = false"); + SignatureVerificationError::InvalidMessageDataSize + })?; + return Ok(VerifiedMessage { + signed_msg_order_params: deserialized.signed_msg_order_params, + sub_account_id: Some(deserialized.sub_account_id), + delegate_signed_taker_pubkey: None, + slot: deserialized.slot, + uuid: deserialized.uuid, + take_profit_order_params: deserialized.take_profit_order_params, + stop_loss_order_params: deserialized.stop_loss_order_params, + signature: *signature, + }); + } +} + /// Check Ed25519Program instruction data verifies the given msg /// /// `ix` an Ed25519Program instruction [see](https://github.com/solana-labs/solana/blob/master/sdk/src/ed25519_instruction.rs)) @@ -232,45 +297,7 @@ pub fn verify_and_decode_ed25519_msg( let payload = hex::decode(payload).map_err(|_| SignatureVerificationError::InvalidMessageHex)?; - if is_delegate_signer { - let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize( - &mut &payload[8..], // 8 byte manual discriminator - ) - .map_err(|_| { - msg!("Invalid message encoding for is_delegate_signer = true"); - SignatureVerificationError::InvalidMessageDataSize - })?; - - return Ok(VerifiedMessage { - signed_msg_order_params: deserialized.signed_msg_order_params, - sub_account_id: None, - delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey), - slot: deserialized.slot, - uuid: deserialized.uuid, - take_profit_order_params: deserialized.take_profit_order_params, - stop_loss_order_params: deserialized.stop_loss_order_params, - signature: *signature, - }); - } else { - let deserialized = SignedMsgOrderParamsMessage::deserialize( - &mut &payload[8..], // 8 byte manual discriminator - ) - .map_err(|_| { - msg!("Invalid delegate message encoding for with is_delegate_signer = false"); - SignatureVerificationError::InvalidMessageDataSize - })?; - - return Ok(VerifiedMessage { - signed_msg_order_params: deserialized.signed_msg_order_params, - sub_account_id: Some(deserialized.sub_account_id), - delegate_signed_taker_pubkey: None, - slot: deserialized.slot, - uuid: deserialized.uuid, - take_profit_order_params: deserialized.take_profit_order_params, - stop_loss_order_params: deserialized.stop_loss_order_params, - signature: *signature, - }); - } + deserialize_into_verified_message(payload, signature, is_delegate_signer) } #[error_code] diff --git a/programs/drift/src/validation/sig_verification/tests.rs b/programs/drift/src/validation/sig_verification/tests.rs new file mode 100644 index 0000000000..e0f941d883 --- /dev/null +++ b/programs/drift/src/validation/sig_verification/tests.rs @@ -0,0 +1,184 @@ +mod sig_verification { + use std::str::FromStr; + + use anchor_lang::prelude::Pubkey; + + use crate::controller::position::PositionDirection; + use crate::validation::sig_verification::deserialize_into_verified_message; + + #[test] + fn test_deserialize_into_verified_message_non_delegate() { + let signature = [1u8; 64]; + let payload = vec![ + 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 1, 0, 202, 154, 59, 0, 0, 0, 0, 0, 248, + 89, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 192, 181, 74, 13, 0, 0, 0, 0, + 1, 0, 248, 89, 13, 0, 0, 0, 0, 0, 0, 232, 3, 0, 0, 0, 0, 0, 0, 72, 112, 54, 84, 106, + 83, 48, 107 + ]; + + // Test deserialization with non-delegate signer + let result = deserialize_into_verified_message(payload, &signature, false); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, Some(0)); + assert_eq!(verified_message.delegate_signed_taker_pubkey, None); + assert_eq!(verified_message.slot, 1000); + assert_eq!(verified_message.uuid, [72, 112, 54, 84, 106, 83, 48, 107]); + assert!(verified_message.take_profit_order_params.is_none()); + assert!(verified_message.stop_loss_order_params.is_none()); + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 1); + assert_eq!(order_params.direction, PositionDirection::Long); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 224000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(223000000i64)); + assert_eq!(order_params.auction_end_price, Some(224000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_non_delegate_with_tpsl() { + let signature = [1u8; 64]; + let payload = vec![ + 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 3, 0, 96, 254, 205, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, + 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, + 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, + 0, 0, 0, 0, 0, 96, 254, 205 + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, false); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, Some(2)); + assert_eq!(verified_message.delegate_signed_taker_pubkey, None); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 3456000000u64); + assert_eq!(tp.trigger_price, 240000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 3456000000u64); + assert_eq!(sl.trigger_price, 225000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 3); + assert_eq!(order_params.direction, PositionDirection::Long); + assert_eq!(order_params.base_asset_amount, 3456000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(230000000i64)); + assert_eq!(order_params.auction_end_price, Some(237000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_delegate() { + let signature = [1u8; 64]; + let payload = vec![ + 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, + 128, 151, 47, 14, 0, 0, 0, 0, 242, 208, 117, 159, 92, 135, 34, 224, 147, 14, 64, 92, 7, + 25, 145, 237, 79, 35, 72, 24, 140, 13, 25, 189, 134, 243, 232, 5, 89, 37, 166, 242, 41, + 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49 + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, true); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, None); + assert_eq!( + verified_message.delegate_signed_taker_pubkey, + Some(Pubkey::from_str("HLr2UfL422cakKkaBG4z1bMZrcyhmzX2pHdegjM6fYXB").unwrap()) + ); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.take_profit_order_params.is_none()); + assert!(verified_message.stop_loss_order_params.is_none()); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 2); + assert_eq!(order_params.direction, PositionDirection::Short); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(240000000i64)); + assert_eq!(order_params.auction_end_price, Some(238000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_delegate_with_tpsl() { + let signature = [1u8; 64]; + let payload = vec![ + 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, + 128, 151, 47, 14, 0, 0, 0, 0, 241, 148, 164, 10, 232, 65, 33, 157, 18, 12, 251, 132, + 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, + 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, + 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, + 59 + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, true); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, None); + assert_eq!( + verified_message.delegate_signed_taker_pubkey, + Some(Pubkey::from_str("HG2iQKnRkkasrLptwMZewV6wT7KPstw9wkA8yyu8Nx3m").unwrap()) + ); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 1000000000u64); + assert_eq!(tp.trigger_price, 230000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 1000000000u64); + assert_eq!(sl.trigger_price, 250000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 2); + assert_eq!(order_params.direction, PositionDirection::Short); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(240000000i64)); + assert_eq!(order_params.auction_end_price, Some(238000000i64)); + } +} From 28f9032414575b10b39c31ecb0e6c4e3a4d1ba98 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 8 Sep 2025 15:37:56 -0400 Subject: [PATCH 155/216] program: add max margin ratio per position (#1847) * program: add max margin ratio perp position * program: test for custom perp position margin ratio * test * make max margin ratio persist * update user level max margin ratio * naming nit * CHANGELOG --- CHANGELOG.md | 1 + programs/drift/src/controller/position.rs | 11 + programs/drift/src/instructions/keeper.rs | 13 +- programs/drift/src/instructions/user.rs | 29 +++ programs/drift/src/lib.rs | 9 + programs/drift/src/math/margin.rs | 46 +++- programs/drift/src/math/margin/tests.rs | 273 ++++++++++++++++++++++ programs/drift/src/state/user.rs | 20 +- programs/drift/src/state/user/tests.rs | 31 +++ 9 files changed, 419 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb573c0cac..acf18ce56f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) - program: add padding to swift messages ([#1845](https://github.com/drift-labs/protocol-v2/pull/1845)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) diff --git a/programs/drift/src/controller/position.rs b/programs/drift/src/controller/position.rs index 97add169ea..6b6ec530d5 100644 --- a/programs/drift/src/controller/position.rs +++ b/programs/drift/src/controller/position.rs @@ -49,8 +49,19 @@ pub fn add_new_position( .position(|market_position| market_position.is_available()) .ok_or(ErrorCode::MaxNumberOfPositions)?; + let max_margin_ratio = { + let old_position = &user_positions[new_position_index]; + + if old_position.market_index == market_index { + old_position.max_margin_ratio + } else { + 0_u16 + } + }; + let new_market_position = PerpPosition { market_index, + max_margin_ratio, ..PerpPosition::default() }; diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 1d29f0d46c..2bf1a003a1 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -1,4 +1,5 @@ use std::cell::RefMut; +use std::collections::BTreeMap; use std::convert::TryFrom; use anchor_lang::prelude::*; @@ -26,6 +27,7 @@ use crate::instructions::constraints::*; use crate::instructions::optional_accounts::{load_maps, AccountMaps}; use crate::math::casting::Cast; use crate::math::constants::QUOTE_SPOT_MARKET_INDEX; +use crate::math::margin::get_margin_calculation_for_disable_high_leverage_mode; use crate::math::margin::{calculate_user_equity, meets_settle_pnl_maintenance_margin_requirement}; use crate::math::orders::{estimate_price_from_side, find_bids_and_asks_from_users}; use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price; @@ -2803,20 +2805,13 @@ pub fn handle_disable_user_high_leverage_mode<'c: 'info, 'info>( } } - let custom_margin_ratio_before = user.max_margin_ratio; - user.max_margin_ratio = 0; - - let margin_calc = calculate_margin_requirement_and_total_collateral_and_liability_info( - &user, + let margin_calc = get_margin_calculation_for_disable_high_leverage_mode( + &mut user, &perp_market_map, &spot_market_map, &mut oracle_map, - MarginContext::standard(MarginRequirementType::Initial) - .margin_buffer(MARGIN_PRECISION / 100), // 1% buffer )?; - user.max_margin_ratio = custom_margin_ratio_before; - if margin_calc.num_perp_liabilities > 0 { let mut requires_invariant_check = false; diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 86229bc1bd..7528413700 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -2945,6 +2945,20 @@ pub fn handle_update_user_custom_margin_ratio( Ok(()) } +pub fn handle_update_user_perp_position_custom_margin_ratio( + ctx: Context, + _sub_account_id: u16, + perp_market_index: u16, + margin_ratio: u16, +) -> Result<()> { + let mut user = load_mut!(ctx.accounts.user)?; + + user.update_perp_position_max_margin_ratio(perp_market_index, margin_ratio)?; + + Ok(()) +} + + pub fn handle_update_user_margin_trading_enabled<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateUser<'info>>, _sub_account_id: u16, @@ -4432,6 +4446,21 @@ pub struct UpdateUser<'info> { pub authority: Signer<'info>, } +#[derive(Accounts)] +#[instruction( + sub_account_id: u16, +)] +pub struct UpdateUserPerpPositionCustomMarginRatio<'info> { + #[account( + mut, + seeds = [b"user", authority.key.as_ref(), sub_account_id.to_le_bytes().as_ref()], + bump, + constraint = can_sign_for_user(&user, &authority)? + )] + pub user: AccountLoader<'info, User>, + pub authority: Signer<'info>, +} + #[derive(Accounts)] pub struct DeleteUser<'info> { #[account( diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 9a14a80e59..964a7851ca 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -367,6 +367,15 @@ pub mod drift { handle_update_user_custom_margin_ratio(ctx, _sub_account_id, margin_ratio) } + pub fn update_user_perp_position_custom_margin_ratio( + ctx: Context, + _sub_account_id: u16, + perp_market_index: u16, + margin_ratio: u16, + ) -> Result<()> { + handle_update_user_perp_position_custom_margin_ratio(ctx, _sub_account_id, perp_market_index, margin_ratio) + } + pub fn update_user_margin_trading_enabled<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateUser<'info>>, _sub_account_id: u16, diff --git a/programs/drift/src/math/margin.rs b/programs/drift/src/math/margin.rs index f62bbb6122..38bbe29697 100644 --- a/programs/drift/src/math/margin.rs +++ b/programs/drift/src/math/margin.rs @@ -6,6 +6,7 @@ use crate::math::constants::{ }; use crate::math::position::calculate_base_asset_value_and_pnl_with_oracle_price; +use crate::MARGIN_PRECISION; use crate::{validate, PRICE_PRECISION_I128}; use crate::{validation, PRICE_PRECISION_I64}; @@ -27,6 +28,7 @@ use crate::state::spot_market_map::SpotMarketMap; use crate::state::user::{MarketType, OrderFillSimulation, PerpPosition, User}; use num_integer::Roots; use std::cmp::{max, min, Ordering}; +use std::collections::BTreeMap; #[cfg(test)] mod tests; @@ -535,6 +537,12 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( 0, )?; + let perp_position_custom_margin_ratio = if context.margin_type == MarginRequirementType::Initial { + market_position.max_margin_ratio as u32 + } else { + 0_u32 + }; + let ( perp_margin_requirement, weighted_pnl, @@ -547,7 +555,7 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( oracle_price_data, &strict_quote_price, context.margin_type, - user_custom_margin_ratio, + user_custom_margin_ratio.max(perp_position_custom_margin_ratio), user_high_leverage_mode, calculation.track_open_orders_fraction(), )?; @@ -884,6 +892,42 @@ pub fn validate_spot_margin_trading( Ok(()) } +pub fn get_margin_calculation_for_disable_high_leverage_mode( + user: &mut User, + perp_market_map: &PerpMarketMap, + spot_market_map: &SpotMarketMap, + oracle_map: &mut OracleMap, +) -> DriftResult { + let custom_margin_ratio_before = user.max_margin_ratio; + + + let mut perp_position_max_margin_ratio_map = BTreeMap::new(); + for (index, position) in user.perp_positions.iter_mut().enumerate() { + if position.max_margin_ratio == 0 { + continue; + } + + perp_position_max_margin_ratio_map.insert(index, position.max_margin_ratio); + position.max_margin_ratio = 0; + } + + let margin_buffer = MARGIN_PRECISION / 100; // 1% buffer + let margin_calc = calculate_margin_requirement_and_total_collateral_and_liability_info( + user, + perp_market_map, + spot_market_map, + oracle_map, + MarginContext::standard(MarginRequirementType::Initial).margin_buffer(margin_buffer), + )?; + + user.max_margin_ratio = custom_margin_ratio_before; + for (index, perp_position_max_margin_ratio) in perp_position_max_margin_ratio_map.iter() { + user.perp_positions[*index].max_margin_ratio = *perp_position_max_margin_ratio; + } + + Ok(margin_calc) +} + pub fn calculate_user_equity( user: &User, perp_market_map: &PerpMarketMap, diff --git a/programs/drift/src/math/margin/tests.rs b/programs/drift/src/math/margin/tests.rs index 7a256b65db..edfcf49984 100644 --- a/programs/drift/src/math/margin/tests.rs +++ b/programs/drift/src/math/margin/tests.rs @@ -1082,6 +1082,132 @@ mod calculate_margin_requirement_and_total_collateral { assert_eq!(total_collateral, 5000000000); // 100 * $100 * .5 } + #[test] + pub fn user_perp_positions_custom_margin_ratio() { + let slot = 0_u64; + + let mut sol_oracle_price = get_pyth_price(100, 6); + let sol_oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + sol_oracle_price, + &sol_oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + + let mut market = PerpMarket { + amm: AMM { + base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, + bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, + sqrt_k: 100 * AMM_RESERVE_PRECISION, + peg_multiplier: 100 * PEG_PRECISION, + order_step_size: 10000000, + oracle: sol_oracle_price_key, + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + status: MarketStatus::Initialized, + ..PerpMarket::default() + }; + create_anchor_account_info!(market, PerpMarket, market_account_info); + let perp_market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + + let mut usdc_spot_market = SpotMarket { + market_index: 0, + oracle_source: OracleSource::QuoteAsset, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 6, + initial_asset_weight: SPOT_WEIGHT_PRECISION, + maintenance_asset_weight: SPOT_WEIGHT_PRECISION, + deposit_balance: 10000 * SPOT_BALANCE_PRECISION, + liquidator_fee: 0, + historical_oracle_data: HistoricalOracleData::default_quote_oracle(), + ..SpotMarket::default() + }; + create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info); + let mut sol_spot_market = SpotMarket { + market_index: 1, + oracle_source: OracleSource::Pyth, + oracle: sol_oracle_price_key, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 9, + initial_asset_weight: 8 * SPOT_WEIGHT_PRECISION / 10, + maintenance_asset_weight: 9 * SPOT_WEIGHT_PRECISION / 10, + initial_liability_weight: 12 * SPOT_WEIGHT_PRECISION / 10, + maintenance_liability_weight: 11 * SPOT_WEIGHT_PRECISION / 10, + liquidator_fee: LIQUIDATION_FEE_PRECISION / 1000, + ..SpotMarket::default() + }; + create_anchor_account_info!(sol_spot_market, SpotMarket, sol_spot_market_account_info); + let spot_market_account_infos = Vec::from([ + &usdc_spot_market_account_info, + &sol_spot_market_account_info, + ]); + let spot_market_map = + SpotMarketMap::load_multiple(spot_market_account_infos, true).unwrap(); + + let mut spot_positions = [SpotPosition::default(); 8]; + spot_positions[0] = SpotPosition { + market_index: 0, + balance_type: SpotBalanceType::Deposit, + scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, + ..SpotPosition::default() + }; + + let user = User { + orders: [Order::default(); 32], + perp_positions: get_positions(PerpPosition { + market_index: 0, + base_asset_amount: 100 * BASE_PRECISION_I64, + max_margin_ratio: 2 * MARGIN_PRECISION as u16, // .5x leverage + ..PerpPosition::default() + }), + spot_positions, + ..User::default() + }; + + let MarginCalculation { + margin_requirement, .. + } = calculate_margin_requirement_and_total_collateral_and_liability_info( + &user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + MarginContext::standard(MarginRequirementType::Initial), + ) + .unwrap(); + + assert_eq!(margin_requirement, 20000000000); + + let user = User { + max_margin_ratio: 4 * MARGIN_PRECISION, // 1x leverage + ..user + }; + + let MarginCalculation { + margin_requirement, .. + } = calculate_margin_requirement_and_total_collateral_and_liability_info( + &user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + MarginContext::standard(MarginRequirementType::Initial), + ) + .unwrap(); + + // user custom margin ratio should override perp position custom margin ratio + assert_eq!(margin_requirement, 40000000000); + } + #[test] pub fn user_dust_deposit() { let slot = 0_u64; @@ -4318,3 +4444,150 @@ mod pools { assert_eq!(result.unwrap_err(), ErrorCode::InvalidPoolId) } } + +#[cfg(test)] +mod get_margin_calculation_for_disable_high_leverage_mode { + use std::str::FromStr; + + use anchor_lang::Owner; + use solana_program::pubkey::Pubkey; + + use crate::{create_account_info, MARGIN_PRECISION}; + use crate::math::constants::{ + AMM_RESERVE_PRECISION, LIQUIDATION_FEE_PRECISION, PEG_PRECISION, + SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, + SPOT_WEIGHT_PRECISION, + }; + use crate::math::margin::get_margin_calculation_for_disable_high_leverage_mode; + use crate::state::oracle::{HistoricalOracleData, OracleSource}; + use crate::state::oracle_map::OracleMap; + use crate::state::perp_market::{MarketStatus, PerpMarket, AMM}; + use crate::state::perp_market_map::PerpMarketMap; + use crate::state::spot_market::{SpotBalanceType, SpotMarket}; + use crate::state::spot_market_map::SpotMarketMap; + use crate::state::user::{Order, PerpPosition, SpotPosition, User}; + use crate::test_utils::*; + use crate::test_utils::get_pyth_price; + use crate::create_anchor_account_info; + + #[test] + pub fn check_user_not_changed() { + let slot = 0_u64; + + let mut sol_oracle_price = get_pyth_price(100, 6); + let sol_oracle_price_key = + Pubkey::from_str("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix").unwrap(); + let pyth_program = crate::ids::pyth_program::id(); + create_account_info!( + sol_oracle_price, + &sol_oracle_price_key, + &pyth_program, + oracle_account_info + ); + let mut oracle_map = OracleMap::load_one(&oracle_account_info, slot, None).unwrap(); + + let mut market = PerpMarket { + amm: AMM { + base_asset_reserve: 100 * AMM_RESERVE_PRECISION, + quote_asset_reserve: 100 * AMM_RESERVE_PRECISION, + bid_base_asset_reserve: 101 * AMM_RESERVE_PRECISION, + bid_quote_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_base_asset_reserve: 99 * AMM_RESERVE_PRECISION, + ask_quote_asset_reserve: 101 * AMM_RESERVE_PRECISION, + sqrt_k: 100 * AMM_RESERVE_PRECISION, + peg_multiplier: 100 * PEG_PRECISION, + order_step_size: 10000000, + oracle: sol_oracle_price_key, + ..AMM::default() + }, + margin_ratio_initial: 1000, + margin_ratio_maintenance: 500, + status: MarketStatus::Initialized, + ..PerpMarket::default() + }; + create_anchor_account_info!(market, PerpMarket, market_account_info); + let perp_market_map = PerpMarketMap::load_one(&market_account_info, true).unwrap(); + + let mut usdc_spot_market = SpotMarket { + market_index: 0, + oracle_source: OracleSource::QuoteAsset, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 6, + initial_asset_weight: SPOT_WEIGHT_PRECISION, + maintenance_asset_weight: SPOT_WEIGHT_PRECISION, + deposit_balance: 10000 * SPOT_BALANCE_PRECISION, + liquidator_fee: 0, + historical_oracle_data: HistoricalOracleData::default_quote_oracle(), + ..SpotMarket::default() + }; + create_anchor_account_info!(usdc_spot_market, SpotMarket, usdc_spot_market_account_info); + let mut sol_spot_market = SpotMarket { + market_index: 1, + oracle_source: OracleSource::Pyth, + oracle: sol_oracle_price_key, + cumulative_deposit_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + cumulative_borrow_interest: SPOT_CUMULATIVE_INTEREST_PRECISION, + decimals: 9, + initial_asset_weight: 8 * SPOT_WEIGHT_PRECISION / 10, + maintenance_asset_weight: 9 * SPOT_WEIGHT_PRECISION / 10, + initial_liability_weight: 12 * SPOT_WEIGHT_PRECISION / 10, + maintenance_liability_weight: 11 * SPOT_WEIGHT_PRECISION / 10, + liquidator_fee: LIQUIDATION_FEE_PRECISION / 1000, + ..SpotMarket::default() + }; + create_anchor_account_info!(sol_spot_market, SpotMarket, sol_spot_market_account_info); + let spot_market_account_infos = Vec::from([ + &usdc_spot_market_account_info, + &sol_spot_market_account_info, + ]); + let spot_market_map = + SpotMarketMap::load_multiple(spot_market_account_infos, true).unwrap(); + + let mut spot_positions = [SpotPosition::default(); 8]; + spot_positions[0] = SpotPosition { + market_index: 0, + balance_type: SpotBalanceType::Deposit, + scaled_balance: 20000 * SPOT_BALANCE_PRECISION_U64, + ..SpotPosition::default() + }; + spot_positions[1] = SpotPosition { + market_index: 1, + balance_type: SpotBalanceType::Borrow, + scaled_balance: 100 * SPOT_BALANCE_PRECISION_U64, + ..SpotPosition::default() + }; + + let mut perp_positions = [PerpPosition::default(); 8]; + perp_positions[1] = PerpPosition { + market_index: 0, + max_margin_ratio: 2 * MARGIN_PRECISION as u16, // .5x leverage + ..PerpPosition::default() + }; + perp_positions[7] = PerpPosition { + market_index: 1, + max_margin_ratio: 5 * MARGIN_PRECISION as u16, // .5x leverage + ..PerpPosition::default() + }; + + let mut user = User { + orders: [Order::default(); 32], + perp_positions, + spot_positions, + max_margin_ratio: 2 * MARGIN_PRECISION as u32, // .5x leverage + ..User::default() + }; + + let user_before = user.clone(); + + get_margin_calculation_for_disable_high_leverage_mode( + &mut user, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + ) + .unwrap(); + + // should not change user + assert_eq!(user, user_before); + } +} \ No newline at end of file diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 87dd21bab4..2a74eab772 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -618,6 +618,19 @@ impl User { return Ok(true); } + + pub fn update_perp_position_max_margin_ratio(&mut self, market_index: u16, margin_ratio: u16) -> DriftResult<()> { + if self.max_margin_ratio > margin_ratio as u32 { + msg!("user.max_margin_ratio ({}) > margin_ratio ({}), setting user.max_margin_ratio to margin_ratio", self.max_margin_ratio, margin_ratio); + self.max_margin_ratio = margin_ratio as u32; + } + + let perp_position = self.force_get_perp_position_mut(market_index)?; + msg!("perp_position.max_margin_ratio ({}) -> {}", perp_position.max_margin_ratio, margin_ratio); + perp_position.max_margin_ratio = margin_ratio; + + Ok(()) + } } pub fn derive_user_account(authority: &Pubkey, sub_account_id: u16) -> Pubkey { @@ -958,10 +971,9 @@ pub struct PerpPosition { /// Used to settle the users lp position /// precision: QUOTE_PRECISION pub last_quote_asset_amount_per_lp: i64, - /// Settling LP position can lead to a small amount of base asset being left over smaller than step size - /// This records that remainder so it can be settled later on - /// precision: BASE_PRECISION - pub remainder_base_asset_amount: i32, + pub padding: [u8; 2], + // custom max margin ratio for perp market + pub max_margin_ratio: u16, /// The market index for the perp market pub market_index: u16, /// The number of open orders diff --git a/programs/drift/src/state/user/tests.rs b/programs/drift/src/state/user/tests.rs index 4e8d4f3be7..c1de9cca59 100644 --- a/programs/drift/src/state/user/tests.rs +++ b/programs/drift/src/state/user/tests.rs @@ -2492,3 +2492,34 @@ mod update_open_bids_and_asks { assert!(order.update_open_bids_and_asks()); } } + +mod force_get_user_perp_position_mut { + use crate::state::user::{PerpPosition, PositionFlag, User}; + + #[test] + fn test() { + let mut user = User::default(); + + let perp_position = PerpPosition { + market_index: 0, + max_margin_ratio: 1, + ..PerpPosition::default() + }; + user.perp_positions[0] = perp_position; + + // if next available slot is same market index and has max margin ratio, persist it + { + let perp_position_mut = user.force_get_perp_position_mut(0).unwrap(); + assert_eq!(perp_position_mut.max_margin_ratio, 1); + } + + // if next available slot is has max margin but different market index, dont persist it + { + let perp_position_mut = user.force_get_perp_position_mut(2).unwrap(); + assert_eq!(perp_position_mut.max_margin_ratio, 0); + } + + assert_eq!(user.perp_positions[0].market_index, 2); + assert_eq!(user.perp_positions[0].max_margin_ratio, 0); + } +} From 65e30da10599b1bec476e63ca9e2a6664fcb9272 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 8 Sep 2025 16:21:38 -0400 Subject: [PATCH 156/216] tweak cargo tests --- programs/drift/src/state/user/tests.rs | 2 +- programs/drift/src/validation/sig_verification/tests.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/programs/drift/src/state/user/tests.rs b/programs/drift/src/state/user/tests.rs index c1de9cca59..675e391ba0 100644 --- a/programs/drift/src/state/user/tests.rs +++ b/programs/drift/src/state/user/tests.rs @@ -2494,7 +2494,7 @@ mod update_open_bids_and_asks { } mod force_get_user_perp_position_mut { - use crate::state::user::{PerpPosition, PositionFlag, User}; + use crate::state::user::{PerpPosition, User}; #[test] fn test() { diff --git a/programs/drift/src/validation/sig_verification/tests.rs b/programs/drift/src/validation/sig_verification/tests.rs index e0f941d883..e016483684 100644 --- a/programs/drift/src/validation/sig_verification/tests.rs +++ b/programs/drift/src/validation/sig_verification/tests.rs @@ -13,7 +13,7 @@ mod sig_verification { 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 1, 0, 202, 154, 59, 0, 0, 0, 0, 0, 248, 89, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 192, 181, 74, 13, 0, 0, 0, 0, 1, 0, 248, 89, 13, 0, 0, 0, 0, 0, 0, 232, 3, 0, 0, 0, 0, 0, 0, 72, 112, 54, 84, 106, - 83, 48, 107 + 83, 48, 107, 0, 0, ]; // Test deserialization with non-delegate signer @@ -51,7 +51,7 @@ mod sig_verification { 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, - 0, 0, 0, 0, 0, 96, 254, 205 + 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, ]; // Test deserialization with delegate signer @@ -97,7 +97,7 @@ mod sig_verification { 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, 128, 151, 47, 14, 0, 0, 0, 0, 242, 208, 117, 159, 92, 135, 34, 224, 147, 14, 64, 92, 7, 25, 145, 237, 79, 35, 72, 24, 140, 13, 25, 189, 134, 243, 232, 5, 89, 37, 166, 242, 41, - 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49 + 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 0, 0, ]; // Test deserialization with delegate signer @@ -141,7 +141,7 @@ mod sig_verification { 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, - 59 + 59, 0, 0, 0, 0, ]; // Test deserialization with delegate signer From 24fbc98f85fda91f9256bb213d05748f23c17479 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 8 Sep 2025 16:23:33 -0400 Subject: [PATCH 157/216] cargo fmt -- --- programs/drift/src/instructions/user.rs | 1 - programs/drift/src/lib.rs | 7 ++++++- programs/drift/src/math/margin.rs | 12 ++++++------ programs/drift/src/math/margin/tests.rs | 13 ++++++------- programs/drift/src/state/user.rs | 12 ++++++++++-- programs/drift/src/state/user/tests.rs | 2 +- 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 7528413700..e6d13d6fec 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -2958,7 +2958,6 @@ pub fn handle_update_user_perp_position_custom_margin_ratio( Ok(()) } - pub fn handle_update_user_margin_trading_enabled<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, UpdateUser<'info>>, _sub_account_id: u16, diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 964a7851ca..43653c8c1a 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -373,7 +373,12 @@ pub mod drift { perp_market_index: u16, margin_ratio: u16, ) -> Result<()> { - handle_update_user_perp_position_custom_margin_ratio(ctx, _sub_account_id, perp_market_index, margin_ratio) + handle_update_user_perp_position_custom_margin_ratio( + ctx, + _sub_account_id, + perp_market_index, + margin_ratio, + ) } pub fn update_user_margin_trading_enabled<'c: 'info, 'info>( diff --git a/programs/drift/src/math/margin.rs b/programs/drift/src/math/margin.rs index 38bbe29697..9d227bfe8b 100644 --- a/programs/drift/src/math/margin.rs +++ b/programs/drift/src/math/margin.rs @@ -537,11 +537,12 @@ pub fn calculate_margin_requirement_and_total_collateral_and_liability_info( 0, )?; - let perp_position_custom_margin_ratio = if context.margin_type == MarginRequirementType::Initial { - market_position.max_margin_ratio as u32 - } else { - 0_u32 - }; + let perp_position_custom_margin_ratio = + if context.margin_type == MarginRequirementType::Initial { + market_position.max_margin_ratio as u32 + } else { + 0_u32 + }; let ( perp_margin_requirement, @@ -899,7 +900,6 @@ pub fn get_margin_calculation_for_disable_high_leverage_mode( oracle_map: &mut OracleMap, ) -> DriftResult { let custom_margin_ratio_before = user.max_margin_ratio; - let mut perp_position_max_margin_ratio_map = BTreeMap::new(); for (index, position) in user.perp_positions.iter_mut().enumerate() { diff --git a/programs/drift/src/math/margin/tests.rs b/programs/drift/src/math/margin/tests.rs index edfcf49984..3d201a587d 100644 --- a/programs/drift/src/math/margin/tests.rs +++ b/programs/drift/src/math/margin/tests.rs @@ -4452,11 +4452,10 @@ mod get_margin_calculation_for_disable_high_leverage_mode { use anchor_lang::Owner; use solana_program::pubkey::Pubkey; - use crate::{create_account_info, MARGIN_PRECISION}; + use crate::create_anchor_account_info; use crate::math::constants::{ - AMM_RESERVE_PRECISION, LIQUIDATION_FEE_PRECISION, PEG_PRECISION, - SPOT_BALANCE_PRECISION, SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, - SPOT_WEIGHT_PRECISION, + AMM_RESERVE_PRECISION, LIQUIDATION_FEE_PRECISION, PEG_PRECISION, SPOT_BALANCE_PRECISION, + SPOT_BALANCE_PRECISION_U64, SPOT_CUMULATIVE_INTEREST_PRECISION, SPOT_WEIGHT_PRECISION, }; use crate::math::margin::get_margin_calculation_for_disable_high_leverage_mode; use crate::state::oracle::{HistoricalOracleData, OracleSource}; @@ -4466,9 +4465,9 @@ mod get_margin_calculation_for_disable_high_leverage_mode { use crate::state::spot_market::{SpotBalanceType, SpotMarket}; use crate::state::spot_market_map::SpotMarketMap; use crate::state::user::{Order, PerpPosition, SpotPosition, User}; - use crate::test_utils::*; use crate::test_utils::get_pyth_price; - use crate::create_anchor_account_info; + use crate::test_utils::*; + use crate::{create_account_info, MARGIN_PRECISION}; #[test] pub fn check_user_not_changed() { @@ -4590,4 +4589,4 @@ mod get_margin_calculation_for_disable_high_leverage_mode { // should not change user assert_eq!(user, user_before); } -} \ No newline at end of file +} diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 2a74eab772..395cd01610 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -619,14 +619,22 @@ impl User { return Ok(true); } - pub fn update_perp_position_max_margin_ratio(&mut self, market_index: u16, margin_ratio: u16) -> DriftResult<()> { + pub fn update_perp_position_max_margin_ratio( + &mut self, + market_index: u16, + margin_ratio: u16, + ) -> DriftResult<()> { if self.max_margin_ratio > margin_ratio as u32 { msg!("user.max_margin_ratio ({}) > margin_ratio ({}), setting user.max_margin_ratio to margin_ratio", self.max_margin_ratio, margin_ratio); self.max_margin_ratio = margin_ratio as u32; } let perp_position = self.force_get_perp_position_mut(market_index)?; - msg!("perp_position.max_margin_ratio ({}) -> {}", perp_position.max_margin_ratio, margin_ratio); + msg!( + "perp_position.max_margin_ratio ({}) -> {}", + perp_position.max_margin_ratio, + margin_ratio + ); perp_position.max_margin_ratio = margin_ratio; Ok(()) diff --git a/programs/drift/src/state/user/tests.rs b/programs/drift/src/state/user/tests.rs index 675e391ba0..4c4eb45a94 100644 --- a/programs/drift/src/state/user/tests.rs +++ b/programs/drift/src/state/user/tests.rs @@ -2518,7 +2518,7 @@ mod force_get_user_perp_position_mut { let perp_position_mut = user.force_get_perp_position_mut(2).unwrap(); assert_eq!(perp_position_mut.max_margin_ratio, 0); } - + assert_eq!(user.perp_positions[0].market_index, 2); assert_eq!(user.perp_positions[0].max_margin_ratio, 0); } From 53f7be83812144f5122c4a35360b12f9fafe5f86 Mon Sep 17 00:00:00 2001 From: LukasDeco Date: Mon, 8 Sep 2025 23:43:04 -0600 Subject: [PATCH 158/216] lukas/per market lev (#1862) * feat: per market max leverage * fix: max margin ratio correct naming and decoding * fix: correctly decode perp position max lev * fix: add missing metadata for drift address on idl --- sdk/src/decode/user.ts | 5 +++-- sdk/src/driftClient.ts | 44 +++++++++++++++++++++++++++++++++++++++ sdk/src/idl/drift.json | 47 +++++++++++++++++++++++++++++++++++------- sdk/src/types.ts | 2 ++ sdk/src/user.ts | 29 ++++++++++++++++++++------ 5 files changed, 112 insertions(+), 15 deletions(-) diff --git a/sdk/src/decode/user.ts b/sdk/src/decode/user.ts index 688a773a1f..79343ca0db 100644 --- a/sdk/src/decode/user.ts +++ b/sdk/src/decode/user.ts @@ -111,7 +111,7 @@ export function decodeUser(buffer: Buffer): UserAccount { offset += 8; const lastQuoteAssetAmountPerLp = readSignedBigInt64LE(buffer, offset); offset += 8; - const remainderBaseAssetAmount = buffer.readInt32LE(offset); + const maxMarginRatio = buffer.readUInt16LE(offset); offset += 4; const marketIndex = buffer.readUInt16LE(offset); offset += 3; @@ -128,12 +128,13 @@ export function decodeUser(buffer: Buffer): UserAccount { openAsks, settledPnl, lpShares, + remainderBaseAssetAmount: 0, lastBaseAssetAmountPerLp, lastQuoteAssetAmountPerLp, - remainderBaseAssetAmount, marketIndex, openOrders, perLpBase, + maxMarginRatio, }); } diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index dc45730204..dd8e680d0b 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -1538,6 +1538,50 @@ export class DriftClient { return ix; } + public async getUpdateUserPerpPositionCustomMarginRatioIx( + perpMarketIndex: number, + marginRatio: number, + subAccountId = 0 + ): Promise { + const userAccountPublicKey = getUserAccountPublicKeySync( + this.program.programId, + this.wallet.publicKey, + subAccountId + ); + + await this.addUser(subAccountId, this.wallet.publicKey); + + const ix = this.program.instruction.updateUserPerpPositionCustomMarginRatio( + subAccountId, + perpMarketIndex, + marginRatio, + { + accounts: { + user: userAccountPublicKey, + authority: this.wallet.publicKey, + }, + } + ); + + return ix; + } + + public async updateUserPerpPositionCustomMarginRatio( + perpMarketIndex: number, + marginRatio: number, + subAccountId = 0, + txParams?: TxParams + ): Promise { + const ix = await this.getUpdateUserPerpPositionCustomMarginRatioIx( + perpMarketIndex, + marginRatio, + subAccountId + ); + const tx = await this.buildTransaction(ix, txParams ?? this.txParams); + const { txSig } = await this.sendTransaction(tx, [], this.opts); + return txSig; + } + public async getUpdateUserMarginTradingEnabledIx( marginTradingEnabled: boolean, subAccountId = 0, diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 65109372eb..316b8f979f 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1444,6 +1444,35 @@ } ] }, + { + "name": "updateUserPerpPositionCustomMarginRatio", + "accounts": [ + { + "name": "user", + "isMut": true, + "isSigner": false + }, + { + "name": "authority", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "subAccountId", + "type": "u16" + }, + { + "name": "perpMarketIndex", + "type": "u16" + }, + { + "name": "marginRatio", + "type": "u16" + } + ] + }, { "name": "updateUserMarginTradingEnabled", "accounts": [ @@ -11484,13 +11513,17 @@ "type": "i64" }, { - "name": "remainderBaseAssetAmount", - "docs": [ - "Settling LP position can lead to a small amount of base asset being left over smaller than step size", - "This records that remainder so it can be settled later on", - "precision: BASE_PRECISION" - ], - "type": "i32" + "name": "padding", + "type": { + "array": [ + "u8", + 2 + ] + } + }, + { + "name": "maxMarginRatio", + "type": "u16" }, { "name": "marketIndex", diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 2d6a34d114..369520dabf 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1042,7 +1042,9 @@ export type PerpPosition = { openAsks: BN; settledPnl: BN; lpShares: BN; + /** TODO: remove this field - it doesn't exist on chain */ remainderBaseAssetAmount: number; + maxMarginRatio: number; lastBaseAssetAmountPerLp: BN; lastQuoteAssetAmountPerLp: BN; perLpBase: number; diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 21bb3d40b4..08494f71f8 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -330,6 +330,7 @@ export class User { lastBaseAssetAmountPerLp: ZERO, lastQuoteAssetAmountPerLp: ZERO, perLpBase: 0, + maxMarginRatio: 0, }; } @@ -471,7 +472,8 @@ export class User { marketIndex, freeCollateral, worstCaseBaseAssetAmount, - enterHighLeverageMode + enterHighLeverageMode, + perpPosition ); } @@ -479,13 +481,18 @@ export class User { marketIndex: number, freeCollateral: BN, baseAssetAmount: BN, - enterHighLeverageMode = undefined + enterHighLeverageMode = undefined, + perpPosition?: PerpPosition ): BN { + const userCustomMargin = Math.max( + perpPosition?.maxMarginRatio ?? 0, + this.getUserAccount().maxMarginRatio + ); const marginRatio = calculateMarketMarginRatio( this.driftClient.getPerpMarketAccount(marketIndex), baseAssetAmount, 'Initial', - this.getUserAccount().maxMarginRatio, + userCustomMargin, enterHighLeverageMode || this.isHighLeverageMode('Initial') ); @@ -2162,7 +2169,10 @@ export class User { ); } - const userCustomMargin = this.getUserAccount().maxMarginRatio; + const userCustomMargin = Math.max( + perpPosition.maxMarginRatio, + this.getUserAccount().maxMarginRatio + ); const marginRatio = calculateMarketMarginRatio( market, baseAssetAmount.abs(), @@ -2212,7 +2222,10 @@ export class User { const proposedBaseAssetAmount = baseAssetAmount.add(positionBaseSizeChange); - const userCustomMargin = this.getUserAccount().maxMarginRatio; + const userCustomMargin = Math.max( + perpPosition.maxMarginRatio, + this.getUserAccount().maxMarginRatio + ); const marginRatio = calculateMarketMarginRatio( market, @@ -3578,12 +3591,16 @@ export class User { oraclePrice ); + const userCustomMargin = Math.max( + perpPosition.maxMarginRatio, + this.getUserAccount().maxMarginRatio + ); const marginRatio = new BN( calculateMarketMarginRatio( perpMarket, worstCaseBaseAmount.abs(), marginCategory, - this.getUserAccount().maxMarginRatio, + userCustomMargin, this.isHighLeverageMode(marginCategory) ) ); From 45ab6300a83b6141a9bd62864dd83729be92ca87 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Sep 2025 05:47:38 +0000 Subject: [PATCH 159/216] sdk: release v2.137.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 59e18f3b3d..e983cc8766 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.1 \ No newline at end of file +2.137.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 1d8bb437c9..e5b3f4bf35 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.1", + "version": "2.137.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 3909555e72a758bedb8cd0036f9353e24a4ef7a2 Mon Sep 17 00:00:00 2001 From: lil perp Date: Tue, 9 Sep 2025 21:58:34 -0400 Subject: [PATCH 160/216] program: add max margin ratio to swift message (#1860) * program: add max margin ratio to swift message * CHANGELOG * ts test * fix tests * lints --- CHANGELOG.md | 1 + programs/drift/src/instructions/keeper.rs | 4 + programs/drift/src/state/order_params.rs | 2 + .../drift/src/validation/sig_verification.rs | 3 + .../src/validation/sig_verification/tests.rs | 109 ++++++++++++++++++ sdk/src/driftClient.ts | 4 + sdk/src/idl/drift.json | 12 ++ sdk/src/types.ts | 2 + tests/placeAndMakeSignedMsgBankrun.ts | 101 +++++++++++++++- 9 files changed, 237 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acf18ce56f..350e6c68ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: add max margin ratio to swift message ([#1860](https://github.com/drift-labs/protocol-v2/pull/1860)) - program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) - program: add padding to swift messages ([#1845](https://github.com/drift-labs/protocol-v2/pull/1845)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 2bf1a003a1..85a4989425 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -762,6 +762,10 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>( return Ok(()); } + if let Some(max_margin_ratio) = verified_message_and_signature.max_margin_ratio { + taker.update_perp_position_max_margin_ratio(market_index, max_margin_ratio)?; + } + // Dont place order if signed msg order already exists let mut taker_order_id_to_use = taker.next_order_id; let mut signed_msg_order_id = diff --git a/programs/drift/src/state/order_params.rs b/programs/drift/src/state/order_params.rs index 48bc7399d2..69e1c6710d 100644 --- a/programs/drift/src/state/order_params.rs +++ b/programs/drift/src/state/order_params.rs @@ -864,6 +864,7 @@ pub struct SignedMsgOrderParamsMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, + pub max_margin_ratio: Option, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] @@ -874,6 +875,7 @@ pub struct SignedMsgOrderParamsDelegateMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, + pub max_margin_ratio: Option, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] diff --git a/programs/drift/src/validation/sig_verification.rs b/programs/drift/src/validation/sig_verification.rs index da3893e66d..66ba1cab98 100644 --- a/programs/drift/src/validation/sig_verification.rs +++ b/programs/drift/src/validation/sig_verification.rs @@ -57,6 +57,7 @@ pub struct VerifiedMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, + pub max_margin_ratio: Option, pub signature: [u8; 64], } @@ -94,6 +95,7 @@ pub fn deserialize_into_verified_message( uuid: deserialized.uuid, take_profit_order_params: deserialized.take_profit_order_params, stop_loss_order_params: deserialized.stop_loss_order_params, + max_margin_ratio: deserialized.max_margin_ratio, signature: *signature, }); } else { @@ -120,6 +122,7 @@ pub fn deserialize_into_verified_message( uuid: deserialized.uuid, take_profit_order_params: deserialized.take_profit_order_params, stop_loss_order_params: deserialized.stop_loss_order_params, + max_margin_ratio: deserialized.max_margin_ratio, signature: *signature, }); } diff --git a/programs/drift/src/validation/sig_verification/tests.rs b/programs/drift/src/validation/sig_verification/tests.rs index e016483684..fae2456b43 100644 --- a/programs/drift/src/validation/sig_verification/tests.rs +++ b/programs/drift/src/validation/sig_verification/tests.rs @@ -30,6 +30,7 @@ mod sig_verification { assert_eq!(verified_message.uuid, [72, 112, 54, 84, 106, 83, 48, 107]); assert!(verified_message.take_profit_order_params.is_none()); assert!(verified_message.stop_loss_order_params.is_none()); + assert!(verified_message.max_margin_ratio.is_none()); // Verify order params let order_params = &verified_message.signed_msg_order_params; assert_eq!(order_params.user_order_id, 1); @@ -66,6 +67,57 @@ mod sig_verification { assert_eq!(verified_message.delegate_signed_taker_pubkey, None); assert_eq!(verified_message.slot, 2345); assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_none()); + + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 3456000000u64); + assert_eq!(tp.trigger_price, 240000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 3456000000u64); + assert_eq!(sl.trigger_price, 225000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 3); + assert_eq!(order_params.direction, PositionDirection::Long); + assert_eq!(order_params.base_asset_amount, 3456000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(230000000i64)); + assert_eq!(order_params.auction_end_price, Some(237000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_non_delegate_with_max_margin_ratio() { + let signature = [1u8; 64]; + let payload = vec![ + 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 3, 0, 96, 254, 205, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, + 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, + 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, + 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 1, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, false); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, Some(2)); + assert_eq!(verified_message.delegate_signed_taker_pubkey, None); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_some()); + assert_eq!(verified_message.max_margin_ratio.unwrap(), 1); + assert!(verified_message.take_profit_order_params.is_some()); let tp = verified_message.take_profit_order_params.unwrap(); assert_eq!(tp.base_asset_amount, 3456000000u64); @@ -117,6 +169,7 @@ mod sig_verification { assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); assert!(verified_message.take_profit_order_params.is_none()); assert!(verified_message.stop_loss_order_params.is_none()); + assert!(verified_message.max_margin_ratio.is_none()); // Verify order params let order_params = &verified_message.signed_msg_order_params; @@ -159,6 +212,62 @@ mod sig_verification { ); assert_eq!(verified_message.slot, 2345); assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_none()); + + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 1000000000u64); + assert_eq!(tp.trigger_price, 230000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 1000000000u64); + assert_eq!(sl.trigger_price, 250000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 2); + assert_eq!(order_params.direction, PositionDirection::Short); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(240000000i64)); + assert_eq!(order_params.auction_end_price, Some(238000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_delegate_with_max_margin_ratio() { + let signature = [1u8; 64]; + let payload = vec![ + 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, + 128, 151, 47, 14, 0, 0, 0, 0, 241, 148, 164, 10, 232, 65, 33, 157, 18, 12, 251, 132, + 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, + 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, + 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, + 59, 0, 0, 0, 0, 1, 1, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, true); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, None); + assert_eq!( + verified_message.delegate_signed_taker_pubkey, + Some(Pubkey::from_str("HG2iQKnRkkasrLptwMZewV6wT7KPstw9wkA8yyu8Nx3m").unwrap()) + ); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_some()); + assert_eq!(verified_message.max_margin_ratio.unwrap(), 1); + assert!(verified_message.take_profit_order_params.is_some()); let tp = verified_message.take_profit_order_params.unwrap(); assert_eq!(tp.base_asset_amount, 1000000000u64); diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index dd8e680d0b..b0d7bf2e69 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -6434,6 +6434,10 @@ export class DriftClient { | SignedMsgOrderParamsDelegateMessage, delegateSigner?: boolean ): SignedMsgOrderParams { + if (orderParamsMessage.maxMarginRatio === undefined) { + orderParamsMessage.maxMarginRatio = null; + } + const borshBuf = this.encodeSignedMsgOrderParamsMessage( orderParamsMessage, delegateSigner diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 316b8f979f..da32f37fdf 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -10139,6 +10139,12 @@ "defined": "SignedMsgTriggerOrderParams" } } + }, + { + "name": "maxMarginRatio", + "type": { + "option": "u16" + } } ] } @@ -10186,6 +10192,12 @@ "defined": "SignedMsgTriggerOrderParams" } } + }, + { + "name": "maxMarginRatio", + "type": { + "option": "u16" + } } ] } diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 369520dabf..0bf0842314 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1247,6 +1247,7 @@ export type SignedMsgOrderParamsMessage = { uuid: Uint8Array; takeProfitOrderParams: SignedMsgTriggerOrderParams | null; stopLossOrderParams: SignedMsgTriggerOrderParams | null; + maxMarginRatio?: number | null; }; export type SignedMsgOrderParamsDelegateMessage = { @@ -1256,6 +1257,7 @@ export type SignedMsgOrderParamsDelegateMessage = { takerPubkey: PublicKey; takeProfitOrderParams: SignedMsgTriggerOrderParams | null; stopLossOrderParams: SignedMsgTriggerOrderParams | null; + maxMarginRatio?: number | null; }; export type SignedMsgTriggerOrderParams = { diff --git a/tests/placeAndMakeSignedMsgBankrun.ts b/tests/placeAndMakeSignedMsgBankrun.ts index 173e1364c4..3b23722445 100644 --- a/tests/placeAndMakeSignedMsgBankrun.ts +++ b/tests/placeAndMakeSignedMsgBankrun.ts @@ -1539,7 +1539,7 @@ describe('place and make signedMsg order', () => { ); assert.fail('should fail'); } catch (e) { - assert(e.toString().includes('0x1776')); + assert(e.toString().includes('0x18a5')); // SignedMsgUserContextUserMismatch const takerOrders = takerDriftClient.getUser().getOpenOrders(); assert(takerOrders.length == 0); } @@ -1604,6 +1604,105 @@ describe('place and make signedMsg order', () => { await takerDriftClientUser.unsubscribe(); await takerDriftClient.unsubscribe(); }); + + it('fills signedMsg with max margin ratio ', async () => { + slot = new BN( + await bankrunContextWrapper.connection.toConnection().getSlot() + ); + const [takerDriftClient, takerDriftClientUser] = + await initializeNewTakerClientAndUser( + bankrunContextWrapper, + chProgram, + usdcMint, + usdcAmount, + marketIndexes, + spotMarketIndexes, + oracleInfos, + bulkAccountLoader + ); + await takerDriftClientUser.fetchAccounts(); + + const marketIndex = 0; + const baseAssetAmount = BASE_PRECISION; + const takerOrderParams = getMarketOrderParams({ + marketIndex, + direction: PositionDirection.LONG, + baseAssetAmount, + price: new BN(224).mul(PRICE_PRECISION), + auctionStartPrice: new BN(223).mul(PRICE_PRECISION), + auctionEndPrice: new BN(224).mul(PRICE_PRECISION), + auctionDuration: 10, + userOrderId: 1, + postOnly: PostOnlyParams.NONE, + marketType: MarketType.PERP, + }) as OrderParams; + + await takerDriftClientUser.fetchAccounts(); + const makerOrderParams = getLimitOrderParams({ + marketIndex, + direction: PositionDirection.SHORT, + baseAssetAmount, + price: new BN(223).mul(PRICE_PRECISION), + postOnly: PostOnlyParams.MUST_POST_ONLY, + bitFlags: 1, + marketType: MarketType.PERP, + }) as OrderParams; + + const uuid = Uint8Array.from(Buffer.from(nanoid(8))); + const takerOrderParamsMessage: SignedMsgOrderParamsMessage = { + signedMsgOrderParams: takerOrderParams, + subAccountId: 0, + slot, + uuid, + stopLossOrderParams: null, + takeProfitOrderParams: null, + maxMarginRatio: 100, + }; + + const signedOrderParams = takerDriftClient.signSignedMsgOrderParamsMessage( + takerOrderParamsMessage + ); + + const ixs = await makerDriftClient.getPlaceAndMakeSignedMsgPerpOrderIxs( + signedOrderParams, + uuid, + { + taker: await takerDriftClient.getUserAccountPublicKey(), + takerUserAccount: takerDriftClient.getUserAccount(), + takerStats: takerDriftClient.getUserStatsAccountPublicKey(), + signingAuthority: takerDriftClient.wallet.publicKey, + }, + makerOrderParams, + undefined, + undefined, + undefined, + 2 + ); + + /* + Transaction size should be largest for filling with trigger orders w/ place and take + Max size: 1232 + We currently trade on sol market w/ sol oracle so would be better with LUT, so -64 bytes + 2 bytes + We dont have referrers for maker so need to add 64 bytes + We want to allow for positions to be full with maximally different markets for maker/taker and spot/perp, + so add 30 bytes for market/oracle for taker and 30 bytes for maker + Add 32 bytes for LUT + size of transaction + 32 + 2 + 30 + 30 < 1232 + */ + assert(getSizeOfTransaction(ixs, false) < 1138); + + const tx = await makerDriftClient.buildTransaction(ixs); + await makerDriftClient.sendTransaction(tx as Transaction); + + const takerPosition = takerDriftClient.getUser().getPerpPosition(0); + + // All orders are placed and one is + // @ts-ignore + assert(takerPosition.maxMarginRatio === 100); + + await takerDriftClientUser.unsubscribe(); + await takerDriftClient.unsubscribe(); + }); }); async function initializeNewTakerClientAndUser( From f14c09f9af3d8b604116935ebdcd1283d7ecbc37 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 02:03:30 +0000 Subject: [PATCH 161/216] sdk: release v2.137.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index e983cc8766..881921d5f1 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.2 \ No newline at end of file +2.137.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index e5b3f4bf35..b9478e32cc 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.2", + "version": "2.137.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 8018711313151529d95a6176926edce5314ea969 Mon Sep 17 00:00:00 2001 From: lil perp Date: Thu, 11 Sep 2025 17:14:21 -0400 Subject: [PATCH 162/216] program: revert swift max margin ratio (#1874) * Revert "program: add max margin ratio to swift message (#1860)" This reverts commit eaa7066fbde318759ac3defb9a8c34134e418f6b. * Revert "program: add swift message padding (#1845)" This reverts commit ecfc19a557faaaa96151a559f88c33643f8d339d. --- CHANGELOG.md | 2 - programs/drift/src/instructions/keeper.rs | 4 - programs/drift/src/state/order_params.rs | 2 - .../drift/src/validation/sig_verification.rs | 108 +++---- .../src/validation/sig_verification/tests.rs | 293 ------------------ sdk/src/driftClient.ts | 4 - sdk/src/idl/drift.json | 12 - sdk/src/types.ts | 2 - tests/placeAndMakeSignedMsgBankrun.ts | 101 +----- 9 files changed, 40 insertions(+), 488 deletions(-) delete mode 100644 programs/drift/src/validation/sig_verification/tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 350e6c68ab..0f1f001d29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features -- program: add max margin ratio to swift message ([#1860](https://github.com/drift-labs/protocol-v2/pull/1860)) - program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) -- program: add padding to swift messages ([#1845](https://github.com/drift-labs/protocol-v2/pull/1845)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) ### Fixes diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 85a4989425..2bf1a003a1 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -762,10 +762,6 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>( return Ok(()); } - if let Some(max_margin_ratio) = verified_message_and_signature.max_margin_ratio { - taker.update_perp_position_max_margin_ratio(market_index, max_margin_ratio)?; - } - // Dont place order if signed msg order already exists let mut taker_order_id_to_use = taker.next_order_id; let mut signed_msg_order_id = diff --git a/programs/drift/src/state/order_params.rs b/programs/drift/src/state/order_params.rs index 69e1c6710d..48bc7399d2 100644 --- a/programs/drift/src/state/order_params.rs +++ b/programs/drift/src/state/order_params.rs @@ -864,7 +864,6 @@ pub struct SignedMsgOrderParamsMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, - pub max_margin_ratio: Option, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] @@ -875,7 +874,6 @@ pub struct SignedMsgOrderParamsDelegateMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, - pub max_margin_ratio: Option, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] diff --git a/programs/drift/src/validation/sig_verification.rs b/programs/drift/src/validation/sig_verification.rs index 66ba1cab98..3349c7d5d7 100644 --- a/programs/drift/src/validation/sig_verification.rs +++ b/programs/drift/src/validation/sig_verification.rs @@ -14,9 +14,6 @@ use solana_program::program_memory::sol_memcmp; use solana_program::sysvar; use std::convert::TryInto; -#[cfg(test)] -mod tests; - const ED25519_PROGRAM_INPUT_HEADER_LEN: usize = 2; const SIGNATURE_LEN: u16 = 64; @@ -48,7 +45,6 @@ pub struct Ed25519SignatureOffsets { pub message_instruction_index: u16, } -#[derive(Debug)] pub struct VerifiedMessage { pub signed_msg_order_params: OrderParams, pub sub_account_id: Option, @@ -57,7 +53,6 @@ pub struct VerifiedMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, - pub max_margin_ratio: Option, pub signature: [u8; 64], } @@ -65,69 +60,6 @@ fn slice_eq(a: &[u8], b: &[u8]) -> bool { a.len() == b.len() && sol_memcmp(a, b, a.len()) == 0 } -pub fn deserialize_into_verified_message( - payload: Vec, - signature: &[u8; 64], - is_delegate_signer: bool, -) -> Result { - if is_delegate_signer { - if payload.len() < 8 { - return Err(SignatureVerificationError::InvalidMessageDataSize.into()); - } - let min_len: usize = std::mem::size_of::(); - let mut owned = payload; - if owned.len() < min_len { - owned.resize(min_len, 0); - } - let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize( - &mut &owned[8..], // 8 byte manual discriminator - ) - .map_err(|_| { - msg!("Invalid message encoding for is_delegate_signer = true"); - SignatureVerificationError::InvalidMessageDataSize - })?; - - return Ok(VerifiedMessage { - signed_msg_order_params: deserialized.signed_msg_order_params, - sub_account_id: None, - delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey), - slot: deserialized.slot, - uuid: deserialized.uuid, - take_profit_order_params: deserialized.take_profit_order_params, - stop_loss_order_params: deserialized.stop_loss_order_params, - max_margin_ratio: deserialized.max_margin_ratio, - signature: *signature, - }); - } else { - if payload.len() < 8 { - return Err(SignatureVerificationError::InvalidMessageDataSize.into()); - } - let min_len: usize = std::mem::size_of::(); - let mut owned = payload; - if owned.len() < min_len { - owned.resize(min_len, 0); - } - let deserialized = SignedMsgOrderParamsMessage::deserialize( - &mut &owned[8..], // 8 byte manual discriminator - ) - .map_err(|_| { - msg!("Invalid delegate message encoding for with is_delegate_signer = false"); - SignatureVerificationError::InvalidMessageDataSize - })?; - return Ok(VerifiedMessage { - signed_msg_order_params: deserialized.signed_msg_order_params, - sub_account_id: Some(deserialized.sub_account_id), - delegate_signed_taker_pubkey: None, - slot: deserialized.slot, - uuid: deserialized.uuid, - take_profit_order_params: deserialized.take_profit_order_params, - stop_loss_order_params: deserialized.stop_loss_order_params, - max_margin_ratio: deserialized.max_margin_ratio, - signature: *signature, - }); - } -} - /// Check Ed25519Program instruction data verifies the given msg /// /// `ix` an Ed25519Program instruction [see](https://github.com/solana-labs/solana/blob/master/sdk/src/ed25519_instruction.rs)) @@ -300,7 +232,45 @@ pub fn verify_and_decode_ed25519_msg( let payload = hex::decode(payload).map_err(|_| SignatureVerificationError::InvalidMessageHex)?; - deserialize_into_verified_message(payload, signature, is_delegate_signer) + if is_delegate_signer { + let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize( + &mut &payload[8..], // 8 byte manual discriminator + ) + .map_err(|_| { + msg!("Invalid message encoding for is_delegate_signer = true"); + SignatureVerificationError::InvalidMessageDataSize + })?; + + return Ok(VerifiedMessage { + signed_msg_order_params: deserialized.signed_msg_order_params, + sub_account_id: None, + delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey), + slot: deserialized.slot, + uuid: deserialized.uuid, + take_profit_order_params: deserialized.take_profit_order_params, + stop_loss_order_params: deserialized.stop_loss_order_params, + signature: *signature, + }); + } else { + let deserialized = SignedMsgOrderParamsMessage::deserialize( + &mut &payload[8..], // 8 byte manual discriminator + ) + .map_err(|_| { + msg!("Invalid delegate message encoding for with is_delegate_signer = false"); + SignatureVerificationError::InvalidMessageDataSize + })?; + + return Ok(VerifiedMessage { + signed_msg_order_params: deserialized.signed_msg_order_params, + sub_account_id: Some(deserialized.sub_account_id), + delegate_signed_taker_pubkey: None, + slot: deserialized.slot, + uuid: deserialized.uuid, + take_profit_order_params: deserialized.take_profit_order_params, + stop_loss_order_params: deserialized.stop_loss_order_params, + signature: *signature, + }); + } } #[error_code] diff --git a/programs/drift/src/validation/sig_verification/tests.rs b/programs/drift/src/validation/sig_verification/tests.rs deleted file mode 100644 index fae2456b43..0000000000 --- a/programs/drift/src/validation/sig_verification/tests.rs +++ /dev/null @@ -1,293 +0,0 @@ -mod sig_verification { - use std::str::FromStr; - - use anchor_lang::prelude::Pubkey; - - use crate::controller::position::PositionDirection; - use crate::validation::sig_verification::deserialize_into_verified_message; - - #[test] - fn test_deserialize_into_verified_message_non_delegate() { - let signature = [1u8; 64]; - let payload = vec![ - 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 1, 0, 202, 154, 59, 0, 0, 0, 0, 0, 248, - 89, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 192, 181, 74, 13, 0, 0, 0, 0, - 1, 0, 248, 89, 13, 0, 0, 0, 0, 0, 0, 232, 3, 0, 0, 0, 0, 0, 0, 72, 112, 54, 84, 106, - 83, 48, 107, 0, 0, - ]; - - // Test deserialization with non-delegate signer - let result = deserialize_into_verified_message(payload, &signature, false); - assert!(result.is_ok()); - - let verified_message = result.unwrap(); - - // Verify the deserialized message has expected structure - assert_eq!(verified_message.signature, signature); - assert_eq!(verified_message.sub_account_id, Some(0)); - assert_eq!(verified_message.delegate_signed_taker_pubkey, None); - assert_eq!(verified_message.slot, 1000); - assert_eq!(verified_message.uuid, [72, 112, 54, 84, 106, 83, 48, 107]); - assert!(verified_message.take_profit_order_params.is_none()); - assert!(verified_message.stop_loss_order_params.is_none()); - assert!(verified_message.max_margin_ratio.is_none()); - // Verify order params - let order_params = &verified_message.signed_msg_order_params; - assert_eq!(order_params.user_order_id, 1); - assert_eq!(order_params.direction, PositionDirection::Long); - assert_eq!(order_params.base_asset_amount, 1000000000u64); - assert_eq!(order_params.price, 224000000u64); - assert_eq!(order_params.market_index, 0); - assert_eq!(order_params.reduce_only, false); - assert_eq!(order_params.auction_duration, Some(10)); - assert_eq!(order_params.auction_start_price, Some(223000000i64)); - assert_eq!(order_params.auction_end_price, Some(224000000i64)); - } - - #[test] - fn test_deserialize_into_verified_message_non_delegate_with_tpsl() { - let signature = [1u8; 64]; - let payload = vec![ - 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 3, 0, 96, 254, 205, 0, 0, 0, 0, 64, 85, - 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, - 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, - 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, - 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, - ]; - - // Test deserialization with delegate signer - let result = deserialize_into_verified_message(payload, &signature, false); - assert!(result.is_ok()); - - let verified_message = result.unwrap(); - - // Verify the deserialized message has expected structure - assert_eq!(verified_message.signature, signature); - assert_eq!(verified_message.sub_account_id, Some(2)); - assert_eq!(verified_message.delegate_signed_taker_pubkey, None); - assert_eq!(verified_message.slot, 2345); - assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); - assert!(verified_message.max_margin_ratio.is_none()); - - assert!(verified_message.take_profit_order_params.is_some()); - let tp = verified_message.take_profit_order_params.unwrap(); - assert_eq!(tp.base_asset_amount, 3456000000u64); - assert_eq!(tp.trigger_price, 240000000u64); - - assert!(verified_message.stop_loss_order_params.is_some()); - let sl = verified_message.stop_loss_order_params.unwrap(); - assert_eq!(sl.base_asset_amount, 3456000000u64); - assert_eq!(sl.trigger_price, 225000000u64); - - // Verify order params - let order_params = &verified_message.signed_msg_order_params; - assert_eq!(order_params.user_order_id, 3); - assert_eq!(order_params.direction, PositionDirection::Long); - assert_eq!(order_params.base_asset_amount, 3456000000u64); - assert_eq!(order_params.price, 237000000u64); - assert_eq!(order_params.market_index, 0); - assert_eq!(order_params.reduce_only, false); - assert_eq!(order_params.auction_duration, Some(10)); - assert_eq!(order_params.auction_start_price, Some(230000000i64)); - assert_eq!(order_params.auction_end_price, Some(237000000i64)); - } - - #[test] - fn test_deserialize_into_verified_message_non_delegate_with_max_margin_ratio() { - let signature = [1u8; 64]; - let payload = vec![ - 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 3, 0, 96, 254, 205, 0, 0, 0, 0, 64, 85, - 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, - 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, - 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, - 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 1, - ]; - - // Test deserialization with delegate signer - let result = deserialize_into_verified_message(payload, &signature, false); - assert!(result.is_ok()); - - let verified_message = result.unwrap(); - - // Verify the deserialized message has expected structure - assert_eq!(verified_message.signature, signature); - assert_eq!(verified_message.sub_account_id, Some(2)); - assert_eq!(verified_message.delegate_signed_taker_pubkey, None); - assert_eq!(verified_message.slot, 2345); - assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); - assert!(verified_message.max_margin_ratio.is_some()); - assert_eq!(verified_message.max_margin_ratio.unwrap(), 1); - - assert!(verified_message.take_profit_order_params.is_some()); - let tp = verified_message.take_profit_order_params.unwrap(); - assert_eq!(tp.base_asset_amount, 3456000000u64); - assert_eq!(tp.trigger_price, 240000000u64); - - assert!(verified_message.stop_loss_order_params.is_some()); - let sl = verified_message.stop_loss_order_params.unwrap(); - assert_eq!(sl.base_asset_amount, 3456000000u64); - assert_eq!(sl.trigger_price, 225000000u64); - - // Verify order params - let order_params = &verified_message.signed_msg_order_params; - assert_eq!(order_params.user_order_id, 3); - assert_eq!(order_params.direction, PositionDirection::Long); - assert_eq!(order_params.base_asset_amount, 3456000000u64); - assert_eq!(order_params.price, 237000000u64); - assert_eq!(order_params.market_index, 0); - assert_eq!(order_params.reduce_only, false); - assert_eq!(order_params.auction_duration, Some(10)); - assert_eq!(order_params.auction_start_price, Some(230000000i64)); - assert_eq!(order_params.auction_end_price, Some(237000000i64)); - } - - #[test] - fn test_deserialize_into_verified_message_delegate() { - let signature = [1u8; 64]; - let payload = vec![ - 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, - 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, - 128, 151, 47, 14, 0, 0, 0, 0, 242, 208, 117, 159, 92, 135, 34, 224, 147, 14, 64, 92, 7, - 25, 145, 237, 79, 35, 72, 24, 140, 13, 25, 189, 134, 243, 232, 5, 89, 37, 166, 242, 41, - 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 0, 0, - ]; - - // Test deserialization with delegate signer - let result = deserialize_into_verified_message(payload, &signature, true); - assert!(result.is_ok()); - - let verified_message = result.unwrap(); - - // Verify the deserialized message has expected structure - assert_eq!(verified_message.signature, signature); - assert_eq!(verified_message.sub_account_id, None); - assert_eq!( - verified_message.delegate_signed_taker_pubkey, - Some(Pubkey::from_str("HLr2UfL422cakKkaBG4z1bMZrcyhmzX2pHdegjM6fYXB").unwrap()) - ); - assert_eq!(verified_message.slot, 2345); - assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); - assert!(verified_message.take_profit_order_params.is_none()); - assert!(verified_message.stop_loss_order_params.is_none()); - assert!(verified_message.max_margin_ratio.is_none()); - - // Verify order params - let order_params = &verified_message.signed_msg_order_params; - assert_eq!(order_params.user_order_id, 2); - assert_eq!(order_params.direction, PositionDirection::Short); - assert_eq!(order_params.base_asset_amount, 1000000000u64); - assert_eq!(order_params.price, 237000000u64); - assert_eq!(order_params.market_index, 0); - assert_eq!(order_params.reduce_only, false); - assert_eq!(order_params.auction_duration, Some(10)); - assert_eq!(order_params.auction_start_price, Some(240000000i64)); - assert_eq!(order_params.auction_end_price, Some(238000000i64)); - } - - #[test] - fn test_deserialize_into_verified_message_delegate_with_tpsl() { - let signature = [1u8; 64]; - let payload = vec![ - 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, - 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, - 128, 151, 47, 14, 0, 0, 0, 0, 241, 148, 164, 10, 232, 65, 33, 157, 18, 12, 251, 132, - 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, - 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, - 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, - 59, 0, 0, 0, 0, - ]; - - // Test deserialization with delegate signer - let result = deserialize_into_verified_message(payload, &signature, true); - assert!(result.is_ok()); - - let verified_message = result.unwrap(); - - // Verify the deserialized message has expected structure - assert_eq!(verified_message.signature, signature); - assert_eq!(verified_message.sub_account_id, None); - assert_eq!( - verified_message.delegate_signed_taker_pubkey, - Some(Pubkey::from_str("HG2iQKnRkkasrLptwMZewV6wT7KPstw9wkA8yyu8Nx3m").unwrap()) - ); - assert_eq!(verified_message.slot, 2345); - assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); - assert!(verified_message.max_margin_ratio.is_none()); - - assert!(verified_message.take_profit_order_params.is_some()); - let tp = verified_message.take_profit_order_params.unwrap(); - assert_eq!(tp.base_asset_amount, 1000000000u64); - assert_eq!(tp.trigger_price, 230000000u64); - - assert!(verified_message.stop_loss_order_params.is_some()); - let sl = verified_message.stop_loss_order_params.unwrap(); - assert_eq!(sl.base_asset_amount, 1000000000u64); - assert_eq!(sl.trigger_price, 250000000u64); - - // Verify order params - let order_params = &verified_message.signed_msg_order_params; - assert_eq!(order_params.user_order_id, 2); - assert_eq!(order_params.direction, PositionDirection::Short); - assert_eq!(order_params.base_asset_amount, 1000000000u64); - assert_eq!(order_params.price, 237000000u64); - assert_eq!(order_params.market_index, 0); - assert_eq!(order_params.reduce_only, false); - assert_eq!(order_params.auction_duration, Some(10)); - assert_eq!(order_params.auction_start_price, Some(240000000i64)); - assert_eq!(order_params.auction_end_price, Some(238000000i64)); - } - - #[test] - fn test_deserialize_into_verified_message_delegate_with_max_margin_ratio() { - let signature = [1u8; 64]; - let payload = vec![ - 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, - 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, - 128, 151, 47, 14, 0, 0, 0, 0, 241, 148, 164, 10, 232, 65, 33, 157, 18, 12, 251, 132, - 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, - 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, - 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, - 59, 0, 0, 0, 0, 1, 1, - ]; - - // Test deserialization with delegate signer - let result = deserialize_into_verified_message(payload, &signature, true); - assert!(result.is_ok()); - - let verified_message = result.unwrap(); - - // Verify the deserialized message has expected structure - assert_eq!(verified_message.signature, signature); - assert_eq!(verified_message.sub_account_id, None); - assert_eq!( - verified_message.delegate_signed_taker_pubkey, - Some(Pubkey::from_str("HG2iQKnRkkasrLptwMZewV6wT7KPstw9wkA8yyu8Nx3m").unwrap()) - ); - assert_eq!(verified_message.slot, 2345); - assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); - assert!(verified_message.max_margin_ratio.is_some()); - assert_eq!(verified_message.max_margin_ratio.unwrap(), 1); - - assert!(verified_message.take_profit_order_params.is_some()); - let tp = verified_message.take_profit_order_params.unwrap(); - assert_eq!(tp.base_asset_amount, 1000000000u64); - assert_eq!(tp.trigger_price, 230000000u64); - - assert!(verified_message.stop_loss_order_params.is_some()); - let sl = verified_message.stop_loss_order_params.unwrap(); - assert_eq!(sl.base_asset_amount, 1000000000u64); - assert_eq!(sl.trigger_price, 250000000u64); - - // Verify order params - let order_params = &verified_message.signed_msg_order_params; - assert_eq!(order_params.user_order_id, 2); - assert_eq!(order_params.direction, PositionDirection::Short); - assert_eq!(order_params.base_asset_amount, 1000000000u64); - assert_eq!(order_params.price, 237000000u64); - assert_eq!(order_params.market_index, 0); - assert_eq!(order_params.reduce_only, false); - assert_eq!(order_params.auction_duration, Some(10)); - assert_eq!(order_params.auction_start_price, Some(240000000i64)); - assert_eq!(order_params.auction_end_price, Some(238000000i64)); - } -} diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index b0d7bf2e69..dd8e680d0b 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -6434,10 +6434,6 @@ export class DriftClient { | SignedMsgOrderParamsDelegateMessage, delegateSigner?: boolean ): SignedMsgOrderParams { - if (orderParamsMessage.maxMarginRatio === undefined) { - orderParamsMessage.maxMarginRatio = null; - } - const borshBuf = this.encodeSignedMsgOrderParamsMessage( orderParamsMessage, delegateSigner diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index da32f37fdf..316b8f979f 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -10139,12 +10139,6 @@ "defined": "SignedMsgTriggerOrderParams" } } - }, - { - "name": "maxMarginRatio", - "type": { - "option": "u16" - } } ] } @@ -10192,12 +10186,6 @@ "defined": "SignedMsgTriggerOrderParams" } } - }, - { - "name": "maxMarginRatio", - "type": { - "option": "u16" - } } ] } diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 0bf0842314..369520dabf 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1247,7 +1247,6 @@ export type SignedMsgOrderParamsMessage = { uuid: Uint8Array; takeProfitOrderParams: SignedMsgTriggerOrderParams | null; stopLossOrderParams: SignedMsgTriggerOrderParams | null; - maxMarginRatio?: number | null; }; export type SignedMsgOrderParamsDelegateMessage = { @@ -1257,7 +1256,6 @@ export type SignedMsgOrderParamsDelegateMessage = { takerPubkey: PublicKey; takeProfitOrderParams: SignedMsgTriggerOrderParams | null; stopLossOrderParams: SignedMsgTriggerOrderParams | null; - maxMarginRatio?: number | null; }; export type SignedMsgTriggerOrderParams = { diff --git a/tests/placeAndMakeSignedMsgBankrun.ts b/tests/placeAndMakeSignedMsgBankrun.ts index 3b23722445..173e1364c4 100644 --- a/tests/placeAndMakeSignedMsgBankrun.ts +++ b/tests/placeAndMakeSignedMsgBankrun.ts @@ -1539,7 +1539,7 @@ describe('place and make signedMsg order', () => { ); assert.fail('should fail'); } catch (e) { - assert(e.toString().includes('0x18a5')); // SignedMsgUserContextUserMismatch + assert(e.toString().includes('0x1776')); const takerOrders = takerDriftClient.getUser().getOpenOrders(); assert(takerOrders.length == 0); } @@ -1604,105 +1604,6 @@ describe('place and make signedMsg order', () => { await takerDriftClientUser.unsubscribe(); await takerDriftClient.unsubscribe(); }); - - it('fills signedMsg with max margin ratio ', async () => { - slot = new BN( - await bankrunContextWrapper.connection.toConnection().getSlot() - ); - const [takerDriftClient, takerDriftClientUser] = - await initializeNewTakerClientAndUser( - bankrunContextWrapper, - chProgram, - usdcMint, - usdcAmount, - marketIndexes, - spotMarketIndexes, - oracleInfos, - bulkAccountLoader - ); - await takerDriftClientUser.fetchAccounts(); - - const marketIndex = 0; - const baseAssetAmount = BASE_PRECISION; - const takerOrderParams = getMarketOrderParams({ - marketIndex, - direction: PositionDirection.LONG, - baseAssetAmount, - price: new BN(224).mul(PRICE_PRECISION), - auctionStartPrice: new BN(223).mul(PRICE_PRECISION), - auctionEndPrice: new BN(224).mul(PRICE_PRECISION), - auctionDuration: 10, - userOrderId: 1, - postOnly: PostOnlyParams.NONE, - marketType: MarketType.PERP, - }) as OrderParams; - - await takerDriftClientUser.fetchAccounts(); - const makerOrderParams = getLimitOrderParams({ - marketIndex, - direction: PositionDirection.SHORT, - baseAssetAmount, - price: new BN(223).mul(PRICE_PRECISION), - postOnly: PostOnlyParams.MUST_POST_ONLY, - bitFlags: 1, - marketType: MarketType.PERP, - }) as OrderParams; - - const uuid = Uint8Array.from(Buffer.from(nanoid(8))); - const takerOrderParamsMessage: SignedMsgOrderParamsMessage = { - signedMsgOrderParams: takerOrderParams, - subAccountId: 0, - slot, - uuid, - stopLossOrderParams: null, - takeProfitOrderParams: null, - maxMarginRatio: 100, - }; - - const signedOrderParams = takerDriftClient.signSignedMsgOrderParamsMessage( - takerOrderParamsMessage - ); - - const ixs = await makerDriftClient.getPlaceAndMakeSignedMsgPerpOrderIxs( - signedOrderParams, - uuid, - { - taker: await takerDriftClient.getUserAccountPublicKey(), - takerUserAccount: takerDriftClient.getUserAccount(), - takerStats: takerDriftClient.getUserStatsAccountPublicKey(), - signingAuthority: takerDriftClient.wallet.publicKey, - }, - makerOrderParams, - undefined, - undefined, - undefined, - 2 - ); - - /* - Transaction size should be largest for filling with trigger orders w/ place and take - Max size: 1232 - We currently trade on sol market w/ sol oracle so would be better with LUT, so -64 bytes + 2 bytes - We dont have referrers for maker so need to add 64 bytes - We want to allow for positions to be full with maximally different markets for maker/taker and spot/perp, - so add 30 bytes for market/oracle for taker and 30 bytes for maker - Add 32 bytes for LUT - size of transaction + 32 + 2 + 30 + 30 < 1232 - */ - assert(getSizeOfTransaction(ixs, false) < 1138); - - const tx = await makerDriftClient.buildTransaction(ixs); - await makerDriftClient.sendTransaction(tx as Transaction); - - const takerPosition = takerDriftClient.getUser().getPerpPosition(0); - - // All orders are placed and one is - // @ts-ignore - assert(takerPosition.maxMarginRatio === 100); - - await takerDriftClientUser.unsubscribe(); - await takerDriftClient.unsubscribe(); - }); }); async function initializeNewTakerClientAndUser( From 4e59148ef70e1cf3878937a2d28d43903916cf85 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Sep 2025 21:19:18 +0000 Subject: [PATCH 163/216] sdk: release v2.137.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 881921d5f1..b18cc2002b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.3 \ No newline at end of file +2.137.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index b9478e32cc..711c9b604f 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.3", + "version": "2.137.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 70be941f4423cd8a57ea0321ce9b8cc35be9829f Mon Sep 17 00:00:00 2001 From: jordy25519 Date: Fri, 12 Sep 2025 07:59:31 +0800 Subject: [PATCH 164/216] add helper to build swiftDepositTrade tx (#1868) * add helper to build swiftDepositTrade tx --- sdk/src/driftClient.ts | 55 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index dd8e680d0b..67dfdde8d2 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -2631,6 +2631,59 @@ export class DriftClient { return instructions; } + public async buildSwiftDepositTx( + signedOrderParams: SignedMsgOrderParams, + takerInfo: { + taker: PublicKey; + takerStats: PublicKey; + takerUserAccount: UserAccount; + signingAuthority: PublicKey; + }, + depositAmount: BN, + depositSpotMarketIndex: number, + tradePerpMarketIndex: number, + subAccountId: number, + takerAssociatedTokenAccount: PublicKey, + initSwiftAccount = false + ) { + const instructions = await this.getDepositTxnIx( + depositAmount, + depositSpotMarketIndex, + takerAssociatedTokenAccount, + subAccountId, + false + ); + + if (initSwiftAccount) { + const isSignedMsgUserOrdersAccountInitialized = + await this.isSignedMsgUserOrdersAccountInitialized( + this.wallet.publicKey + ); + + if (!isSignedMsgUserOrdersAccountInitialized) { + const [, initializeSignedMsgUserOrdersAccountIx] = + await this.getInitializeSignedMsgUserOrdersAccountIx( + this.wallet.publicKey, + 8 + ); + + instructions.push(initializeSignedMsgUserOrdersAccountIx); + } + } + + const ixsWithPlace = await this.getPlaceSignedMsgTakerPerpOrderIxs( + signedOrderParams, + tradePerpMarketIndex, + takerInfo, + instructions + ); + + await this.buildTransaction(ixsWithPlace, { + computeUnitsPrice: 1_000, + computeUnits: 100_000, + }); + } + public async createDepositTxn( amount: BN, marketIndex: number, @@ -6448,7 +6501,7 @@ export class DriftClient { /** * Builds a deposit and place request for Swift service * - * @param depositTx - The signed tx containing a drift deposit (e.g. see `createDepositTxn`) + * @param depositTx - The signed tx containing a drift deposit (e.g. see `buildSwiftDepositTx`) * @param orderParamsMessage - The order parameters message to sign * @param delegateSigner - Whether this is a delegate signer * From 401646228feafd5217acbb5e1c84f0b74e7c5536 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:04:44 +0000 Subject: [PATCH 165/216] sdk: release v2.137.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b18cc2002b..48a63d2ca9 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.4 \ No newline at end of file +2.137.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 711c9b604f..c60831fb94 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.4", + "version": "2.137.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From b5ee5eafce89e02cfaf7de110b9b7440e94c1764 Mon Sep 17 00:00:00 2001 From: lowkeynicc <85139158+lowkeynicc@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:10:35 -0400 Subject: [PATCH 166/216] add optional maintenance arge to disable hlm ix (#1869) --- sdk/src/driftClient.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 67dfdde8d2..b5924614dd 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -10033,7 +10033,8 @@ export class DriftClient { public async getDisableHighLeverageModeIx( user: PublicKey, - userAccount?: UserAccount + userAccount?: UserAccount, + maintenance = false ): Promise { const remainingAccounts = userAccount ? this.getRemainingAccounts({ @@ -10042,7 +10043,7 @@ export class DriftClient { : undefined; const ix = await this.program.instruction.disableUserHighLeverageMode( - false, + maintenance, { accounts: { state: await this.getStatePublicKey(), From 3d10faf66ca171d847faed1ba8d1ac83e508e86a Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:15:26 +0000 Subject: [PATCH 167/216] sdk: release v2.137.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 48a63d2ca9..bc860719ea 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.5 \ No newline at end of file +2.137.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index c60831fb94..24277ca76e 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.5", + "version": "2.137.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From e306e42c32b3d51a439c8eab585c3a45c06135f1 Mon Sep 17 00:00:00 2001 From: moosecat Date: Mon, 15 Sep 2025 11:26:53 -0700 Subject: [PATCH 168/216] program: make mm oracle sequence id based (#1834) * make mm oracle sequence id based * sdkbug fix * add volatiltiy check for sequence ids * add zero checks for sequnce ids * safe div * CHANGELOG * cargo fmt -- --------- Co-authored-by: Chris Heaney --- CHANGELOG.md | 1 + programs/drift/src/controller/amm/tests.rs | 3 ++ .../drift/src/controller/position/tests.rs | 7 +++ programs/drift/src/controller/repeg/tests.rs | 10 +++++ .../src/controller/spot_balance/tests.rs | 1 + programs/drift/src/math/amm/tests.rs | 24 +++++++++++ programs/drift/src/math/funding/tests.rs | 1 + programs/drift/src/math/margin/tests.rs | 3 ++ programs/drift/src/math/oracle/tests.rs | 2 + programs/drift/src/math/repeg.rs | 1 + programs/drift/src/math/repeg/tests.rs | 14 ++++++ programs/drift/src/state/oracle.rs | 43 ++++++++++++++++++- programs/drift/src/state/oracle/tests.rs | 42 +++++++++++++++++- programs/drift/src/state/oracle_map.rs | 3 ++ programs/drift/src/state/perp_market.rs | 2 + programs/drift/src/state/user/tests.rs | 8 ++++ sdk/src/driftClient.ts | 28 +++++++++++- sdk/src/oracles/pythLazerClient.ts | 1 + sdk/src/oracles/pythPullClient.ts | 1 + sdk/src/oracles/types.ts | 1 + 20 files changed, 192 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1f001d29..db444559c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: add sequence id to exchange/mm oracle ([#1834](https://github.com/drift-labs/protocol-v2/pull/1834)) - program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) diff --git a/programs/drift/src/controller/amm/tests.rs b/programs/drift/src/controller/amm/tests.rs index a2a33fd6d5..ba51779f5e 100644 --- a/programs/drift/src/controller/amm/tests.rs +++ b/programs/drift/src/controller/amm/tests.rs @@ -133,6 +133,7 @@ fn formualic_k_tests() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; // zero funding cost @@ -192,6 +193,7 @@ fn iterative_bounds_formualic_k_tests() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; // negative funding cost @@ -236,6 +238,7 @@ fn iterative_no_bounds_formualic_k_tests() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; // negative funding cost diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index b80f19c121..ab6cf1034c 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -508,6 +508,7 @@ fn amm_pred_market_example() { confidence: 47843, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = perp_market .get_mm_oracle_price_data( @@ -646,6 +647,7 @@ fn amm_ref_price_decay_tail_test() { confidence: PRICE_PRECISION_U64 / 100000, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = perp_market .get_mm_oracle_price_data( @@ -822,6 +824,7 @@ fn amm_ref_price_offset_decay_logic() { confidence: PRICE_PRECISION_U64 / 1000, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = perp_market .get_mm_oracle_price_data( @@ -995,6 +998,7 @@ fn amm_negative_ref_price_offset_decay_logic() { confidence: PRICE_PRECISION_U64 / 1000, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = perp_market .get_mm_oracle_price_data( @@ -1184,6 +1188,7 @@ fn amm_perp_ref_offset() { confidence: PRICE_PRECISION_U64 / 1000, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = perp_market .get_mm_oracle_price_data( @@ -2446,6 +2451,7 @@ fn recenter_amm_2() { let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 1, OracleValidity::default(), *oracle_price_data, ) @@ -2587,6 +2593,7 @@ fn test_move_amm() { let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 1, OracleValidity::default(), *oracle_price_data, ) diff --git a/programs/drift/src/controller/repeg/tests.rs b/programs/drift/src/controller/repeg/tests.rs index 08b4f32d18..92d012dd8e 100644 --- a/programs/drift/src/controller/repeg/tests.rs +++ b/programs/drift/src/controller/repeg/tests.rs @@ -76,6 +76,7 @@ pub fn update_amm_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let reserve_price_before = market.amm.reserve_price().unwrap(); @@ -227,6 +228,7 @@ pub fn update_amm_test_bad_oracle() { confidence: 0, delay: 12, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -285,6 +287,7 @@ pub fn update_amm_larg_conf_test() { confidence: 0, delay: 9, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -309,6 +312,7 @@ pub fn update_amm_larg_conf_test() { confidence: 100 * PRICE_PRECISION_U64, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -335,6 +339,7 @@ pub fn update_amm_larg_conf_test() { confidence: 100 * PRICE_PRECISION_U64, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let fee_budget = calculate_fee_pool(&market).unwrap(); @@ -377,6 +382,7 @@ pub fn update_amm_larg_conf_test() { confidence: 121 * PRICE_PRECISION_U64, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -432,6 +438,7 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { confidence: 0, delay: 9, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -486,6 +493,7 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { confidence: 100 * PRICE_PRECISION_U64, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -512,6 +520,7 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { confidence: 100 * PRICE_PRECISION_U64, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) @@ -588,6 +597,7 @@ pub fn update_amm_larg_conf_w_neg_tfmd_test() { confidence: 121 * PRICE_PRECISION_U64, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) diff --git a/programs/drift/src/controller/spot_balance/tests.rs b/programs/drift/src/controller/spot_balance/tests.rs index b57790f361..83d6603fff 100644 --- a/programs/drift/src/controller/spot_balance/tests.rs +++ b/programs/drift/src/controller/spot_balance/tests.rs @@ -1700,6 +1700,7 @@ fn check_usdc_spot_market_twap() { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; update_spot_market_twap_stats(&mut spot_market, Some(&oracle_price_data), now).unwrap(); diff --git a/programs/drift/src/math/amm/tests.rs b/programs/drift/src/math/amm/tests.rs index da9e87e243..938dc1e0c0 100644 --- a/programs/drift/src/math/amm/tests.rs +++ b/programs/drift/src/math/amm/tests.rs @@ -22,6 +22,7 @@ fn calculate_amm_available_guards() { confidence: PRICE_PRECISION_U64 / 100, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; assert_eq!(market.amm.net_revenue_since_last_funding, 0); @@ -98,6 +99,7 @@ fn calculate_net_user_pnl_test() { confidence: PRICE_PRECISION_U64 / 100, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let net_user_pnl = calculate_net_user_pnl(&amm, oracle_price_data.price).unwrap(); @@ -127,6 +129,7 @@ fn calculate_expiry_price_long_imbalance_with_loss_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let market_position = PerpPosition { @@ -209,6 +212,7 @@ fn calculate_expiry_price_long_imbalance_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let market_position = PerpPosition { @@ -307,6 +311,7 @@ fn calculate_expiry_price_test() { confidence: PRICE_PRECISION_U64 / 100, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mut expiry_price = calculate_expiry_price(&amm, oracle_price_data.price, 0).unwrap(); @@ -324,6 +329,7 @@ fn calculate_expiry_price_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let market_position = PerpPosition { @@ -483,10 +489,12 @@ fn calc_mark_std_tests() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 3, + 1, OracleValidity::default(), oracle_price_data, ) @@ -605,10 +613,12 @@ fn update_mark_twap_tests() { confidence: PRICE_PRECISION_U64 / 100, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 3, + 1, OracleValidity::default(), oracle_price_data, ) @@ -704,10 +714,12 @@ fn update_mark_twap_tests() { confidence: PRICE_PRECISION_U64 / 80, delay: 14, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 15, + 1, OracleValidity::default(), oracle_price_data, ) @@ -773,10 +785,12 @@ fn calc_oracle_twap_tests() { confidence: PRICE_PRECISION_U64 / 100, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 2, + 1, OracleValidity::default(), oracle_price_data, ) @@ -798,10 +812,12 @@ fn calc_oracle_twap_tests() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 3, + 1, OracleValidity::default(), oracle_price_data, ) @@ -833,10 +849,12 @@ fn calc_oracle_twap_tests() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 3, + 1, OracleValidity::default(), oracle_price_data, ) @@ -898,10 +916,12 @@ fn calc_oracle_twap_clamp_update_tests() { confidence: PRICE_PRECISION_U64 / 10, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 2, + 1, OracleValidity::default(), oracle_price_data, ) @@ -985,10 +1005,12 @@ fn test_last_oracle_conf_update() { confidence: PRICE_PRECISION_U64 / 10, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 2, + 1, OracleValidity::default(), oracle_price_data, ) @@ -1004,11 +1026,13 @@ fn test_last_oracle_conf_update() { confidence: 1, delay: 5, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, 2, + 1, OracleValidity::default(), oracle_price_data, ) diff --git a/programs/drift/src/math/funding/tests.rs b/programs/drift/src/math/funding/tests.rs index 3fb3c37514..fb46a5cbce 100644 --- a/programs/drift/src/math/funding/tests.rs +++ b/programs/drift/src/math/funding/tests.rs @@ -415,6 +415,7 @@ fn unsettled_funding_pnl() { let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), *oracle_price_data, ) diff --git a/programs/drift/src/math/margin/tests.rs b/programs/drift/src/math/margin/tests.rs index 3d201a587d..2e9e25da44 100644 --- a/programs/drift/src/math/margin/tests.rs +++ b/programs/drift/src/math/margin/tests.rs @@ -265,6 +265,7 @@ mod test { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let market_position = PerpPosition { @@ -4197,6 +4198,7 @@ mod calculate_perp_position_value_and_pnl_prediction_market { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let market_position = PerpPosition { @@ -4260,6 +4262,7 @@ mod calculate_perp_position_value_and_pnl_prediction_market { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let market_position = PerpPosition { diff --git a/programs/drift/src/math/oracle/tests.rs b/programs/drift/src/math/oracle/tests.rs index 2295e15288..adf34a28b4 100644 --- a/programs/drift/src/math/oracle/tests.rs +++ b/programs/drift/src/math/oracle/tests.rs @@ -34,6 +34,7 @@ fn calculate_oracle_valid() { confidence: PRICE_PRECISION_U64 / 100, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mut market: PerpMarket = PerpMarket { amm, @@ -84,6 +85,7 @@ fn calculate_oracle_valid() { confidence: PRICE_PRECISION_U64 / 100, delay: 11, has_sufficient_number_of_data_points: true, + sequence_id: None, }; oracle_status = get_oracle_status( &market, diff --git a/programs/drift/src/math/repeg.rs b/programs/drift/src/math/repeg.rs index 0255ac452e..4cab184297 100644 --- a/programs/drift/src/math/repeg.rs +++ b/programs/drift/src/math/repeg.rs @@ -76,6 +76,7 @@ pub fn calculate_repeg_validity( confidence: oracle_conf, delay: _, has_sufficient_number_of_data_points: _, + sequence_id: _, } = *oracle_price_data; let oracle_price_u128 = oracle_price.cast::()?; diff --git a/programs/drift/src/math/repeg/tests.rs b/programs/drift/src/math/repeg/tests.rs index 2407675814..4beb1c7bfb 100644 --- a/programs/drift/src/math/repeg/tests.rs +++ b/programs/drift/src/math/repeg/tests.rs @@ -62,10 +62,12 @@ fn calculate_optimal_peg_and_budget_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) @@ -85,10 +87,12 @@ fn calculate_optimal_peg_and_budget_test() { confidence: 167, delay: 21, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) @@ -106,10 +110,12 @@ fn calculate_optimal_peg_and_budget_test() { confidence: 167, delay: 21, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) @@ -127,10 +133,12 @@ fn calculate_optimal_peg_and_budget_test() { confidence: 1234567, delay: 21, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) @@ -148,10 +156,12 @@ fn calculate_optimal_peg_and_budget_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) @@ -186,10 +196,12 @@ fn calculate_optimal_peg_and_budget_test() { confidence: 0, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) @@ -246,10 +258,12 @@ fn calculate_optimal_peg_and_budget_2_test() { confidence: 10233, delay: 2, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let mm_oracle_price_data = MMOraclePriceData::new( oracle_price_data.price, oracle_price_data.delay + 1, + 0, OracleValidity::default(), oracle_price_data, ) diff --git a/programs/drift/src/state/oracle.rs b/programs/drift/src/state/oracle.rs index 2feebb5a23..3becf945f6 100644 --- a/programs/drift/src/state/oracle.rs +++ b/programs/drift/src/state/oracle.rs @@ -177,6 +177,7 @@ const MM_EXCHANGE_FALLBACK_THRESHOLD: u128 = PERCENTAGE_PRECISION / 100; // 1% pub struct MMOraclePriceData { mm_oracle_price: i64, mm_oracle_delay: i64, + mm_oracle_sequence_id: u64, mm_oracle_validity: OracleValidity, mm_exchange_diff_bps: u128, exchange_oracle_price_data: OraclePriceData, @@ -187,6 +188,7 @@ impl MMOraclePriceData { pub fn new( mm_oracle_price: i64, mm_oracle_delay: i64, + mm_oracle_sequence_id: u64, mm_oracle_validity: OracleValidity, oracle_price_data: OraclePriceData, ) -> DriftResult { @@ -196,7 +198,23 @@ impl MMOraclePriceData { .cast::()? .safe_mul(PERCENTAGE_PRECISION)? .safe_div(oracle_price_data.price.abs().max(1).cast::()?)?; - let safe_oracle_price_data = if mm_oracle_delay > oracle_price_data.delay + + let exchange_oracle_is_more_recent = if let Some(exchange_seq_id) = + oracle_price_data.sequence_id + { + if exchange_seq_id.abs_diff(mm_oracle_sequence_id) < exchange_seq_id.safe_div(10_000)? + && exchange_seq_id != 0 + && mm_oracle_sequence_id != 0 + { + exchange_seq_id > mm_oracle_sequence_id + } else { + mm_oracle_delay > oracle_price_data.delay + } + } else { + mm_oracle_delay > oracle_price_data.delay + }; + + let safe_oracle_price_data = if exchange_oracle_is_more_recent || mm_oracle_price == 0i64 || !is_oracle_valid_for_action(mm_oracle_validity, Some(DriftAction::UseMMOraclePrice))? || price_diff_bps > MM_EXCHANGE_FALLBACK_THRESHOLD @@ -214,6 +232,7 @@ impl MMOraclePriceData { confidence: adjusted_confidence, delay: mm_oracle_delay, has_sufficient_number_of_data_points: true, + sequence_id: Some(mm_oracle_sequence_id), } }; @@ -224,6 +243,7 @@ impl MMOraclePriceData { mm_exchange_diff_bps: price_diff_bps, exchange_oracle_price_data: oracle_price_data, safe_oracle_price_data, + mm_oracle_sequence_id, }) } @@ -275,6 +295,11 @@ impl MMOraclePriceData { pub fn get_mm_oracle_delay(&self) -> i64 { self.mm_oracle_delay } + + // For potential future observability + pub fn _get_mm_oracle_sequence_id(&self) -> u64 { + self.mm_oracle_sequence_id + } } #[derive(Default, Clone, Copy, Debug)] @@ -283,6 +308,7 @@ pub struct OraclePriceData { pub confidence: u64, pub delay: i64, pub has_sufficient_number_of_data_points: bool, + pub sequence_id: Option, } pub fn get_oracle_price( @@ -304,6 +330,7 @@ pub fn get_oracle_price( confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }), OracleSource::Prelaunch => get_prelaunch_price(price_oracle, clock_slot), OracleSource::PythPull => get_pyth_price(price_oracle, clock_slot, oracle_source), @@ -336,6 +363,7 @@ pub fn get_pyth_price( let mut has_sufficient_number_of_data_points: bool = true; let mut oracle_precision: u128; let published_slot: u64; + let sequence_id: Option; if oracle_source.is_pyth_pull_oracle() { let price_message = pyth_solana_receiver_sdk::price_update::PriceUpdateV2::try_deserialize( @@ -346,6 +374,13 @@ pub fn get_pyth_price( oracle_conf = price_message.price_message.conf; oracle_precision = 10_u128.pow(price_message.price_message.exponent.unsigned_abs()); published_slot = price_message.posted_slot; + sequence_id = Some( + price_message + .price_message + .publish_time + .max(0) + .cast::()?, + ); } else if oracle_source.is_pyth_push_oracle() { let price_data = pyth_client::cast::(pyth_price_data); oracle_price = price_data.agg.price; @@ -364,12 +399,14 @@ pub fn get_pyth_price( oracle_precision = 10_u128.pow(price_data.expo.unsigned_abs()); published_slot = price_data.valid_slot; + sequence_id = None; } else { let price_data = PythLazerOracle::try_deserialize(&mut pyth_price_data).unwrap(); oracle_price = price_data.price; oracle_conf = price_data.conf; oracle_precision = 10_u128.pow(price_data.exponent.unsigned_abs()); published_slot = price_data.posted_slot; + sequence_id = Some(price_data.publish_time.max(0).cast::()?); } if oracle_precision <= multiple { @@ -406,6 +443,7 @@ pub fn get_pyth_price( confidence: oracle_conf_scaled, delay: oracle_delay, has_sufficient_number_of_data_points, + sequence_id, }) } @@ -463,6 +501,7 @@ pub fn get_switchboard_price( confidence, delay, has_sufficient_number_of_data_points, + sequence_id: None, }) } @@ -502,6 +541,7 @@ pub fn get_sb_on_demand_price( confidence, delay, has_sufficient_number_of_data_points, + sequence_id: None, }) } @@ -541,6 +581,7 @@ pub fn get_prelaunch_price(price_oracle: &AccountInfo, slot: u64) -> DriftResult confidence: oracle.confidence, delay: oracle.amm_last_update_slot.saturating_sub(slot).cast()?, has_sufficient_number_of_data_points: true, + sequence_id: None, }) } diff --git a/programs/drift/src/state/oracle/tests.rs b/programs/drift/src/state/oracle/tests.rs index 62d3525b57..8d64f725b4 100644 --- a/programs/drift/src/state/oracle/tests.rs +++ b/programs/drift/src/state/oracle/tests.rs @@ -163,11 +163,12 @@ fn oracle_map_diff_oracle_source() { #[test] fn use_mm_oracle() { let slot = 303030303; - let oracle_price_data = OraclePriceData { + let mut oracle_price_data = OraclePriceData { price: 130 * PRICE_PRECISION_I64 + 873, confidence: PRICE_PRECISION_U64 / 10, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: Some(1756262481), }; let mut market = PerpMarket { market_index: 0, @@ -180,6 +181,7 @@ fn use_mm_oracle() { max_spread: 1000, mm_oracle_price: 130 * PRICE_PRECISION_I64 + 973, mm_oracle_slot: slot, + mm_oracle_sequence_id: 1756262481, historical_oracle_data: HistoricalOracleData::default_with_current_oracle( oracle_price_data, ), @@ -209,13 +211,47 @@ fn use_mm_oracle() { mm_oracle_price_data.mm_oracle_delay ); - // Update the MM oracle slot to be delayed, should use oracle price data + // Update the MM oracle slot to be equal but the sequence number to be behind, should use exchange oracle + market.amm.mm_oracle_sequence_id = 1756262481 - 10; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + assert_eq!(mm_oracle_price_data.get_price(), oracle_price_data.price); + assert_eq!(mm_oracle_price_data.get_delay(), oracle_price_data.delay,); + + // Update oracle price data to have no sequence id, fall back to using slot comparison + oracle_price_data.sequence_id = None; + + // With no sequence id and delayed mm oracle slot, should fall back to using oracle price data market.amm.mm_oracle_slot = slot - 5; let mm_oracle_price_data = market .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) .unwrap(); assert_eq!(mm_oracle_price_data.get_price(), oracle_price_data.price); assert_eq!(mm_oracle_price_data.get_delay(), oracle_price_data.delay,); + + // With no sequence id and up to date mm oracle slot, should use mm oracle + market.amm.mm_oracle_slot = slot; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + assert_eq!( + mm_oracle_price_data.get_price(), + mm_oracle_price_data.mm_oracle_price + ); + assert_eq!( + mm_oracle_price_data.get_delay(), + mm_oracle_price_data.mm_oracle_delay + ); + + // With really off sequence id and up to date mm oracle slot, should fall back to slot comparison + market.amm.mm_oracle_sequence_id = 1756262481000; // wrong resolution + market.amm.mm_oracle_slot = slot - 5; + let mm_oracle_price_data = market + .get_mm_oracle_price_data(oracle_price_data, slot, &state.oracle_guard_rails.validity) + .unwrap(); + assert_eq!(mm_oracle_price_data.get_price(), oracle_price_data.price); + assert_eq!(mm_oracle_price_data.get_delay(), oracle_price_data.delay); } #[test] @@ -226,6 +262,7 @@ fn mm_oracle_confidence() { confidence: PRICE_PRECISION_U64 / 10, delay: 1, has_sufficient_number_of_data_points: true, + sequence_id: Some(0), }; let market = PerpMarket { market_index: 0, @@ -238,6 +275,7 @@ fn mm_oracle_confidence() { max_spread: 1000, mm_oracle_price: 130 * PRICE_PRECISION_I64 + 999, mm_oracle_slot: slot, + mm_oracle_sequence_id: 1, historical_oracle_data: HistoricalOracleData::default_with_current_oracle( oracle_price_data, ), diff --git a/programs/drift/src/state/oracle_map.rs b/programs/drift/src/state/oracle_map.rs index 64d979315c..dc574b8d98 100644 --- a/programs/drift/src/state/oracle_map.rs +++ b/programs/drift/src/state/oracle_map.rs @@ -245,6 +245,7 @@ impl<'a> OracleMap<'a> { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }, }) } @@ -308,6 +309,7 @@ impl<'a> OracleMap<'a> { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }, }) } @@ -339,6 +341,7 @@ impl<'a> OracleMap<'a> { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }, } } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index e2bec65a8a..9ba2d008ae 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -833,6 +833,7 @@ impl PerpMarket { let oracle_data = OraclePriceData { price: self.amm.mm_oracle_price, delay, + sequence_id: None, confidence: oracle_price_data.confidence, has_sufficient_number_of_data_points: true, }; @@ -854,6 +855,7 @@ impl PerpMarket { Ok(MMOraclePriceData::new( self.amm.mm_oracle_price, delay, + self.amm.mm_oracle_sequence_id, oracle_validity, oracle_price_data, )?) diff --git a/programs/drift/src/state/user/tests.rs b/programs/drift/src/state/user/tests.rs index 4c4eb45a94..db945b5007 100644 --- a/programs/drift/src/state/user/tests.rs +++ b/programs/drift/src/state/user/tests.rs @@ -621,6 +621,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -718,6 +719,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -818,6 +820,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -915,6 +918,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -1012,6 +1016,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -1109,6 +1114,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -1206,6 +1212,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { @@ -1303,6 +1310,7 @@ mod get_worst_case_fill_simulation { confidence: 1, delay: 0, has_sufficient_number_of_data_points: true, + sequence_id: None, }; let strict_price = StrictOraclePrice { diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index b5924614dd..4a02efca43 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -8696,6 +8696,32 @@ export class DriftClient { .abs() .mul(PERCENTAGE_PRECISION) .div(BN.max(oracleData.price, ONE)); + + const mmOracleSequenceId = perpMarket.amm.mmOracleSequenceId; + + // Do slot check for recency if sequence ids are zero or they're too divergent + const doSlotCheckForRecency = + oracleData.sequenceId == null || + oracleData.sequenceId.eq(ZERO) || + mmOracleSequenceId.eq(ZERO) || + oracleData.sequenceId + .sub(perpMarket.amm.mmOracleSequenceId) + .abs() + .gt(oracleData.sequenceId.div(new BN(10_000))); + + let isExchangeOracleMoreRecent = true; + if ( + doSlotCheckForRecency && + oracleData.slot <= perpMarket.amm.mmOracleSlot + ) { + isExchangeOracleMoreRecent = false; + } else if ( + !doSlotCheckForRecency && + oracleData.sequenceId < mmOracleSequenceId + ) { + isExchangeOracleMoreRecent = false; + } + if ( !isOracleValid( perpMarket, @@ -8704,7 +8730,7 @@ export class DriftClient { stateAccountAndSlot.slot ) || perpMarket.amm.mmOraclePrice.eq(ZERO) || - perpMarket.amm.mmOracleSlot < oracleData.slot || + isExchangeOracleMoreRecent || pctDiff.gt(PERCENTAGE_PRECISION.divn(100)) // 1% threshold ) { return { ...oracleData, isMMOracleActive }; diff --git a/sdk/src/oracles/pythLazerClient.ts b/sdk/src/oracles/pythLazerClient.ts index d13ba5fa1c..9e341215db 100644 --- a/sdk/src/oracles/pythLazerClient.ts +++ b/sdk/src/oracles/pythLazerClient.ts @@ -83,6 +83,7 @@ export class PythLazerClient implements OracleClient { this.multiple ), hasSufficientNumberOfDataPoints: true, + sequenceId: priceData.publishTime, }; } } diff --git a/sdk/src/oracles/pythPullClient.ts b/sdk/src/oracles/pythPullClient.ts index 90048dc60a..0359b35928 100644 --- a/sdk/src/oracles/pythPullClient.ts +++ b/sdk/src/oracles/pythPullClient.ts @@ -88,6 +88,7 @@ export class PythPullClient implements OracleClient { this.multiple ), hasSufficientNumberOfDataPoints: true, + sequenceId: priceData.publishTime, }; } } diff --git a/sdk/src/oracles/types.ts b/sdk/src/oracles/types.ts index 4c015be0da..f5df74154c 100644 --- a/sdk/src/oracles/types.ts +++ b/sdk/src/oracles/types.ts @@ -17,6 +17,7 @@ export type OraclePriceData = { twap?: BN; twapConfidence?: BN; maxPrice?: BN; // pre-launch markets only + sequenceId?: BN; }; export type OracleInfo = { From df990752008e679dddf9e551c762bdb69a7a9001 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 18:32:48 +0000 Subject: [PATCH 169/216] sdk: release v2.137.0-beta.7 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index bc860719ea..c1e52d68d5 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.6 \ No newline at end of file +2.137.0-beta.7 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 24277ca76e..ca370deb67 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.6", + "version": "2.137.0-beta.7", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 8543110e7b5de919eae434008e19dfa472f911a8 Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:39:39 -0400 Subject: [PATCH 170/216] program: relax fee tier constraints for maker tier (#1876) * program: fix-fee-validate-different-denoms * fix build * CHANGELOG --------- Co-authored-by: Chris Heaney --- CHANGELOG.md | 2 ++ .../drift/src/validation/fee_structure.rs | 21 +++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db444559c0..4cf80d25d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- program: relax fee tier constraints for maker ([#1876](https://github.com/drift-labs/protocol-v2/pull/1876)) + ### Breaking ## [2.136.0] - 2025-09-03 diff --git a/programs/drift/src/validation/fee_structure.rs b/programs/drift/src/validation/fee_structure.rs index 19254b7af0..73e0107648 100644 --- a/programs/drift/src/validation/fee_structure.rs +++ b/programs/drift/src/validation/fee_structure.rs @@ -3,6 +3,7 @@ use crate::msg; use crate::error::{DriftResult, ErrorCode}; use crate::math::constants::{ FEE_DENOMINATOR, FEE_PERCENTAGE_DENOMINATOR, OPEN_ORDER_MARGIN_REQUIREMENT, + PERCENTAGE_PRECISION, }; use crate::state::state::{FeeStructure, FeeTier}; use crate::validate; @@ -89,14 +90,26 @@ pub fn validate_fee_tier( )?; let taker_fee = fee_tier.fee_numerator * (100 - fee_tier.referee_fee_numerator) / 100; - let fee_to_market = taker_fee - - fee_tier.maker_rebate_numerator + let fee_to_market_pre_maker = taker_fee - taker_fee * (fee_tier.referrer_reward_numerator + filler_reward_numerator) / 100; validate!( - fee_to_market <= fee_tier.fee_numerator, + fee_to_market_pre_maker <= fee_tier.fee_numerator, ErrorCode::InvalidFeeStructure, - "invalid fee to market ({}) for index ({})", + "invalid taker fee to market ({}) for index ({})", + fee_tier.referrer_reward_numerator, + fee_tier_index, + )?; + + let fee_to_market = fee_to_market_pre_maker as u128 * PERCENTAGE_PRECISION + / fee_tier.fee_denominator as u128 + - fee_tier.maker_rebate_numerator as u128 * PERCENTAGE_PRECISION + / fee_tier.maker_rebate_denominator as u128; + + validate!( + fee_to_market >= 0, + ErrorCode::InvalidFeeStructure, + "invalid maker fee to market ({}) for index ({})", fee_tier.referrer_reward_numerator, fee_tier_index, )?; From 3ca3ed1c3b32d96ea1f95ba57bbe2059df70f2b7 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 15 Sep 2025 14:41:48 -0400 Subject: [PATCH 171/216] program: make it easer to fill min order size orders (#1799) * program: make it easer to fill min order size orders * reduce only tweak * CHANGELOG --- CHANGELOG.md | 1 + programs/drift/src/controller/orders.rs | 13 +++++++------ programs/drift/src/validation/order.rs | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf80d25d2..cbf67e876a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- program: make it easier to fill step size orders ([#1799](https://github.com/drift-labs/protocol-v2/pull/1799)) - program: relax fee tier constraints for maker ([#1876](https://github.com/drift-labs/protocol-v2/pull/1876)) ### Breaking diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 8ac70e1c0d..6cff2d1350 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -2172,12 +2172,13 @@ pub fn fulfill_perp_order_with_amm( }; // if user position is less than min order size, step size is the threshold - let amm_size_threshold = - if existing_base_asset_amount.unsigned_abs() > market.amm.min_order_size { - market.amm.min_order_size - } else { - market.amm.order_step_size - }; + let amm_size_threshold = if !user.orders[order_index].reduce_only + && existing_base_asset_amount.unsigned_abs() > market.amm.min_order_size + { + market.amm.min_order_size + } else { + market.amm.order_step_size + }; if base_asset_amount < amm_size_threshold { // if is an actual swap (and not amm jit order) then msg! diff --git a/programs/drift/src/validation/order.rs b/programs/drift/src/validation/order.rs index 6fdd9e9fc7..92dd4da465 100644 --- a/programs/drift/src/validation/order.rs +++ b/programs/drift/src/validation/order.rs @@ -142,7 +142,7 @@ fn validate_limit_order( order, market.amm.order_step_size, market.amm.min_order_size, - order.reduce_only, + order.reduce_only || order.is_jit_maker(), )?; if order.price == 0 && !order.has_oracle_price_offset() { @@ -339,7 +339,7 @@ fn validate_base_asset_amount( order: &Order, step_size: u64, min_order_size: u64, - reduce_only: bool, + reduce_only_or_jit_maker: bool, ) -> DriftResult { if order.base_asset_amount == 0 { msg!("Order base_asset_amount cant be 0"); @@ -355,7 +355,7 @@ fn validate_base_asset_amount( )?; validate!( - reduce_only || order.base_asset_amount >= min_order_size, + reduce_only_or_jit_maker || order.base_asset_amount >= min_order_size, ErrorCode::InvalidOrderMinOrderSize, "Order base_asset_amount ({}) < min_order_size ({})", order.base_asset_amount, From 2d09cef1f1b2e0416565e527cb255829844d6ec4 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 15 Sep 2025 15:01:38 -0400 Subject: [PATCH 172/216] program: fix clippy --- programs/drift/src/validation/fee_structure.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/drift/src/validation/fee_structure.rs b/programs/drift/src/validation/fee_structure.rs index 73e0107648..0421110e97 100644 --- a/programs/drift/src/validation/fee_structure.rs +++ b/programs/drift/src/validation/fee_structure.rs @@ -101,13 +101,13 @@ pub fn validate_fee_tier( fee_tier_index, )?; - let fee_to_market = fee_to_market_pre_maker as u128 * PERCENTAGE_PRECISION + let has_fee_for_market = fee_to_market_pre_maker as u128 * PERCENTAGE_PRECISION / fee_tier.fee_denominator as u128 - - fee_tier.maker_rebate_numerator as u128 * PERCENTAGE_PRECISION + > fee_tier.maker_rebate_numerator as u128 * PERCENTAGE_PRECISION / fee_tier.maker_rebate_denominator as u128; validate!( - fee_to_market >= 0, + has_fee_for_market, ErrorCode::InvalidFeeStructure, "invalid maker fee to market ({}) for index ({})", fee_tier.referrer_reward_numerator, From 4ee44ba4302248e1770616b7325be3242a0c26ab Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Mon, 15 Sep 2025 15:50:13 -0400 Subject: [PATCH 173/216] fix cargo test --- programs/drift/src/validation/fee_structure.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/drift/src/validation/fee_structure.rs b/programs/drift/src/validation/fee_structure.rs index 0421110e97..afaa1674d2 100644 --- a/programs/drift/src/validation/fee_structure.rs +++ b/programs/drift/src/validation/fee_structure.rs @@ -103,7 +103,7 @@ pub fn validate_fee_tier( let has_fee_for_market = fee_to_market_pre_maker as u128 * PERCENTAGE_PRECISION / fee_tier.fee_denominator as u128 - > fee_tier.maker_rebate_numerator as u128 * PERCENTAGE_PRECISION + >= fee_tier.maker_rebate_numerator as u128 * PERCENTAGE_PRECISION / fee_tier.maker_rebate_denominator as u128; validate!( From a4d38d2869b17e6d0874ae5dce0bdb3abfd14df8 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 15 Sep 2025 17:14:46 -0400 Subject: [PATCH 174/216] program: apply reduce only logic for post only orders (#1878) * program: apply reduce only logic for post only orders * CHANGELOG --- CHANGELOG.md | 1 + programs/drift/src/state/user.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbf67e876a..06f4d1a605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: post only respects reduce only ([#1878](https://github.com/drift-labs/protocol-v2/pull/1878)) - program: add sequence id to exchange/mm oracle ([#1834](https://github.com/drift-labs/protocol-v2/pull/1834)) - program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 395cd01610..faccc82ddf 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1408,7 +1408,7 @@ impl Order { }; // if order is post only, can disregard reduce only - if !self.reduce_only || self.post_only { + if !self.reduce_only { return Ok(base_asset_amount_unfilled); } From e6c83517060c460241a8301a690e19a2f9414aaa Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 15 Sep 2025 17:30:24 -0700 Subject: [PATCH 175/216] fix tests --- programs/drift/src/state/user.rs | 1 - programs/drift/src/state/user/tests.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index faccc82ddf..1017fd3dd5 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -1407,7 +1407,6 @@ impl Order { } }; - // if order is post only, can disregard reduce only if !self.reduce_only { return Ok(base_asset_amount_unfilled); } diff --git a/programs/drift/src/state/user/tests.rs b/programs/drift/src/state/user/tests.rs index db945b5007..5bd2b154f5 100644 --- a/programs/drift/src/state/user/tests.rs +++ b/programs/drift/src/state/user/tests.rs @@ -1540,7 +1540,7 @@ mod get_base_asset_amount_unfilled { ..Order::default() }; - assert_eq!(order.get_base_asset_amount_unfilled(Some(1)).unwrap(), 1) + assert_eq!(order.get_base_asset_amount_unfilled(Some(1)).unwrap(), 0) } #[test] From c4451e0448c51b01cfd89532e6b3ad1e6ba5a703 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 15 Sep 2025 16:59:43 -0700 Subject: [PATCH 176/216] v2.237.0 --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 4 ++-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06f4d1a605..3492f6be78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +### Fixes + +### Breaking + +## [2.137.0] - 2025-09-15 + +### Features + - program: post only respects reduce only ([#1878](https://github.com/drift-labs/protocol-v2/pull/1878)) - program: add sequence id to exchange/mm oracle ([#1834](https://github.com/drift-labs/protocol-v2/pull/1834)) - program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) diff --git a/Cargo.lock b/Cargo.lock index 6c40571441..58aef0bce6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.136.0" +version = "2.137.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 6d9488cca1..945e055dee 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.136.0" +version = "2.137.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index ca370deb67..1db18b1f76 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0-beta.7", + "version": "2.137.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 316b8f979f..f923272c05 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.136.0", + "version": "2.137.0", "name": "drift", "instructions": [ { @@ -15966,4 +15966,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} \ No newline at end of file +} From 354e65fd6cd615c25c33dfe1683ab4eed4e39ba9 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:39:15 +0000 Subject: [PATCH 177/216] sdk: release v2.138.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index c1e52d68d5..25d23a27d6 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.137.0-beta.7 \ No newline at end of file +2.138.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 1db18b1f76..1398a07a8d 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.137.0", + "version": "2.138.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 3fdcc49f58e8dd40f95aa86f151ebe8dc83e8a01 Mon Sep 17 00:00:00 2001 From: Chester Sim Date: Tue, 16 Sep 2025 21:58:01 +0800 Subject: [PATCH 178/216] refactor(sdk): export TransactionParamProcessor --- sdk/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 4214847795..7f30e2afa0 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -104,6 +104,7 @@ export * from './tx/priorityFeeCalculator'; export * from './tx/forwardOnlyTxSender'; export * from './tx/types'; export * from './tx/txHandler'; +export * from './tx/txParamProcessor'; export * from './util/computeUnits'; export * from './util/digest'; export * from './util/tps'; From 0883a3dc6333c57d55bfc01f1a67c25f15df9c7c Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:03:06 +0000 Subject: [PATCH 179/216] sdk: release v2.138.0-beta.1 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 25d23a27d6..8e036be9d4 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.0 \ No newline at end of file +2.138.0-beta.1 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 1398a07a8d..2800e7bce0 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.0", + "version": "2.138.0-beta.1", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From a8a9731ace1d773cfa0795f2571f4265c2368b93 Mon Sep 17 00:00:00 2001 From: lowkeynicc <85139158+lowkeynicc@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:35:51 -0600 Subject: [PATCH 180/216] add hlmm to decodeUser (#1881) * add hlmm to decodeUser * throw if unrecognized margin mode * fallback to default margin mode --- sdk/src/decode/user.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sdk/src/decode/user.ts b/sdk/src/decode/user.ts index 79343ca0db..e0d852f6e8 100644 --- a/sdk/src/decode/user.ts +++ b/sdk/src/decode/user.ts @@ -338,8 +338,15 @@ export function decodeUser(buffer: Buffer): UserAccount { const marginModeNum = buffer.readUInt8(offset); if (marginModeNum === 0) { marginMode = MarginMode.DEFAULT; - } else { + } else if (marginModeNum === 1) { marginMode = MarginMode.HIGH_LEVERAGE; + } else if (marginModeNum === 2) { + marginMode = MarginMode.HIGH_LEVERAGE_MAINTENANCE; + } else { + console.error( + `Detected unknown margin mode: ${marginModeNum}. Please update @drift-labs/sdk for latest IDL.` + ); + marginMode = MarginMode.DEFAULT; } offset += 1; From 01ce90472ace28ae8042ef8316d65806e1d457ff Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 23:40:19 +0000 Subject: [PATCH 181/216] sdk: release v2.138.0-beta.2 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 8e036be9d4..4e57451163 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.1 \ No newline at end of file +2.138.0-beta.2 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 2800e7bce0..27022ee9d7 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.1", + "version": "2.138.0-beta.2", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 70a6cd905951d9339458997c84f55ce72c3ac7c7 Mon Sep 17 00:00:00 2001 From: Jack Waller Date: Tue, 16 Sep 2025 11:14:16 +1000 Subject: [PATCH 182/216] chore: add laserstream client --- sdk/package.json | 1 + .../accounts/lazerProgramAccountSubscriber.ts | 215 ++++++++++++++++++ sdk/src/accounts/types.ts | 1 + sdk/src/orderSubscriber/grpcSubscription.ts | 42 +++- sdk/yarn.lock | 70 ++++++ 5 files changed, 317 insertions(+), 12 deletions(-) create mode 100644 sdk/src/accounts/lazerProgramAccountSubscriber.ts diff --git a/sdk/package.json b/sdk/package.json index 27022ee9d7..0da76946e7 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -55,6 +55,7 @@ "@triton-one/yellowstone-grpc": "1.3.0", "anchor-bankrun": "0.3.0", "gill": "^0.10.2", + "helius-laserstream": "^0.1.7", "nanoid": "3.3.4", "node-cache": "5.1.2", "rpc-websockets": "7.5.1", diff --git a/sdk/src/accounts/lazerProgramAccountSubscriber.ts b/sdk/src/accounts/lazerProgramAccountSubscriber.ts new file mode 100644 index 0000000000..babfb1704f --- /dev/null +++ b/sdk/src/accounts/lazerProgramAccountSubscriber.ts @@ -0,0 +1,215 @@ +import { GrpcConfigs, ResubOpts } from './types'; +import { Program } from '@coral-xyz/anchor'; +import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; +import * as Buffer from 'buffer'; +import { WebSocketProgramAccountSubscriber } from './webSocketProgramAccountSubscriber'; + +import { + CommitmentLevel as LaserCommitmentLevel, + subscribe as LaserSubscribe, + LaserstreamConfig, + SubscribeRequest, + SubscribeUpdate, + CompressionAlgorithms, +} from 'helius-laserstream'; +import { CommitmentLevel } from '@triton-one/yellowstone-grpc'; + +type LaserCommitment = + (typeof LaserCommitmentLevel)[keyof typeof LaserCommitmentLevel]; + +export class LaserstreamProgramAccountSubscriber< + T, +> extends WebSocketProgramAccountSubscriber { + private stream: + | { + id: string; + cancel: () => void; + write?: (req: SubscribeRequest) => Promise; + } + | undefined; + + private commitmentLevel: CommitmentLevel; + public listenerId?: number; + + private readonly laserConfig: LaserstreamConfig; + + private constructor( + laserConfig: LaserstreamConfig, + commitmentLevel: CommitmentLevel, + subscriptionName: string, + accountDiscriminator: string, + program: Program, + decodeBufferFn: (accountName: string, ix: Buffer) => T, + options: { filters: MemcmpFilter[] } = { filters: [] }, + resubOpts?: ResubOpts + ) { + super( + subscriptionName, + accountDiscriminator, + program, + decodeBufferFn, + options, + resubOpts + ); + this.laserConfig = laserConfig; + this.commitmentLevel = this.toLaserCommitment(commitmentLevel); + } + + public static async create( + grpcConfigs: GrpcConfigs, + subscriptionName: string, + accountDiscriminator: string, + program: Program, + decodeBufferFn: (accountName: string, ix: Buffer) => U, + options: { filters: MemcmpFilter[] } = { + filters: [], + }, + resubOpts?: ResubOpts + ): Promise> { + const laserConfig: LaserstreamConfig = { + apiKey: grpcConfigs.token, + endpoint: grpcConfigs.endpoint, + maxReconnectAttempts: grpcConfigs.enableReconnect ? 10 : 0, + channelOptions: { + 'grpc.default_compression_algorithm': CompressionAlgorithms.zstd, + 'grpc.max_receive_message_length': 1_000_000_000, + }, + }; + + const commitmentLevel = + grpcConfigs.commitmentLevel ?? CommitmentLevel.CONFIRMED; + + return new LaserstreamProgramAccountSubscriber( + laserConfig, + commitmentLevel, + subscriptionName, + accountDiscriminator, + program, + decodeBufferFn, + options, + resubOpts + ); + } + + async subscribe( + onChange: ( + accountId: PublicKey, + data: T, + context: Context, + buffer: Buffer + ) => void + ): Promise { + if (this.listenerId != null || this.isUnsubscribing) return; + + this.onChange = onChange; + + const filters = this.options.filters.map((filter) => { + return { + memcmp: { + offset: filter.memcmp.offset, + base58: filter.memcmp.bytes, + }, + }; + }); + + const request: SubscribeRequest = { + slots: {}, + accounts: { + drift: { + account: [], + owner: [this.program.programId.toBase58()], + filters, + }, + }, + transactions: {}, + blocks: {}, + blocksMeta: {}, + accountsDataSlice: [], + commitment: this.commitmentLevel, + entry: {}, + transactionsStatus: {}, + }; + + try { + const stream = await LaserSubscribe( + this.laserConfig, + request, + async (update: SubscribeUpdate) => { + if (update.account) { + const slot = Number(update.account.slot); + const acc = update.account.account; + + const accountInfo = { + owner: new PublicKey(acc.owner), + lamports: Number(acc.lamports), + data: Buffer.Buffer.from(acc.data), + executable: acc.executable, + rentEpoch: Number(acc.rentEpoch), + }; + + const payload = { + accountId: new PublicKey(acc.pubkey), + accountInfo, + }; + + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + clearTimeout(this.timeoutId); + this.handleRpcResponse({ slot }, payload); + this.setTimeout(); + } else { + this.handleRpcResponse({ slot }, payload); + } + } + }, + async (error) => { + console.error('LaserStream client error:', error); + throw error; + } + ); + + this.stream = stream; + this.listenerId = 1; + + if (this.resubOpts?.resubTimeoutMs) { + this.receivingData = true; + this.setTimeout(); + } + } catch (err) { + console.error('Failed to start LaserStream client:', err); + throw err; + } + } + + public async unsubscribe(onResub = false): Promise { + if (!onResub && this.resubOpts) { + this.resubOpts.resubTimeoutMs = undefined; + } + this.isUnsubscribing = true; + clearTimeout(this.timeoutId); + this.timeoutId = undefined; + + if (this.listenerId != null && this.stream) { + try { + this.stream.cancel(); + } finally { + this.listenerId = undefined; + this.isUnsubscribing = false; + } + } else { + this.isUnsubscribing = false; + } + } + + public toLaserCommitment( + level: string | number | undefined + ): LaserCommitment { + if (typeof level === 'string') { + return ( + (LaserCommitmentLevel as any)[level.toUpperCase()] ?? + LaserCommitmentLevel.CONFIRMED + ); + } + return (level as LaserCommitment) ?? LaserCommitmentLevel.CONFIRMED; + } +} diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index 98ab7133fb..80fe5a43dc 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -234,6 +234,7 @@ export type GrpcConfigs = { * Defaults to false, will throw on connection loss. */ enableReconnect?: boolean; + client?: 'yellowstone' | 'lazer'; }; export interface HighLeverageModeConfigAccountSubscriber { diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 41101435ab..795f10ff81 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -5,6 +5,7 @@ import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; import { getUserFilter, getNonIdleUserFilter } from '../memcmp'; +import { LaserstreamProgramAccountSubscriber } from '../accounts/lazerProgramAccountSubscriber'; export class grpcSubscription { private orderSubscriber: OrderSubscriber; @@ -12,7 +13,9 @@ export class grpcSubscription { private resubOpts?: ResubOpts; private resyncIntervalMs?: number; - private subscriber?: grpcProgramAccountSubscriber; + private subscriber?: + | grpcProgramAccountSubscriber + | LaserstreamProgramAccountSubscriber; private resyncTimeoutId?: ReturnType; private decoded?: boolean; @@ -47,17 +50,32 @@ export class grpcSubscription { return; } - this.subscriber = await grpcProgramAccountSubscriber.create( - this.grpcConfigs, - 'OrderSubscriber', - 'User', - this.orderSubscriber.driftClient.program, - this.orderSubscriber.decodeFn, - { - filters: [getUserFilter(), getNonIdleUserFilter()], - }, - this.resubOpts - ); + if (this.grpcConfigs.client === 'lazer') { + this.subscriber = + await LaserstreamProgramAccountSubscriber.create( + this.grpcConfigs, + 'OrderSubscriber', + 'User', + this.orderSubscriber.driftClient.program, + this.orderSubscriber.decodeFn, + { + filters: [getUserFilter(), getNonIdleUserFilter()], + }, + this.resubOpts + ); + } else { + this.subscriber = await grpcProgramAccountSubscriber.create( + this.grpcConfigs, + 'OrderSubscriber', + 'User', + this.orderSubscriber.driftClient.program, + this.orderSubscriber.decodeFn, + { + filters: [getUserFilter(), getNonIdleUserFilter()], + }, + this.resubOpts + ); + } await this.subscriber.subscribe( ( diff --git a/sdk/yarn.lock b/sdk/yarn.lock index 960207492a..fdcc859a96 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -1281,6 +1281,13 @@ dependencies: undici-types "~5.26.4" +"@types/protobufjs@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/protobufjs/-/protobufjs-6.0.0.tgz#aeabb43f9507bb19c8adfb479584c151082353e4" + integrity sha512-A27RDExpAf3rdDjIrHKiJK6x8kqqJ4CmoChwtipfhVAn1p7+wviQFFP7dppn8FslSbHtQeVPvi8wNKkDjSYjHw== + dependencies: + protobufjs "*" + "@types/semver@^7.5.0": version "7.7.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.0.tgz#64c441bdae033b378b6eef7d0c3d77c329b9378e" @@ -2787,6 +2794,51 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +helius-laserstream-darwin-arm64@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream-darwin-arm64/-/helius-laserstream-darwin-arm64-0.1.7.tgz#c84df402bdb8a2159bcfc2711bf2b64fe09edd24" + integrity sha512-oMkt6qr7EQLfgiOCVO/9lTQLi8futBVqhUSRvsCmYcAqmaFNsvmf+/rRVq/o56+iq0PseqV6DcRF+5s88tYEIg== + +helius-laserstream-darwin-x64@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream-darwin-x64/-/helius-laserstream-darwin-x64-0.1.7.tgz#fe15b0513a8d1f55075f27b1a5836bf73ec470ff" + integrity sha512-88utpg/ZMtsnF9RX268D50fl6B3kQOFS0nCzoenS1CwMvMprTqHSEDqYFgBX8O7t52gox5aw8+x3XqhHX0fIMQ== + +helius-laserstream-linux-arm64-gnu@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream-linux-arm64-gnu/-/helius-laserstream-linux-arm64-gnu-0.1.7.tgz#cc7a636bc12961d314a4c46008d63f4fb6fe6b03" + integrity sha512-4YQuISaa3OWOBQCUnslT+HguuGRMO1KRQeWSjtuSHDYn7oO/KXePCtEo9vqnQx7HtQFDnS1/kuFExIa2L4Sp2w== + +helius-laserstream-linux-arm64-musl@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream-linux-arm64-musl/-/helius-laserstream-linux-arm64-musl-0.1.7.tgz#96dcf87b855698e9f28214d96031d0934324db45" + integrity sha512-VyN/5nzqUtBN88PLDIMVJmFDgImMCL1sjBWKf49ppAWKN3LVvYVYT8tGMi4nzb5vj7ObduIi1ZZ+cGWNM6H2kA== + +helius-laserstream-linux-x64-gnu@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream-linux-x64-gnu/-/helius-laserstream-linux-x64-gnu-0.1.7.tgz#0bcaed9697b975033e86a74ab4d734ee3d4d3e1c" + integrity sha512-0//p5wlITWbWKBaW2CIYfS3/9fBJCvMn8fBvBKot28psIWSQ6Uc5u/IqS2ls438NvTiEvBp6pgScWoYHXKU+VQ== + +helius-laserstream-linux-x64-musl@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream-linux-x64-musl/-/helius-laserstream-linux-x64-musl-0.1.7.tgz#6c38ca7f97bd1ff46947794fd907ecd8492a0249" + integrity sha512-4VFxsKE+X3Jj/DBrdKQCUs/6ljuYadiPrF5QBMIyaflzZka/hOOvd2bDAo8Bi9/qGGCCaJG6F3U3OFKGKSiE9w== + +helius-laserstream@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/helius-laserstream/-/helius-laserstream-0.1.7.tgz#67f9d570f56ba9bb801d210ebff5e9cf1cc97faa" + integrity sha512-xsCbc8dApJpLb6OShOCeJ5/6pQMdGk6sQojEgihTNGZaGhaAzwYzJcxL5q4uszE3qG/viJO67Mi/MxArDb+QaQ== + dependencies: + "@types/protobufjs" "^6.0.0" + protobufjs "^7.5.3" + optionalDependencies: + helius-laserstream-darwin-arm64 "0.1.7" + helius-laserstream-darwin-x64 "0.1.7" + helius-laserstream-linux-arm64-gnu "0.1.7" + helius-laserstream-linux-arm64-musl "0.1.7" + helius-laserstream-linux-x64-gnu "0.1.7" + helius-laserstream-linux-x64-musl "0.1.7" + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -3635,6 +3687,24 @@ pretty-ms@^7.0.1: dependencies: parse-ms "^2.1.0" +protobufjs@*, protobufjs@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a" + integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + protobufjs@^7.2.5, protobufjs@^7.4.0: version "7.5.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.3.tgz#13f95a9e3c84669995ec3652db2ac2fb00b89363" From c5affe2d9db50cc52a66246fe76ddbf462f8557b Mon Sep 17 00:00:00 2001 From: Jack Waller Date: Wed, 17 Sep 2025 09:42:52 +1000 Subject: [PATCH 183/216] fix: update to correct naming --- ...ramAccountSubscriber.ts => laserProgramAccountSubscriber.ts} | 0 sdk/src/orderSubscriber/grpcSubscription.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename sdk/src/accounts/{lazerProgramAccountSubscriber.ts => laserProgramAccountSubscriber.ts} (100%) diff --git a/sdk/src/accounts/lazerProgramAccountSubscriber.ts b/sdk/src/accounts/laserProgramAccountSubscriber.ts similarity index 100% rename from sdk/src/accounts/lazerProgramAccountSubscriber.ts rename to sdk/src/accounts/laserProgramAccountSubscriber.ts diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 795f10ff81..8800ead4ff 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -5,7 +5,7 @@ import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; import { getUserFilter, getNonIdleUserFilter } from '../memcmp'; -import { LaserstreamProgramAccountSubscriber } from '../accounts/lazerProgramAccountSubscriber'; +import { LaserstreamProgramAccountSubscriber } from '../accounts/laserProgramAccountSubscriber'; export class grpcSubscription { private orderSubscriber: OrderSubscriber; From 989532e514708903ac6a795211e0cf5bb82315ba Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 00:05:52 +0000 Subject: [PATCH 184/216] sdk: release v2.138.0-beta.3 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 4e57451163..bc48aa16fc 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.2 \ No newline at end of file +2.138.0-beta.3 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 0da76946e7..c64a460132 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.2", + "version": "2.138.0-beta.3", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 897fdef850dc1a7a55e5888f0ea4053a1c1f09a0 Mon Sep 17 00:00:00 2001 From: Jack Waller Date: Wed, 17 Sep 2025 10:08:38 +1000 Subject: [PATCH 185/216] chore: rename lazer -> laser --- sdk/src/accounts/types.ts | 2 +- sdk/src/orderSubscriber/grpcSubscription.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index 80fe5a43dc..c572ae2798 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -234,7 +234,7 @@ export type GrpcConfigs = { * Defaults to false, will throw on connection loss. */ enableReconnect?: boolean; - client?: 'yellowstone' | 'lazer'; + client?: 'yellowstone' | 'laser'; }; export interface HighLeverageModeConfigAccountSubscriber { diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 8800ead4ff..04160c0bff 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -50,7 +50,7 @@ export class grpcSubscription { return; } - if (this.grpcConfigs.client === 'lazer') { + if (this.grpcConfigs.client === 'laser') { this.subscriber = await LaserstreamProgramAccountSubscriber.create( this.grpcConfigs, From 4b24ba5374c9d7c78e0dc73f92fd392d9a180a39 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 00:34:31 +0000 Subject: [PATCH 186/216] sdk: release v2.138.0-beta.4 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index bc48aa16fc..ec4ab7a37e 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.3 \ No newline at end of file +2.138.0-beta.4 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index c64a460132..7347bec710 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.3", + "version": "2.138.0-beta.4", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 5ee4b37f9f4db048d9356c818940f6e7daf5a4cf Mon Sep 17 00:00:00 2001 From: Jack Waller Date: Wed, 17 Sep 2025 11:14:57 +1000 Subject: [PATCH 187/216] Revert "chore: rename lazer -> laser" This reverts commit f6d85303c56a723fab1ddbf8a573f9c2426b9140. --- sdk/src/accounts/types.ts | 2 +- sdk/src/orderSubscriber/grpcSubscription.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index c572ae2798..80fe5a43dc 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -234,7 +234,7 @@ export type GrpcConfigs = { * Defaults to false, will throw on connection loss. */ enableReconnect?: boolean; - client?: 'yellowstone' | 'laser'; + client?: 'yellowstone' | 'lazer'; }; export interface HighLeverageModeConfigAccountSubscriber { diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 04160c0bff..8800ead4ff 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -50,7 +50,7 @@ export class grpcSubscription { return; } - if (this.grpcConfigs.client === 'laser') { + if (this.grpcConfigs.client === 'lazer') { this.subscriber = await LaserstreamProgramAccountSubscriber.create( this.grpcConfigs, From 671db10772b505ab4aa4854f8c86a1692c6cba70 Mon Sep 17 00:00:00 2001 From: Jack Waller Date: Wed, 17 Sep 2025 11:15:02 +1000 Subject: [PATCH 188/216] Revert "fix: update to correct naming" This reverts commit 42c8b10269514f561f3de354825cca0ed39604d8. --- ...ramAccountSubscriber.ts => lazerProgramAccountSubscriber.ts} | 0 sdk/src/orderSubscriber/grpcSubscription.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename sdk/src/accounts/{laserProgramAccountSubscriber.ts => lazerProgramAccountSubscriber.ts} (100%) diff --git a/sdk/src/accounts/laserProgramAccountSubscriber.ts b/sdk/src/accounts/lazerProgramAccountSubscriber.ts similarity index 100% rename from sdk/src/accounts/laserProgramAccountSubscriber.ts rename to sdk/src/accounts/lazerProgramAccountSubscriber.ts diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 8800ead4ff..795f10ff81 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -5,7 +5,7 @@ import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; import { getUserFilter, getNonIdleUserFilter } from '../memcmp'; -import { LaserstreamProgramAccountSubscriber } from '../accounts/laserProgramAccountSubscriber'; +import { LaserstreamProgramAccountSubscriber } from '../accounts/lazerProgramAccountSubscriber'; export class grpcSubscription { private orderSubscriber: OrderSubscriber; From a4b1977344b1bb67b1bb7bd1d9f098556e098a06 Mon Sep 17 00:00:00 2001 From: Jack Waller Date: Wed, 17 Sep 2025 11:15:04 +1000 Subject: [PATCH 189/216] Revert "chore: add laserstream client" This reverts commit 8da91cbab11bcee6cbe4038207d6eec9ffae7113. --- sdk/package.json | 1 - .../accounts/lazerProgramAccountSubscriber.ts | 215 ------------------ sdk/src/accounts/types.ts | 1 - sdk/src/orderSubscriber/grpcSubscription.ts | 42 +--- sdk/yarn.lock | 70 ------ 5 files changed, 12 insertions(+), 317 deletions(-) delete mode 100644 sdk/src/accounts/lazerProgramAccountSubscriber.ts diff --git a/sdk/package.json b/sdk/package.json index 7347bec710..896fd77fc7 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -55,7 +55,6 @@ "@triton-one/yellowstone-grpc": "1.3.0", "anchor-bankrun": "0.3.0", "gill": "^0.10.2", - "helius-laserstream": "^0.1.7", "nanoid": "3.3.4", "node-cache": "5.1.2", "rpc-websockets": "7.5.1", diff --git a/sdk/src/accounts/lazerProgramAccountSubscriber.ts b/sdk/src/accounts/lazerProgramAccountSubscriber.ts deleted file mode 100644 index babfb1704f..0000000000 --- a/sdk/src/accounts/lazerProgramAccountSubscriber.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { GrpcConfigs, ResubOpts } from './types'; -import { Program } from '@coral-xyz/anchor'; -import { Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; -import * as Buffer from 'buffer'; -import { WebSocketProgramAccountSubscriber } from './webSocketProgramAccountSubscriber'; - -import { - CommitmentLevel as LaserCommitmentLevel, - subscribe as LaserSubscribe, - LaserstreamConfig, - SubscribeRequest, - SubscribeUpdate, - CompressionAlgorithms, -} from 'helius-laserstream'; -import { CommitmentLevel } from '@triton-one/yellowstone-grpc'; - -type LaserCommitment = - (typeof LaserCommitmentLevel)[keyof typeof LaserCommitmentLevel]; - -export class LaserstreamProgramAccountSubscriber< - T, -> extends WebSocketProgramAccountSubscriber { - private stream: - | { - id: string; - cancel: () => void; - write?: (req: SubscribeRequest) => Promise; - } - | undefined; - - private commitmentLevel: CommitmentLevel; - public listenerId?: number; - - private readonly laserConfig: LaserstreamConfig; - - private constructor( - laserConfig: LaserstreamConfig, - commitmentLevel: CommitmentLevel, - subscriptionName: string, - accountDiscriminator: string, - program: Program, - decodeBufferFn: (accountName: string, ix: Buffer) => T, - options: { filters: MemcmpFilter[] } = { filters: [] }, - resubOpts?: ResubOpts - ) { - super( - subscriptionName, - accountDiscriminator, - program, - decodeBufferFn, - options, - resubOpts - ); - this.laserConfig = laserConfig; - this.commitmentLevel = this.toLaserCommitment(commitmentLevel); - } - - public static async create( - grpcConfigs: GrpcConfigs, - subscriptionName: string, - accountDiscriminator: string, - program: Program, - decodeBufferFn: (accountName: string, ix: Buffer) => U, - options: { filters: MemcmpFilter[] } = { - filters: [], - }, - resubOpts?: ResubOpts - ): Promise> { - const laserConfig: LaserstreamConfig = { - apiKey: grpcConfigs.token, - endpoint: grpcConfigs.endpoint, - maxReconnectAttempts: grpcConfigs.enableReconnect ? 10 : 0, - channelOptions: { - 'grpc.default_compression_algorithm': CompressionAlgorithms.zstd, - 'grpc.max_receive_message_length': 1_000_000_000, - }, - }; - - const commitmentLevel = - grpcConfigs.commitmentLevel ?? CommitmentLevel.CONFIRMED; - - return new LaserstreamProgramAccountSubscriber( - laserConfig, - commitmentLevel, - subscriptionName, - accountDiscriminator, - program, - decodeBufferFn, - options, - resubOpts - ); - } - - async subscribe( - onChange: ( - accountId: PublicKey, - data: T, - context: Context, - buffer: Buffer - ) => void - ): Promise { - if (this.listenerId != null || this.isUnsubscribing) return; - - this.onChange = onChange; - - const filters = this.options.filters.map((filter) => { - return { - memcmp: { - offset: filter.memcmp.offset, - base58: filter.memcmp.bytes, - }, - }; - }); - - const request: SubscribeRequest = { - slots: {}, - accounts: { - drift: { - account: [], - owner: [this.program.programId.toBase58()], - filters, - }, - }, - transactions: {}, - blocks: {}, - blocksMeta: {}, - accountsDataSlice: [], - commitment: this.commitmentLevel, - entry: {}, - transactionsStatus: {}, - }; - - try { - const stream = await LaserSubscribe( - this.laserConfig, - request, - async (update: SubscribeUpdate) => { - if (update.account) { - const slot = Number(update.account.slot); - const acc = update.account.account; - - const accountInfo = { - owner: new PublicKey(acc.owner), - lamports: Number(acc.lamports), - data: Buffer.Buffer.from(acc.data), - executable: acc.executable, - rentEpoch: Number(acc.rentEpoch), - }; - - const payload = { - accountId: new PublicKey(acc.pubkey), - accountInfo, - }; - - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - clearTimeout(this.timeoutId); - this.handleRpcResponse({ slot }, payload); - this.setTimeout(); - } else { - this.handleRpcResponse({ slot }, payload); - } - } - }, - async (error) => { - console.error('LaserStream client error:', error); - throw error; - } - ); - - this.stream = stream; - this.listenerId = 1; - - if (this.resubOpts?.resubTimeoutMs) { - this.receivingData = true; - this.setTimeout(); - } - } catch (err) { - console.error('Failed to start LaserStream client:', err); - throw err; - } - } - - public async unsubscribe(onResub = false): Promise { - if (!onResub && this.resubOpts) { - this.resubOpts.resubTimeoutMs = undefined; - } - this.isUnsubscribing = true; - clearTimeout(this.timeoutId); - this.timeoutId = undefined; - - if (this.listenerId != null && this.stream) { - try { - this.stream.cancel(); - } finally { - this.listenerId = undefined; - this.isUnsubscribing = false; - } - } else { - this.isUnsubscribing = false; - } - } - - public toLaserCommitment( - level: string | number | undefined - ): LaserCommitment { - if (typeof level === 'string') { - return ( - (LaserCommitmentLevel as any)[level.toUpperCase()] ?? - LaserCommitmentLevel.CONFIRMED - ); - } - return (level as LaserCommitment) ?? LaserCommitmentLevel.CONFIRMED; - } -} diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index 80fe5a43dc..98ab7133fb 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -234,7 +234,6 @@ export type GrpcConfigs = { * Defaults to false, will throw on connection loss. */ enableReconnect?: boolean; - client?: 'yellowstone' | 'lazer'; }; export interface HighLeverageModeConfigAccountSubscriber { diff --git a/sdk/src/orderSubscriber/grpcSubscription.ts b/sdk/src/orderSubscriber/grpcSubscription.ts index 795f10ff81..41101435ab 100644 --- a/sdk/src/orderSubscriber/grpcSubscription.ts +++ b/sdk/src/orderSubscriber/grpcSubscription.ts @@ -5,7 +5,6 @@ import { OrderSubscriber } from './OrderSubscriber'; import { GrpcConfigs, ResubOpts } from '../accounts/types'; import { UserAccount } from '../types'; import { getUserFilter, getNonIdleUserFilter } from '../memcmp'; -import { LaserstreamProgramAccountSubscriber } from '../accounts/lazerProgramAccountSubscriber'; export class grpcSubscription { private orderSubscriber: OrderSubscriber; @@ -13,9 +12,7 @@ export class grpcSubscription { private resubOpts?: ResubOpts; private resyncIntervalMs?: number; - private subscriber?: - | grpcProgramAccountSubscriber - | LaserstreamProgramAccountSubscriber; + private subscriber?: grpcProgramAccountSubscriber; private resyncTimeoutId?: ReturnType; private decoded?: boolean; @@ -50,32 +47,17 @@ export class grpcSubscription { return; } - if (this.grpcConfigs.client === 'lazer') { - this.subscriber = - await LaserstreamProgramAccountSubscriber.create( - this.grpcConfigs, - 'OrderSubscriber', - 'User', - this.orderSubscriber.driftClient.program, - this.orderSubscriber.decodeFn, - { - filters: [getUserFilter(), getNonIdleUserFilter()], - }, - this.resubOpts - ); - } else { - this.subscriber = await grpcProgramAccountSubscriber.create( - this.grpcConfigs, - 'OrderSubscriber', - 'User', - this.orderSubscriber.driftClient.program, - this.orderSubscriber.decodeFn, - { - filters: [getUserFilter(), getNonIdleUserFilter()], - }, - this.resubOpts - ); - } + this.subscriber = await grpcProgramAccountSubscriber.create( + this.grpcConfigs, + 'OrderSubscriber', + 'User', + this.orderSubscriber.driftClient.program, + this.orderSubscriber.decodeFn, + { + filters: [getUserFilter(), getNonIdleUserFilter()], + }, + this.resubOpts + ); await this.subscriber.subscribe( ( diff --git a/sdk/yarn.lock b/sdk/yarn.lock index fdcc859a96..960207492a 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -1281,13 +1281,6 @@ dependencies: undici-types "~5.26.4" -"@types/protobufjs@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@types/protobufjs/-/protobufjs-6.0.0.tgz#aeabb43f9507bb19c8adfb479584c151082353e4" - integrity sha512-A27RDExpAf3rdDjIrHKiJK6x8kqqJ4CmoChwtipfhVAn1p7+wviQFFP7dppn8FslSbHtQeVPvi8wNKkDjSYjHw== - dependencies: - protobufjs "*" - "@types/semver@^7.5.0": version "7.7.0" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.0.tgz#64c441bdae033b378b6eef7d0c3d77c329b9378e" @@ -2794,51 +2787,6 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -helius-laserstream-darwin-arm64@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream-darwin-arm64/-/helius-laserstream-darwin-arm64-0.1.7.tgz#c84df402bdb8a2159bcfc2711bf2b64fe09edd24" - integrity sha512-oMkt6qr7EQLfgiOCVO/9lTQLi8futBVqhUSRvsCmYcAqmaFNsvmf+/rRVq/o56+iq0PseqV6DcRF+5s88tYEIg== - -helius-laserstream-darwin-x64@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream-darwin-x64/-/helius-laserstream-darwin-x64-0.1.7.tgz#fe15b0513a8d1f55075f27b1a5836bf73ec470ff" - integrity sha512-88utpg/ZMtsnF9RX268D50fl6B3kQOFS0nCzoenS1CwMvMprTqHSEDqYFgBX8O7t52gox5aw8+x3XqhHX0fIMQ== - -helius-laserstream-linux-arm64-gnu@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream-linux-arm64-gnu/-/helius-laserstream-linux-arm64-gnu-0.1.7.tgz#cc7a636bc12961d314a4c46008d63f4fb6fe6b03" - integrity sha512-4YQuISaa3OWOBQCUnslT+HguuGRMO1KRQeWSjtuSHDYn7oO/KXePCtEo9vqnQx7HtQFDnS1/kuFExIa2L4Sp2w== - -helius-laserstream-linux-arm64-musl@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream-linux-arm64-musl/-/helius-laserstream-linux-arm64-musl-0.1.7.tgz#96dcf87b855698e9f28214d96031d0934324db45" - integrity sha512-VyN/5nzqUtBN88PLDIMVJmFDgImMCL1sjBWKf49ppAWKN3LVvYVYT8tGMi4nzb5vj7ObduIi1ZZ+cGWNM6H2kA== - -helius-laserstream-linux-x64-gnu@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream-linux-x64-gnu/-/helius-laserstream-linux-x64-gnu-0.1.7.tgz#0bcaed9697b975033e86a74ab4d734ee3d4d3e1c" - integrity sha512-0//p5wlITWbWKBaW2CIYfS3/9fBJCvMn8fBvBKot28psIWSQ6Uc5u/IqS2ls438NvTiEvBp6pgScWoYHXKU+VQ== - -helius-laserstream-linux-x64-musl@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream-linux-x64-musl/-/helius-laserstream-linux-x64-musl-0.1.7.tgz#6c38ca7f97bd1ff46947794fd907ecd8492a0249" - integrity sha512-4VFxsKE+X3Jj/DBrdKQCUs/6ljuYadiPrF5QBMIyaflzZka/hOOvd2bDAo8Bi9/qGGCCaJG6F3U3OFKGKSiE9w== - -helius-laserstream@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/helius-laserstream/-/helius-laserstream-0.1.7.tgz#67f9d570f56ba9bb801d210ebff5e9cf1cc97faa" - integrity sha512-xsCbc8dApJpLb6OShOCeJ5/6pQMdGk6sQojEgihTNGZaGhaAzwYzJcxL5q4uszE3qG/viJO67Mi/MxArDb+QaQ== - dependencies: - "@types/protobufjs" "^6.0.0" - protobufjs "^7.5.3" - optionalDependencies: - helius-laserstream-darwin-arm64 "0.1.7" - helius-laserstream-darwin-x64 "0.1.7" - helius-laserstream-linux-arm64-gnu "0.1.7" - helius-laserstream-linux-arm64-musl "0.1.7" - helius-laserstream-linux-x64-gnu "0.1.7" - helius-laserstream-linux-x64-musl "0.1.7" - humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -3687,24 +3635,6 @@ pretty-ms@^7.0.1: dependencies: parse-ms "^2.1.0" -protobufjs@*, protobufjs@^7.5.3: - version "7.5.4" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a" - integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - protobufjs@^7.2.5, protobufjs@^7.4.0: version "7.5.3" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.3.tgz#13f95a9e3c84669995ec3652db2ac2fb00b89363" From 0fafeb2920a98f6c6478409550a1538de2555c23 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 01:23:14 +0000 Subject: [PATCH 190/216] sdk: release v2.138.0-beta.5 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index ec4ab7a37e..592728499b 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.4 \ No newline at end of file +2.138.0-beta.5 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 896fd77fc7..d1a6250ca4 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.4", + "version": "2.138.0-beta.5", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 06116f181e53ae3b5742e64401fd360c22725408 Mon Sep 17 00:00:00 2001 From: LukasDeco Date: Wed, 17 Sep 2025 11:40:52 -0600 Subject: [PATCH 191/216] feat: pin deps away from mal packages (#1858) * feat: pin deps away from mal packages * fix: chalk deps break lint * fix: linter unix format * try fix broken anchor tests --------- Co-authored-by: Nick Caradonna --- package.json | 42 ++++++++++++++++++++++- sdk/bun.lock | 88 +++++++++++++++++++++++++++++++++++------------- sdk/package.json | 42 +++++++++++++++++++++-- 3 files changed, 145 insertions(+), 27 deletions(-) diff --git a/package.json b/package.json index 9470e1191a..de9fdd10f5 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,51 @@ "prepare": "husky install", "prettify": "prettier --check './sdk/src/**/*.ts' './tests/**.ts' './cli/**.ts'", "prettify:fix": "prettier --write './sdk/src/**/*.ts' './tests/**.ts' './cli/**.ts'", - "lint": "eslint . --ext ts --quiet", + "lint": "eslint . --ext ts --quiet --format unix", "lint:fix": "eslint . --ext ts --fix", "update-idl": "cp target/idl/drift.json sdk/src/idl/drift.json" }, "engines": { "node": ">=12" + }, + "resolutions": { + "chalk": "4.1.2", + "debug": "<4.4.2", + "ansi-styles": "4.3.0", + "supports-color": "7.2.0", + "strip-ansi": "6.0.1", + "ansi-regex": "5.0.1", + "wrap-ansi": "7.0.0", + "color-convert": "<3.1.1", + "color-name": "<2.0.1", + "color-string": "<2.1.1", + "simple-swizzle": "<0.2.3", + "is-arrayish": "<0.3.3", + "slice-ansi": "3.0.0", + "error-ex": "<1.3.3", + "backslash": "<0.2.1", + "chalk-template": "<1.1.1", + "supports-hyperlinks": "<4.1.1", + "has-ansi": "<6.0.1" + }, + "overrides": { + "chalk": "4.1.2", + "debug": "<4.4.2", + "ansi-styles": "4.3.0", + "supports-color": "7.2.0", + "strip-ansi": "6.0.1", + "ansi-regex": "5.0.1", + "wrap-ansi": "7.0.0", + "color-convert": "<3.1.1", + "color-name": "<2.0.1", + "color-string": "<2.1.1", + "simple-swizzle": "<0.2.3", + "is-arrayish": "<0.3.3", + "slice-ansi": "3.0.0", + "error-ex": "<1.3.3", + "backslash": "<0.2.1", + "chalk-template": "<1.1.1", + "supports-hyperlinks": "<4.1.1", + "has-ansi": "<6.0.1" } } \ No newline at end of file diff --git a/sdk/bun.lock b/sdk/bun.lock index bb1e7df66d..b97bf95c4b 100644 --- a/sdk/bun.lock +++ b/sdk/bun.lock @@ -29,7 +29,7 @@ "tweetnacl-util": "0.15.1", "uuid": "8.3.2", "yargs": "17.7.2", - "zod": "^4.0.17", + "zod": "4.0.17", "zstddec": "0.1.0", }, "devDependencies": { @@ -58,9 +58,15 @@ }, }, "overrides": { - "@solana/codecs-data-structures": "2.0.0-preview.4", - "@solana/errors": "2.0.0-preview.4", - "@solana/web3.js": "1.98.0", + "debug": "<4.4.2", + "supports-color": "7.2.0", + "ansi-regex": "5.0.1", + "color-convert": "<3.1.1", + "ansi-styles": "4.3.0", + "wrap-ansi": "7.0.0", + "chalk": "4.1.2", + "strip-ansi": "6.0.1", + "color-name": "<2.0.1", }, "packages": { "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], @@ -211,13 +217,13 @@ "@solana/codecs-core": ["@solana/codecs-core@2.3.0", "", { "dependencies": { "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw=="], - "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-preview.4", "", { "dependencies": { "@solana/codecs-core": "2.0.0-preview.4", "@solana/codecs-numbers": "2.0.0-preview.4", "@solana/errors": "2.0.0-preview.4" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA=="], + "@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-qvU5LE5DqEdYMYgELRHv+HMOx73sSoV1ZZkwIrclwUmwTbTaH8QAJURBj0RhQ/zCne7VuLLOZFFGv6jGigWhSw=="], "@solana/codecs-numbers": ["@solana/codecs-numbers@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg=="], "@solana/codecs-strings": ["@solana/codecs-strings@2.3.0", "", { "dependencies": { "@solana/codecs-core": "2.3.0", "@solana/codecs-numbers": "2.3.0", "@solana/errors": "2.3.0" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5.3.3" } }, "sha512-y5pSBYwzVziXu521hh+VxqUtp0hYGTl1eWGoc1W+8mdvBdC1kTqm/X7aYQw33J42hw03JjryvYOvmGgk3Qz/Ug=="], - "@solana/errors": ["@solana/errors@2.0.0-preview.4", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA=="], + "@solana/errors": ["@solana/errors@2.3.0", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^14.0.0" }, "peerDependencies": { "typescript": ">=5.3.3" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ=="], "@solana/fast-stable-stringify": ["@solana/fast-stable-stringify@2.3.0", "", { "peerDependencies": { "typescript": ">=5.3.3" } }, "sha512-KfJPrMEieUg6D3hfQACoPy0ukrAV8Kio883llt/8chPEG3FVTX9z/Zuf4O01a15xZmBbmQ7toil2Dp0sxMJSxw=="], @@ -391,7 +397,7 @@ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "ansicolors": ["ansicolors@0.3.2", "", {}, "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg=="], @@ -483,7 +489,7 @@ "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + "color-convert": ["color-convert@3.1.0", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-TVoqAq8ZDIpK5lsQY874DDnu65CSsc9vzq0wLpNQ6UMBq81GSZocVazPiBbYGzngzBOIRahpkTzCLVe2at4MfA=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -1047,7 +1053,7 @@ "superstruct": ["superstruct@0.15.5", "", {}, "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ=="], - "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -1173,13 +1179,7 @@ "@sinonjs/commons/type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], - "@solana/codecs-data-structures/@solana/codecs-core": ["@solana/codecs-core@2.0.0-preview.4", "", { "dependencies": { "@solana/errors": "2.0.0-preview.4" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw=="], - - "@solana/codecs-data-structures/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-preview.4", "", { "dependencies": { "@solana/codecs-core": "2.0.0-preview.4", "@solana/errors": "2.0.0-preview.4" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ=="], - - "@solana/errors/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "@solana/errors/commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], "@solana/rpc-transport-http/undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], @@ -1201,10 +1201,6 @@ "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "defaults/clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], @@ -1231,6 +1227,8 @@ "jayson/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], + "jito-ts/@solana/web3.js": ["@solana/web3.js@1.77.4", "", { "dependencies": { "@babel/runtime": "^7.12.5", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.3.0", "@solana/buffer-layout": "^4.0.0", "agentkeepalive": "^4.2.1", "bigint-buffer": "^1.1.5", "bn.js": "^5.0.0", "borsh": "^0.7.0", "bs58": "^4.0.1", "buffer": "6.0.3", "fast-stable-stringify": "^1.0.0", "jayson": "^4.1.0", "node-fetch": "^2.6.7", "rpc-websockets": "^7.5.1", "superstruct": "^0.14.2" } }, "sha512-XdN0Lh4jdY7J8FYMyucxCwzn6Ga2Sr1DHDWRbqVzk7ZPmmpSPOVWHzO67X1cVT+jNi1D6gZi2tgjHgDPuj6e9Q=="], + "jito-ts/dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], "jito-ts/superstruct": ["superstruct@1.0.4", "", {}, "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ=="], @@ -1257,16 +1255,12 @@ "sass-lookup/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], - "sinon/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], "stylus-lookup/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], "ts-node/diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], "@ellipsis-labs/phoenix-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], @@ -1279,6 +1273,8 @@ "@solana/spl-token-group/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/spl-token-group/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], @@ -1287,6 +1283,8 @@ "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core": ["@solana/codecs-core@2.0.0-rc.1", "", { "dependencies": { "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-data-structures": ["@solana/codecs-data-structures@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog=="], + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings": ["@solana/codecs-strings@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/codecs-numbers": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "fastestsmallesttextencoderdecoder": "^1.0.22", "typescript": ">=5" } }, "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g=="], @@ -1313,12 +1311,54 @@ "glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "jito-ts/@solana/web3.js/superstruct": ["superstruct@0.14.2", "", {}, "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ=="], + "mocha/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], "mocha/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + "@solana/spl-token-group/@solana/codecs/@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-group/@solana/codecs/@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-data-structures/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/options/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], + "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], "detective-typescript/@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-group/@solana/codecs/@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-data-structures/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-numbers/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/codecs-strings/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + + "@solana/spl-token-metadata/@solana/codecs/@solana/options/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], } } diff --git a/sdk/package.json b/sdk/package.json index d1a6250ca4..569f869dcd 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -11,7 +11,7 @@ "url": "git@github.com:drift-labs/protocol-v2.git" }, "scripts": { - "lint": "eslint './**/*.{ts,tsx}' --quiet", + "lint": "eslint './**/*.{ts,tsx}' --quiet --format unix", "build": "yarn clean && tsc -p tsconfig.json && tsc -p tsconfig.browser.json && node scripts/postbuild.js", "build:browser": "yarn clean && tsc -p tsconfig.json && tsc -p tsconfig.browser.json && node scripts/postbuild.js --force-env browser", "clean": "rm -rf lib", @@ -97,6 +97,44 @@ "resolutions": { "@solana/web3.js": "1.98.0", "@solana/errors": "2.0.0-preview.4", - "@solana/codecs-data-structures": "2.0.0-preview.4" + "@solana/codecs-data-structures": "2.0.0-preview.4", + "chalk": "4.1.2", + "debug": "<4.4.2", + "ansi-styles": "4.3.0", + "supports-color": "7.2.0", + "strip-ansi": "6.0.1", + "ansi-regex": "5.0.1", + "wrap-ansi": "7.0.0", + "color-convert": "<3.1.1", + "color-name": "<2.0.1", + "color-string": "<2.1.1", + "simple-swizzle": "<0.2.3", + "is-arrayish": "<0.3.3", + "slice-ansi": "3.0.0", + "error-ex": "<1.3.3", + "backslash": "<0.2.1", + "chalk-template": "<0.4.0", + "supports-hyperlinks": "<4.1.1", + "has-ansi": "<6.0.1" + }, + "overrides": { + "chalk": "4.1.2", + "debug": "<4.4.2", + "ansi-styles": "4.3.0", + "supports-color": "7.2.0", + "strip-ansi": "6.0.1", + "ansi-regex": "5.0.1", + "wrap-ansi": "7.0.0", + "color-convert": "<3.1.1", + "color-name": "<2.0.1", + "color-string": "<2.1.1", + "simple-swizzle": "<0.2.3", + "is-arrayish": "<0.3.3", + "slice-ansi": "3.0.0", + "error-ex": "<1.3.3", + "backslash": "<0.2.1", + "chalk-template": "<1.1.1", + "supports-hyperlinks": "<4.1.1", + "has-ansi": "<6.0.1" } } From 1e29984c8fabc6c0a0170722dbfb0e23e2cb4800 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:46:03 +0000 Subject: [PATCH 192/216] sdk: release v2.138.0-beta.6 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- sdk/yarn.lock | 117 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 33 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 592728499b..512f6530a1 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.5 \ No newline at end of file +2.138.0-beta.6 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 569f869dcd..522c3f4c08 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.5", + "version": "2.138.0-beta.6", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/yarn.lock b/sdk/yarn.lock index 960207492a..4013c2cc05 100644 --- a/sdk/yarn.lock +++ b/sdk/yarn.lock @@ -1552,23 +1552,18 @@ ansi-colors@^4.1.3: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== -ansi-regex@^5.0.1: +ansi-regex@5.0.1, ansi-regex@^5.0.1, ansi-regex@^6.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^4.0.0, ansi-styles@^4.1.0: +ansi-styles@4.3.0, ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^5.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - ansicolors@^0.3.2, ansicolors@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" @@ -1628,6 +1623,11 @@ ast-module-types@^6.0.1: resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-6.0.1.tgz#4b4ca0251c57b815bab62604dcb22f8c903e2523" integrity sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1649,6 +1649,11 @@ axios@^1.8.3, axios@^1.9.0: form-data "^4.0.0" proxy-from-env "^1.1.0" +backslash@<0.2.1: + version "0.2.0" + resolved "https://registry.yarnpkg.com/backslash/-/backslash-0.2.0.tgz#6c3c1fce7e7e714ccfc10fd74f0f73410677375f" + integrity sha512-Avs+8FUZ1HF/VFP4YWwHQZSGzRPm37ukU1JQYQWijuHhtXdOuAzcZ8PcAzfIw898a8PyBzdn+RtnKA6MzW0X2A== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -1858,7 +1863,14 @@ chai@4.5.0: pathval "^1.1.1" type-detect "^4.1.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2, chalk@~4.1.2: +chalk-template@<0.4.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/chalk-template/-/chalk-template-0.3.1.tgz#9511cd31ec3c4911448410d49645526c1c7a3a22" + integrity sha512-sbWkBbb9Tfo81aTtQrfP9eBSVCTL8biVvZ0tA1rH9xqVrKoV2T9Y6Bp94wB+DRXtSGl/UXsgV83Np5hLhNRXww== + dependencies: + chalk "^4.1.2" + +chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2, chalk@^5.3.0, chalk@~4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1866,11 +1878,6 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2, chalk@~4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8" - integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== - check-error@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" @@ -1938,17 +1945,24 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== +color-convert@<3.1.1, color-convert@^2.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-3.1.0.tgz#ce16ebb832f9d7522649ed9e11bc0ccb9433a524" + integrity sha512-TVoqAq8ZDIpK5lsQY874DDnu65CSsc9vzq0wLpNQ6UMBq81GSZocVazPiBbYGzngzBOIRahpkTzCLVe2at4MfA== dependencies: - color-name "~1.1.4" + color-name "^2.0.0" -color-name@^1.1.4, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@<2.0.1, color-name@^1.1.4, color-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-2.0.0.tgz#03ff6b1b5aec9bb3cf1ed82400c2790dfcd01d2d" + integrity sha512-SbtvAMWvASO5TE2QP07jHBMXKafgdZz8Vrsrn96fiL+O92/FN/PLARzUW5sKt013fjAprK2d2iCn2hk2Xb5oow== + +color-string@<2.1.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-2.1.0.tgz#a1cc4bb16a23032ff1048a2458a170323b15a23f" + integrity sha512-gNVoDzpaSwvftp6Y8nqk97FtZoXP9Yj7KGYB8yIXuv0JcfqbYihTrd1OU5iZW9btfXde4YAOCRySBHT7O910MA== + dependencies: + color-name "^2.0.0" combined-stream@^1.0.8: version "1.0.8" @@ -2008,7 +2022,7 @@ crypto-hash@^1.3.0: resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== -debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5: +debug@<4.4.2, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5: version "4.4.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== @@ -2244,6 +2258,13 @@ entities@^4.5.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +error-ex@<1.3.3: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" @@ -2751,11 +2772,23 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +has-ansi@<6.0.1: + version "6.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-6.0.0.tgz#8118b2fb548c062f9356c7d5013b192a238ce3b3" + integrity sha512-1AYj+gqAskFf9Skb7xuEYMfJqkW3TJ8lukw4Fczw+Y6jRkgxvcE4JiFWuTO4DsoleMvvHudryolA9ObJHJKHWQ== + dependencies: + ansi-regex "^6.0.1" + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-flag@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-5.0.1.tgz#5483db2ae02a472d1d0691462fc587d1843cd940" + integrity sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA== + has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -2850,6 +2883,11 @@ is-arguments@^1.0.4: call-bound "^1.0.2" has-tostringtag "^1.0.2" +is-arrayish@<0.3.3, is-arrayish@^0.2.1, is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -3872,6 +3910,13 @@ signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-swizzle@<0.2.3: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + sinon@18.0.1: version "18.0.1" resolved "https://registry.yarnpkg.com/sinon/-/sinon-18.0.1.tgz#464334cdfea2cddc5eda9a4ea7e2e3f0c7a91c5e" @@ -3889,6 +3934,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -4010,7 +4064,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -4054,19 +4108,20 @@ superstruct@^2.0.2: resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-2.0.2.tgz#3f6d32fbdc11c357deff127d591a39b996300c54" integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A== -supports-color@^7, supports-color@^7.1.0: +supports-color@7.2.0, supports-color@^10.0.0, supports-color@^7, supports-color@^7.1.0, supports-color@^8.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" -supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== +supports-hyperlinks@<4.1.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-4.1.0.tgz#f006d9e2f6b9b6672f86c86c6f76bf52a69f4d91" + integrity sha512-6lY0rDZ5bbZhAPrwpz/nMR6XmeaFmh2itk7YnIyph2jblPmDcKMCPkSdLFTlaX8snBvg7OJmaOL3WRLqMEqcJQ== dependencies: - has-flag "^4.0.0" + has-flag "^5.0.1" + supports-color "^10.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" @@ -4320,7 +4375,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -wrap-ansi@^7.0.0: +wrap-ansi@7.0.0, wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 3c0977ad6ea34df9a547b36b9d2cb9c2833b6d54 Mon Sep 17 00:00:00 2001 From: wphan Date: Thu, 18 Sep 2025 09:20:30 -0700 Subject: [PATCH 193/216] Revert "Crispeaney/revert swift max margin ratio" (#1877) * Revert "program: revert swift max margin ratio (#1874)" This reverts commit 87bfe72dfb010fcb0cd94319307a1c4c3ed651f7. * add SignedMsgExtensions enum struct variant * Revert "add SignedMsgExtensions enum struct variant" This reverts commit 9dbe65c0e95c0ab428db3343dbe57b799ee2650c. * add extended SignedMsgOrderParamsMessage variant * zero pad short swift messages when decoding * revert to single sdk decode function * cargo fmt * comments * use fixed padding for sdk swift decode fn * fix comments --- CHANGELOG.md | 1 + programs/drift/src/instructions/keeper.rs | 4 + programs/drift/src/state/order_params.rs | 2 + .../drift/src/validation/sig_verification.rs | 108 ++++--- .../src/validation/sig_verification/tests.rs | 293 ++++++++++++++++++ sdk/src/driftClient.ts | 14 +- sdk/src/idl/drift.json | 19 +- sdk/src/types.ts | 2 + tests/placeAndMakeSignedMsgBankrun.ts | 101 +++++- 9 files changed, 497 insertions(+), 47 deletions(-) create mode 100644 programs/drift/src/validation/sig_verification/tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3492f6be78..c87e67e3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - program: post only respects reduce only ([#1878](https://github.com/drift-labs/protocol-v2/pull/1878)) - program: add sequence id to exchange/mm oracle ([#1834](https://github.com/drift-labs/protocol-v2/pull/1834)) - program: perp position max margin ratio ([#1847](https://github.com/drift-labs/protocol-v2/pull/1847)) +- program: add padding to swift messages ([#1845](https://github.com/drift-labs/protocol-v2/pull/1845)) - program: rm lp ([#1755](https://github.com/drift-labs/protocol-v2/pull/1755)) ### Fixes diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 2bf1a003a1..85a4989425 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -762,6 +762,10 @@ pub fn place_signed_msg_taker_order<'c: 'info, 'info>( return Ok(()); } + if let Some(max_margin_ratio) = verified_message_and_signature.max_margin_ratio { + taker.update_perp_position_max_margin_ratio(market_index, max_margin_ratio)?; + } + // Dont place order if signed msg order already exists let mut taker_order_id_to_use = taker.next_order_id; let mut signed_msg_order_id = diff --git a/programs/drift/src/state/order_params.rs b/programs/drift/src/state/order_params.rs index 48bc7399d2..69e1c6710d 100644 --- a/programs/drift/src/state/order_params.rs +++ b/programs/drift/src/state/order_params.rs @@ -864,6 +864,7 @@ pub struct SignedMsgOrderParamsMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, + pub max_margin_ratio: Option, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] @@ -874,6 +875,7 @@ pub struct SignedMsgOrderParamsDelegateMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, + pub max_margin_ratio: Option, } #[derive(AnchorSerialize, AnchorDeserialize, Clone, Default, Eq, PartialEq, Debug)] diff --git a/programs/drift/src/validation/sig_verification.rs b/programs/drift/src/validation/sig_verification.rs index 3349c7d5d7..66ba1cab98 100644 --- a/programs/drift/src/validation/sig_verification.rs +++ b/programs/drift/src/validation/sig_verification.rs @@ -14,6 +14,9 @@ use solana_program::program_memory::sol_memcmp; use solana_program::sysvar; use std::convert::TryInto; +#[cfg(test)] +mod tests; + const ED25519_PROGRAM_INPUT_HEADER_LEN: usize = 2; const SIGNATURE_LEN: u16 = 64; @@ -45,6 +48,7 @@ pub struct Ed25519SignatureOffsets { pub message_instruction_index: u16, } +#[derive(Debug)] pub struct VerifiedMessage { pub signed_msg_order_params: OrderParams, pub sub_account_id: Option, @@ -53,6 +57,7 @@ pub struct VerifiedMessage { pub uuid: [u8; 8], pub take_profit_order_params: Option, pub stop_loss_order_params: Option, + pub max_margin_ratio: Option, pub signature: [u8; 64], } @@ -60,6 +65,69 @@ fn slice_eq(a: &[u8], b: &[u8]) -> bool { a.len() == b.len() && sol_memcmp(a, b, a.len()) == 0 } +pub fn deserialize_into_verified_message( + payload: Vec, + signature: &[u8; 64], + is_delegate_signer: bool, +) -> Result { + if is_delegate_signer { + if payload.len() < 8 { + return Err(SignatureVerificationError::InvalidMessageDataSize.into()); + } + let min_len: usize = std::mem::size_of::(); + let mut owned = payload; + if owned.len() < min_len { + owned.resize(min_len, 0); + } + let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize( + &mut &owned[8..], // 8 byte manual discriminator + ) + .map_err(|_| { + msg!("Invalid message encoding for is_delegate_signer = true"); + SignatureVerificationError::InvalidMessageDataSize + })?; + + return Ok(VerifiedMessage { + signed_msg_order_params: deserialized.signed_msg_order_params, + sub_account_id: None, + delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey), + slot: deserialized.slot, + uuid: deserialized.uuid, + take_profit_order_params: deserialized.take_profit_order_params, + stop_loss_order_params: deserialized.stop_loss_order_params, + max_margin_ratio: deserialized.max_margin_ratio, + signature: *signature, + }); + } else { + if payload.len() < 8 { + return Err(SignatureVerificationError::InvalidMessageDataSize.into()); + } + let min_len: usize = std::mem::size_of::(); + let mut owned = payload; + if owned.len() < min_len { + owned.resize(min_len, 0); + } + let deserialized = SignedMsgOrderParamsMessage::deserialize( + &mut &owned[8..], // 8 byte manual discriminator + ) + .map_err(|_| { + msg!("Invalid delegate message encoding for with is_delegate_signer = false"); + SignatureVerificationError::InvalidMessageDataSize + })?; + return Ok(VerifiedMessage { + signed_msg_order_params: deserialized.signed_msg_order_params, + sub_account_id: Some(deserialized.sub_account_id), + delegate_signed_taker_pubkey: None, + slot: deserialized.slot, + uuid: deserialized.uuid, + take_profit_order_params: deserialized.take_profit_order_params, + stop_loss_order_params: deserialized.stop_loss_order_params, + max_margin_ratio: deserialized.max_margin_ratio, + signature: *signature, + }); + } +} + /// Check Ed25519Program instruction data verifies the given msg /// /// `ix` an Ed25519Program instruction [see](https://github.com/solana-labs/solana/blob/master/sdk/src/ed25519_instruction.rs)) @@ -232,45 +300,7 @@ pub fn verify_and_decode_ed25519_msg( let payload = hex::decode(payload).map_err(|_| SignatureVerificationError::InvalidMessageHex)?; - if is_delegate_signer { - let deserialized = SignedMsgOrderParamsDelegateMessage::deserialize( - &mut &payload[8..], // 8 byte manual discriminator - ) - .map_err(|_| { - msg!("Invalid message encoding for is_delegate_signer = true"); - SignatureVerificationError::InvalidMessageDataSize - })?; - - return Ok(VerifiedMessage { - signed_msg_order_params: deserialized.signed_msg_order_params, - sub_account_id: None, - delegate_signed_taker_pubkey: Some(deserialized.taker_pubkey), - slot: deserialized.slot, - uuid: deserialized.uuid, - take_profit_order_params: deserialized.take_profit_order_params, - stop_loss_order_params: deserialized.stop_loss_order_params, - signature: *signature, - }); - } else { - let deserialized = SignedMsgOrderParamsMessage::deserialize( - &mut &payload[8..], // 8 byte manual discriminator - ) - .map_err(|_| { - msg!("Invalid delegate message encoding for with is_delegate_signer = false"); - SignatureVerificationError::InvalidMessageDataSize - })?; - - return Ok(VerifiedMessage { - signed_msg_order_params: deserialized.signed_msg_order_params, - sub_account_id: Some(deserialized.sub_account_id), - delegate_signed_taker_pubkey: None, - slot: deserialized.slot, - uuid: deserialized.uuid, - take_profit_order_params: deserialized.take_profit_order_params, - stop_loss_order_params: deserialized.stop_loss_order_params, - signature: *signature, - }); - } + deserialize_into_verified_message(payload, signature, is_delegate_signer) } #[error_code] diff --git a/programs/drift/src/validation/sig_verification/tests.rs b/programs/drift/src/validation/sig_verification/tests.rs new file mode 100644 index 0000000000..fae2456b43 --- /dev/null +++ b/programs/drift/src/validation/sig_verification/tests.rs @@ -0,0 +1,293 @@ +mod sig_verification { + use std::str::FromStr; + + use anchor_lang::prelude::Pubkey; + + use crate::controller::position::PositionDirection; + use crate::validation::sig_verification::deserialize_into_verified_message; + + #[test] + fn test_deserialize_into_verified_message_non_delegate() { + let signature = [1u8; 64]; + let payload = vec![ + 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 1, 0, 202, 154, 59, 0, 0, 0, 0, 0, 248, + 89, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 192, 181, 74, 13, 0, 0, 0, 0, + 1, 0, 248, 89, 13, 0, 0, 0, 0, 0, 0, 232, 3, 0, 0, 0, 0, 0, 0, 72, 112, 54, 84, 106, + 83, 48, 107, 0, 0, + ]; + + // Test deserialization with non-delegate signer + let result = deserialize_into_verified_message(payload, &signature, false); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, Some(0)); + assert_eq!(verified_message.delegate_signed_taker_pubkey, None); + assert_eq!(verified_message.slot, 1000); + assert_eq!(verified_message.uuid, [72, 112, 54, 84, 106, 83, 48, 107]); + assert!(verified_message.take_profit_order_params.is_none()); + assert!(verified_message.stop_loss_order_params.is_none()); + assert!(verified_message.max_margin_ratio.is_none()); + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 1); + assert_eq!(order_params.direction, PositionDirection::Long); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 224000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(223000000i64)); + assert_eq!(order_params.auction_end_price, Some(224000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_non_delegate_with_tpsl() { + let signature = [1u8; 64]; + let payload = vec![ + 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 3, 0, 96, 254, 205, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, + 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, + 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, + 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, false); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, Some(2)); + assert_eq!(verified_message.delegate_signed_taker_pubkey, None); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_none()); + + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 3456000000u64); + assert_eq!(tp.trigger_price, 240000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 3456000000u64); + assert_eq!(sl.trigger_price, 225000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 3); + assert_eq!(order_params.direction, PositionDirection::Long); + assert_eq!(order_params.base_asset_amount, 3456000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(230000000i64)); + assert_eq!(order_params.auction_end_price, Some(237000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_non_delegate_with_max_margin_ratio() { + let signature = [1u8; 64]; + let payload = vec![ + 200, 213, 166, 94, 34, 52, 245, 93, 0, 1, 0, 3, 0, 96, 254, 205, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 128, 133, 181, 13, 0, 0, 0, 0, + 1, 64, 85, 32, 14, 0, 0, 0, 0, 2, 0, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, + 71, 49, 1, 0, 28, 78, 14, 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 64, 58, 105, 13, + 0, 0, 0, 0, 0, 96, 254, 205, 0, 0, 0, 0, 1, 1, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, false); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, Some(2)); + assert_eq!(verified_message.delegate_signed_taker_pubkey, None); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_some()); + assert_eq!(verified_message.max_margin_ratio.unwrap(), 1); + + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 3456000000u64); + assert_eq!(tp.trigger_price, 240000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 3456000000u64); + assert_eq!(sl.trigger_price, 225000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 3); + assert_eq!(order_params.direction, PositionDirection::Long); + assert_eq!(order_params.base_asset_amount, 3456000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(230000000i64)); + assert_eq!(order_params.auction_end_price, Some(237000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_delegate() { + let signature = [1u8; 64]; + let payload = vec![ + 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, + 128, 151, 47, 14, 0, 0, 0, 0, 242, 208, 117, 159, 92, 135, 34, 224, 147, 14, 64, 92, 7, + 25, 145, 237, 79, 35, 72, 24, 140, 13, 25, 189, 134, 243, 232, 5, 89, 37, 166, 242, 41, + 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 0, 0, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, true); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, None); + assert_eq!( + verified_message.delegate_signed_taker_pubkey, + Some(Pubkey::from_str("HLr2UfL422cakKkaBG4z1bMZrcyhmzX2pHdegjM6fYXB").unwrap()) + ); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.take_profit_order_params.is_none()); + assert!(verified_message.stop_loss_order_params.is_none()); + assert!(verified_message.max_margin_ratio.is_none()); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 2); + assert_eq!(order_params.direction, PositionDirection::Short); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(240000000i64)); + assert_eq!(order_params.auction_end_price, Some(238000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_delegate_with_tpsl() { + let signature = [1u8; 64]; + let payload = vec![ + 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, + 128, 151, 47, 14, 0, 0, 0, 0, 241, 148, 164, 10, 232, 65, 33, 157, 18, 12, 251, 132, + 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, + 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, + 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, + 59, 0, 0, 0, 0, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, true); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, None); + assert_eq!( + verified_message.delegate_signed_taker_pubkey, + Some(Pubkey::from_str("HG2iQKnRkkasrLptwMZewV6wT7KPstw9wkA8yyu8Nx3m").unwrap()) + ); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_none()); + + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 1000000000u64); + assert_eq!(tp.trigger_price, 230000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 1000000000u64); + assert_eq!(sl.trigger_price, 250000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 2); + assert_eq!(order_params.direction, PositionDirection::Short); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(240000000i64)); + assert_eq!(order_params.auction_end_price, Some(238000000i64)); + } + + #[test] + fn test_deserialize_into_verified_message_delegate_with_max_margin_ratio() { + let signature = [1u8; 64]; + let payload = vec![ + 66, 101, 102, 56, 199, 37, 158, 35, 0, 1, 1, 2, 0, 202, 154, 59, 0, 0, 0, 0, 64, 85, + 32, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 1, 0, 28, 78, 14, 0, 0, 0, 0, 1, + 128, 151, 47, 14, 0, 0, 0, 0, 241, 148, 164, 10, 232, 65, 33, 157, 18, 12, 251, 132, + 245, 208, 37, 127, 112, 55, 83, 186, 54, 139, 1, 135, 220, 180, 208, 219, 189, 94, 79, + 148, 41, 9, 0, 0, 0, 0, 0, 0, 67, 82, 79, 51, 105, 114, 71, 49, 1, 128, 133, 181, 13, + 0, 0, 0, 0, 0, 202, 154, 59, 0, 0, 0, 0, 1, 128, 178, 230, 14, 0, 0, 0, 0, 0, 202, 154, + 59, 0, 0, 0, 0, 1, 1, + ]; + + // Test deserialization with delegate signer + let result = deserialize_into_verified_message(payload, &signature, true); + assert!(result.is_ok()); + + let verified_message = result.unwrap(); + + // Verify the deserialized message has expected structure + assert_eq!(verified_message.signature, signature); + assert_eq!(verified_message.sub_account_id, None); + assert_eq!( + verified_message.delegate_signed_taker_pubkey, + Some(Pubkey::from_str("HG2iQKnRkkasrLptwMZewV6wT7KPstw9wkA8yyu8Nx3m").unwrap()) + ); + assert_eq!(verified_message.slot, 2345); + assert_eq!(verified_message.uuid, [67, 82, 79, 51, 105, 114, 71, 49]); + assert!(verified_message.max_margin_ratio.is_some()); + assert_eq!(verified_message.max_margin_ratio.unwrap(), 1); + + assert!(verified_message.take_profit_order_params.is_some()); + let tp = verified_message.take_profit_order_params.unwrap(); + assert_eq!(tp.base_asset_amount, 1000000000u64); + assert_eq!(tp.trigger_price, 230000000u64); + + assert!(verified_message.stop_loss_order_params.is_some()); + let sl = verified_message.stop_loss_order_params.unwrap(); + assert_eq!(sl.base_asset_amount, 1000000000u64); + assert_eq!(sl.trigger_price, 250000000u64); + + // Verify order params + let order_params = &verified_message.signed_msg_order_params; + assert_eq!(order_params.user_order_id, 2); + assert_eq!(order_params.direction, PositionDirection::Short); + assert_eq!(order_params.base_asset_amount, 1000000000u64); + assert_eq!(order_params.price, 237000000u64); + assert_eq!(order_params.market_index, 0); + assert_eq!(order_params.reduce_only, false); + assert_eq!(order_params.auction_duration, Some(10)); + assert_eq!(order_params.auction_start_price, Some(240000000i64)); + assert_eq!(order_params.auction_end_price, Some(238000000i64)); + } +} diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 4a02efca43..e55029c5cc 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -6541,6 +6541,10 @@ export class DriftClient { | SignedMsgOrderParamsDelegateMessage, delegateSigner?: boolean ): Buffer { + if (orderParamsMessage.maxMarginRatio === undefined) { + orderParamsMessage.maxMarginRatio = null; + } + const anchorIxName = delegateSigner ? 'global' + ':' + 'SignedMsgOrderParamsDelegateMessage' : 'global' + ':' + 'SignedMsgOrderParamsMessage'; @@ -6561,7 +6565,10 @@ export class DriftClient { } /* - * Decode signedMsg taker order params from borsh buffer + * Decode signedMsg taker order params from borsh buffer. Zero pads the message in case the + * received message was encoded by an outdated IDL (size will be too small and decode will throw). + * Note: the 128 will be problematic if the type we are expecting to deserializze into is 128 bytes + * larger than the message we are receiving (unlikely, especially if all new fields are Options). */ public decodeSignedMsgOrderParamsMessage( encodedMessage: Buffer, @@ -6572,7 +6579,10 @@ export class DriftClient { : 'SignedMsgOrderParamsMessage'; return this.program.coder.types.decode( decodeStr, - encodedMessage.slice(8) // assumes discriminator + Buffer.concat([ + encodedMessage.slice(8), // strip out discriminator + Buffer.alloc(128), // pad on 128 bytes, this is most efficient way to messages that are too small + ]) ); } diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index f923272c05..64a7b6df03 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -10139,6 +10139,12 @@ "defined": "SignedMsgTriggerOrderParams" } } + }, + { + "name": "maxMarginRatio", + "type": { + "option": "u16" + } } ] } @@ -10186,6 +10192,12 @@ "defined": "SignedMsgTriggerOrderParams" } } + }, + { + "name": "maxMarginRatio", + "type": { + "option": "u16" + } } ] } @@ -15962,8 +15974,5 @@ "name": "InvalidIfRebalanceSwap", "msg": "Invalid If Rebalance Swap" } - ], - "metadata": { - "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" - } -} + ] +} \ No newline at end of file diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 369520dabf..0bf0842314 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -1247,6 +1247,7 @@ export type SignedMsgOrderParamsMessage = { uuid: Uint8Array; takeProfitOrderParams: SignedMsgTriggerOrderParams | null; stopLossOrderParams: SignedMsgTriggerOrderParams | null; + maxMarginRatio?: number | null; }; export type SignedMsgOrderParamsDelegateMessage = { @@ -1256,6 +1257,7 @@ export type SignedMsgOrderParamsDelegateMessage = { takerPubkey: PublicKey; takeProfitOrderParams: SignedMsgTriggerOrderParams | null; stopLossOrderParams: SignedMsgTriggerOrderParams | null; + maxMarginRatio?: number | null; }; export type SignedMsgTriggerOrderParams = { diff --git a/tests/placeAndMakeSignedMsgBankrun.ts b/tests/placeAndMakeSignedMsgBankrun.ts index 173e1364c4..3b23722445 100644 --- a/tests/placeAndMakeSignedMsgBankrun.ts +++ b/tests/placeAndMakeSignedMsgBankrun.ts @@ -1539,7 +1539,7 @@ describe('place and make signedMsg order', () => { ); assert.fail('should fail'); } catch (e) { - assert(e.toString().includes('0x1776')); + assert(e.toString().includes('0x18a5')); // SignedMsgUserContextUserMismatch const takerOrders = takerDriftClient.getUser().getOpenOrders(); assert(takerOrders.length == 0); } @@ -1604,6 +1604,105 @@ describe('place and make signedMsg order', () => { await takerDriftClientUser.unsubscribe(); await takerDriftClient.unsubscribe(); }); + + it('fills signedMsg with max margin ratio ', async () => { + slot = new BN( + await bankrunContextWrapper.connection.toConnection().getSlot() + ); + const [takerDriftClient, takerDriftClientUser] = + await initializeNewTakerClientAndUser( + bankrunContextWrapper, + chProgram, + usdcMint, + usdcAmount, + marketIndexes, + spotMarketIndexes, + oracleInfos, + bulkAccountLoader + ); + await takerDriftClientUser.fetchAccounts(); + + const marketIndex = 0; + const baseAssetAmount = BASE_PRECISION; + const takerOrderParams = getMarketOrderParams({ + marketIndex, + direction: PositionDirection.LONG, + baseAssetAmount, + price: new BN(224).mul(PRICE_PRECISION), + auctionStartPrice: new BN(223).mul(PRICE_PRECISION), + auctionEndPrice: new BN(224).mul(PRICE_PRECISION), + auctionDuration: 10, + userOrderId: 1, + postOnly: PostOnlyParams.NONE, + marketType: MarketType.PERP, + }) as OrderParams; + + await takerDriftClientUser.fetchAccounts(); + const makerOrderParams = getLimitOrderParams({ + marketIndex, + direction: PositionDirection.SHORT, + baseAssetAmount, + price: new BN(223).mul(PRICE_PRECISION), + postOnly: PostOnlyParams.MUST_POST_ONLY, + bitFlags: 1, + marketType: MarketType.PERP, + }) as OrderParams; + + const uuid = Uint8Array.from(Buffer.from(nanoid(8))); + const takerOrderParamsMessage: SignedMsgOrderParamsMessage = { + signedMsgOrderParams: takerOrderParams, + subAccountId: 0, + slot, + uuid, + stopLossOrderParams: null, + takeProfitOrderParams: null, + maxMarginRatio: 100, + }; + + const signedOrderParams = takerDriftClient.signSignedMsgOrderParamsMessage( + takerOrderParamsMessage + ); + + const ixs = await makerDriftClient.getPlaceAndMakeSignedMsgPerpOrderIxs( + signedOrderParams, + uuid, + { + taker: await takerDriftClient.getUserAccountPublicKey(), + takerUserAccount: takerDriftClient.getUserAccount(), + takerStats: takerDriftClient.getUserStatsAccountPublicKey(), + signingAuthority: takerDriftClient.wallet.publicKey, + }, + makerOrderParams, + undefined, + undefined, + undefined, + 2 + ); + + /* + Transaction size should be largest for filling with trigger orders w/ place and take + Max size: 1232 + We currently trade on sol market w/ sol oracle so would be better with LUT, so -64 bytes + 2 bytes + We dont have referrers for maker so need to add 64 bytes + We want to allow for positions to be full with maximally different markets for maker/taker and spot/perp, + so add 30 bytes for market/oracle for taker and 30 bytes for maker + Add 32 bytes for LUT + size of transaction + 32 + 2 + 30 + 30 < 1232 + */ + assert(getSizeOfTransaction(ixs, false) < 1138); + + const tx = await makerDriftClient.buildTransaction(ixs); + await makerDriftClient.sendTransaction(tx as Transaction); + + const takerPosition = takerDriftClient.getUser().getPerpPosition(0); + + // All orders are placed and one is + // @ts-ignore + assert(takerPosition.maxMarginRatio === 100); + + await takerDriftClientUser.unsubscribe(); + await takerDriftClient.unsubscribe(); + }); }); async function initializeNewTakerClientAndUser( From c565cc8ebf0f99e2c73d911dc20fdae05a66e348 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 16:25:58 +0000 Subject: [PATCH 194/216] sdk: release v2.138.0-beta.7 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 512f6530a1..dcb20e5d3e 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.6 \ No newline at end of file +2.138.0-beta.7 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 522c3f4c08..3b0739f5a2 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.6", + "version": "2.138.0-beta.7", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 104dbd0563609eca5600e79991c79d2495fe1598 Mon Sep 17 00:00:00 2001 From: moosecat Date: Thu, 18 Sep 2025 09:49:14 -0700 Subject: [PATCH 195/216] add lp events for evnet subscriber (#1892) * add lp events for evnet subscriber * idl build --- programs/drift/src/state/events.rs | 109 ++++++++++++ sdk/src/events/types.ts | 14 +- sdk/src/idl/drift.json | 265 +++++++++++++++++++++++++++++ sdk/src/types.ts | 62 +++++++ 4 files changed, 449 insertions(+), 1 deletion(-) diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index f4806fa5be..55e9cecaeb 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -737,3 +737,112 @@ pub fn emit_buffers( Ok(()) } + +#[event] +#[derive(Default)] +pub struct LPSettleRecord { + pub record_id: u64, + // previous settle unix timestamp + pub last_ts: i64, + // previous settle slot + pub last_slot: u64, + // current settle unix timestamp + pub ts: i64, + // current slot + pub slot: u64, + // amm perp market index + pub perp_market_index: u16, + // token amount to settle to lp (positive is from amm to lp, negative lp to amm) + pub settle_to_lp_amount: i64, + // quote pnl of amm since last settle + pub perp_amm_pnl_delta: i64, + // exchange fees earned by market/amm since last settle + pub perp_amm_ex_fee_delta: i64, + // current aum of lp + pub lp_aum: u128, + // current mint price of lp + pub lp_price: u128, +} + +#[event] +#[derive(Default)] +pub struct LPSwapRecord { + pub ts: i64, + pub slot: u64, + pub authority: Pubkey, + /// precision: out market mint precision, gross fees + pub out_amount: u128, + /// precision: in market mint precision, gross fees + pub in_amount: u128, + /// precision: fee on amount_out, in market mint precision + pub out_fee: i128, + /// precision: fee on amount_in, out market mint precision + pub in_fee: i128, + // out spot market index + pub out_spot_market_index: u16, + // in spot market index + pub in_spot_market_index: u16, + // out constituent index + pub out_constituent_index: u16, + // in constituent index + pub in_constituent_index: u16, + /// precision: PRICE_PRECISION + pub out_oracle_price: i64, + /// precision: PRICE_PRECISION + pub in_oracle_price: i64, + /// LPPool last_aum, QUOTE_PRECISION + pub last_aum: u128, + pub last_aum_slot: u64, + /// PERCENTAGE_PRECISION + pub in_market_current_weight: i64, + /// PERCENTAGE_PRECISION + pub out_market_current_weight: i64, + /// PERCENTAGE_PRECISION + pub in_market_target_weight: i64, + /// PERCENTAGE_PRECISION + pub out_market_target_weight: i64, + pub in_swap_id: u64, + pub out_swap_id: u64, +} + +impl Size for LPSwapRecord { + const SIZE: usize = 376; +} + +#[event] +#[derive(Default)] +pub struct LPMintRedeemRecord { + pub ts: i64, + pub slot: u64, + pub authority: Pubkey, + pub description: u8, + /// precision: continutent mint precision, gross fees + pub amount: u128, + /// precision: fee on amount, constituent market mint precision + pub fee: i128, + // spot market index + pub spot_market_index: u16, + // constituent index + pub constituent_index: u16, + /// precision: PRICE_PRECISION + pub oracle_price: i64, + /// token mint + pub mint: Pubkey, + /// lp amount, lp mint precision + pub lp_amount: u64, + /// lp fee, lp mint precision + pub lp_fee: i64, + /// the fair price of the lp token, PRICE_PRECISION + pub lp_price: u128, + pub mint_redeem_id: u64, + /// LPPool last_aum + pub last_aum: u128, + pub last_aum_slot: u64, + /// PERCENTAGE_PRECISION + pub in_market_current_weight: i64, + pub in_market_target_weight: i64, +} + +impl Size for LPMintRedeemRecord { + const SIZE: usize = 328; +} diff --git a/sdk/src/events/types.ts b/sdk/src/events/types.ts index 2671344ceb..7909992b4e 100644 --- a/sdk/src/events/types.ts +++ b/sdk/src/events/types.ts @@ -21,6 +21,9 @@ import { FuelSeasonRecord, InsuranceFundSwapRecord, TransferProtocolIfSharesToRevenuePoolRecord, + LPMintRedeemRecord, + LPSettleRecord, + LPSwapRecord, } from '../types'; import { EventEmitter } from 'events'; @@ -61,6 +64,9 @@ export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = { 'FuelSeasonRecord', 'InsuranceFundSwapRecord', 'TransferProtocolIfSharesToRevenuePoolRecord', + 'LPMintRedeemRecord', + 'LPSettleRecord', + 'LPSwapRecord', ], maxEventsPerType: 4096, orderBy: 'blockchain', @@ -110,6 +116,9 @@ export type EventMap = { FuelSeasonRecord: Event; InsuranceFundSwapRecord: Event; TransferProtocolIfSharesToRevenuePoolRecord: Event; + LPMintRedeemRecord: Event; + LPSettleRecord: Event; + LPSwapRecord: Event; }; export type EventType = keyof EventMap; @@ -135,7 +144,10 @@ export type DriftEvent = | Event | Event | Event - | Event; + | Event + | Event + | Event + | Event; export interface EventSubscriberEvents { newEvent: (event: WrappedEvent) => void; diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 64a7b6df03..350795cf77 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -14386,6 +14386,271 @@ "index": false } ] + }, + { + "name": "LPSettleRecord", + "fields": [ + { + "name": "recordId", + "type": "u64", + "index": false + }, + { + "name": "lastTs", + "type": "i64", + "index": false + }, + { + "name": "lastSlot", + "type": "u64", + "index": false + }, + { + "name": "ts", + "type": "i64", + "index": false + }, + { + "name": "slot", + "type": "u64", + "index": false + }, + { + "name": "perpMarketIndex", + "type": "u16", + "index": false + }, + { + "name": "settleToLpAmount", + "type": "i64", + "index": false + }, + { + "name": "perpAmmPnlDelta", + "type": "i64", + "index": false + }, + { + "name": "perpAmmExFeeDelta", + "type": "i64", + "index": false + }, + { + "name": "lpAum", + "type": "u128", + "index": false + }, + { + "name": "lpPrice", + "type": "u128", + "index": false + } + ] + }, + { + "name": "LPSwapRecord", + "fields": [ + { + "name": "ts", + "type": "i64", + "index": false + }, + { + "name": "slot", + "type": "u64", + "index": false + }, + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "outAmount", + "type": "u128", + "index": false + }, + { + "name": "inAmount", + "type": "u128", + "index": false + }, + { + "name": "outFee", + "type": "i128", + "index": false + }, + { + "name": "inFee", + "type": "i128", + "index": false + }, + { + "name": "outSpotMarketIndex", + "type": "u16", + "index": false + }, + { + "name": "inSpotMarketIndex", + "type": "u16", + "index": false + }, + { + "name": "outConstituentIndex", + "type": "u16", + "index": false + }, + { + "name": "inConstituentIndex", + "type": "u16", + "index": false + }, + { + "name": "outOraclePrice", + "type": "i64", + "index": false + }, + { + "name": "inOraclePrice", + "type": "i64", + "index": false + }, + { + "name": "lastAum", + "type": "u128", + "index": false + }, + { + "name": "lastAumSlot", + "type": "u64", + "index": false + }, + { + "name": "inMarketCurrentWeight", + "type": "i64", + "index": false + }, + { + "name": "outMarketCurrentWeight", + "type": "i64", + "index": false + }, + { + "name": "inMarketTargetWeight", + "type": "i64", + "index": false + }, + { + "name": "outMarketTargetWeight", + "type": "i64", + "index": false + }, + { + "name": "inSwapId", + "type": "u64", + "index": false + }, + { + "name": "outSwapId", + "type": "u64", + "index": false + } + ] + }, + { + "name": "LPMintRedeemRecord", + "fields": [ + { + "name": "ts", + "type": "i64", + "index": false + }, + { + "name": "slot", + "type": "u64", + "index": false + }, + { + "name": "authority", + "type": "publicKey", + "index": false + }, + { + "name": "description", + "type": "u8", + "index": false + }, + { + "name": "amount", + "type": "u128", + "index": false + }, + { + "name": "fee", + "type": "i128", + "index": false + }, + { + "name": "spotMarketIndex", + "type": "u16", + "index": false + }, + { + "name": "constituentIndex", + "type": "u16", + "index": false + }, + { + "name": "oraclePrice", + "type": "i64", + "index": false + }, + { + "name": "mint", + "type": "publicKey", + "index": false + }, + { + "name": "lpAmount", + "type": "u64", + "index": false + }, + { + "name": "lpFee", + "type": "i64", + "index": false + }, + { + "name": "lpPrice", + "type": "u128", + "index": false + }, + { + "name": "mintRedeemId", + "type": "u64", + "index": false + }, + { + "name": "lastAum", + "type": "u128", + "index": false + }, + { + "name": "lastAumSlot", + "type": "u64", + "index": false + }, + { + "name": "inMarketCurrentWeight", + "type": "i64", + "index": false + }, + { + "name": "inMarketTargetWeight", + "type": "i64", + "index": false + } + ] } ], "errors": [ diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 0bf0842314..2d49e61b58 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -741,6 +741,68 @@ export type TransferProtocolIfSharesToRevenuePoolRecord = { transferAmount: BN; }; +export type LPSwapRecord = { + ts: BN; + slot: BN; + authority: PublicKey; + outAmount: BN; + inAmount: BN; + outFee: BN; + inFee: BN; + outSpotMarketIndex: number; + inSpotMarketIndex: number; + outConstituentIndex: number; + inConstituentIndex: number; + outOraclePrice: BN; + inOraclePrice: BN; + outMint: PublicKey; + inMint: PublicKey; + lastAum: BN; + lastAumSlot: BN; + inMarketCurrentWeight: BN; + outMarketCurrentWeight: BN; + inMarketTargetWeight: BN; + outMarketTargetWeight: BN; + inSwapId: BN; + outSwapId: BN; +}; + +export type LPMintRedeemRecord = { + ts: BN; + slot: BN; + authority: PublicKey; + description: number; + amount: BN; + fee: BN; + spotMarketIndex: number; + constituentIndex: number; + oraclePrice: BN; + mint: PublicKey; + lpMint: PublicKey; + lpAmount: BN; + lpFee: BN; + lpPrice: BN; + mintRedeemId: BN; + lastAum: BN; + lastAumSlot: BN; + inMarketCurrentWeight: BN; + inMarketTargetWeight: BN; +}; + +export type LPSettleRecord = { + recordId: BN; + lastTs: BN; + lastSlot: BN; + ts: BN; + slot: BN; + perpMarketIndex: number; + settleToLpAmount: BN; + perpAmmPnlDelta: BN; + perpAmmExFeeDelta: BN; + lpAum: BN; + lpPrice: BN; +}; + export type StateAccount = { admin: PublicKey; exchangeStatus: number; From f1c73b00ce7c55aa0d65f22c9590d19f35ab1346 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 16:54:32 +0000 Subject: [PATCH 196/216] sdk: release v2.138.0-beta.8 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index dcb20e5d3e..527cc0d755 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.7 \ No newline at end of file +2.138.0-beta.8 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index 3b0739f5a2..a22403a8ff 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.7", + "version": "2.138.0-beta.8", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From a288e2e5da9cb17c0ffb278aff267f0f674034b5 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Fri, 19 Sep 2025 15:58:34 -0400 Subject: [PATCH 197/216] CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c87e67e3ad..d0dbcfc239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- program: support scaled ui extension ([#1894](https://github.com/drift-labs/protocol-v2/pull/1894)) + ### Fixes ### Breaking From f824aa089711d8b4cc1f723eec54e057c03a3435 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Fri, 19 Sep 2025 16:01:08 -0400 Subject: [PATCH 198/216] sdk: fix idl --- sdk/src/idl/drift.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 350795cf77..708752bc02 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -16239,5 +16239,8 @@ "name": "InvalidIfRebalanceSwap", "msg": "Invalid If Rebalance Swap" } - ] + ], + "metadata": { + "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" + } } \ No newline at end of file From 4b0aceaf2ef3adf142e9e7cdae92a0de45f5fe7a Mon Sep 17 00:00:00 2001 From: lil perp Date: Fri, 19 Sep 2025 16:01:39 -0400 Subject: [PATCH 199/216] program: token 22 scaled ui support (#1894) * init * program: scaled ui working * cargo fmt -- * ts lint --- programs/drift/src/controller/token.rs | 18 + programs/drift/src/instructions/admin.rs | 41 +- .../drift/src/instructions/constraints.rs | 27 + test-scripts/single-anchor-test.sh | 4 +- tests/fixtures/token_2022_test.so | Bin 0 -> 1382016 bytes tests/spotDepositWithdraw22ScaledUI.ts | 475 ++++++++++++++++++ 6 files changed, 547 insertions(+), 18 deletions(-) create mode 100644 tests/fixtures/token_2022_test.so create mode 100644 tests/spotDepositWithdraw22ScaledUI.ts diff --git a/programs/drift/src/controller/token.rs b/programs/drift/src/controller/token.rs index 81aa3997a0..6e5d03193e 100644 --- a/programs/drift/src/controller/token.rs +++ b/programs/drift/src/controller/token.rs @@ -199,3 +199,21 @@ pub fn transfer_checked_with_transfer_hook<'info>( solana_program::program::invoke_signed(&ix, &account_infos, signer_seeds).map_err(Into::into) } + +pub fn initialize_token_account<'info>( + token_program: &Interface<'info, TokenInterface>, + account: &AccountInfo<'info>, + owner: &AccountInfo<'info>, + mint: &InterfaceAccount<'info, Mint>, +) -> Result<()> { + let cpi_program = token_program.to_account_info(); + let accounts = ::anchor_spl::token_interface::InitializeAccount3 { + account: account.to_account_info(), + mint: mint.to_account_info(), + authority: owner.to_account_info(), + }; + let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); + ::anchor_spl::token_interface::initialize_account3(cpi_ctx)?; + + Ok(()) +} diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index a4ecc2f8d1..0af2ce1c7d 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -10,7 +10,7 @@ use pyth_solana_receiver_sdk::cpi::accounts::InitPriceUpdate; use pyth_solana_receiver_sdk::program::PythSolanaReceiver; use serum_dex::state::ToAlignedBytes; -use crate::controller::token::close_vault; +use crate::controller::token::{close_vault, initialize_token_account}; use crate::error::ErrorCode; use crate::ids::{admin_hot_wallet, amm_spread_adjust_wallet, mm_oracle_crank_wallet}; use crate::instructions::constraints::*; @@ -146,15 +146,19 @@ pub fn handle_initialize_spot_market( let state = &mut ctx.accounts.state; let spot_market_pubkey = ctx.accounts.spot_market.key(); - // protocol must be authority of collateral vault - if ctx.accounts.spot_market_vault.owner != state.signer { - return Err(ErrorCode::InvalidSpotMarketAuthority.into()); - } + initialize_token_account( + &ctx.accounts.token_program, + &ctx.accounts.spot_market_vault, + &ctx.accounts.drift_signer, + &ctx.accounts.spot_market_mint, + )?; - // protocol must be authority of collateral vault - if ctx.accounts.insurance_fund_vault.owner != state.signer { - return Err(ErrorCode::InvalidInsuranceFundAuthority.into()); - } + initialize_token_account( + &ctx.accounts.token_program, + &ctx.accounts.insurance_fund_vault, + &ctx.accounts.drift_signer, + &ctx.accounts.spot_market_mint, + )?; validate_borrow_rate(optimal_utilization, optimal_borrow_rate, max_borrow_rate, 0)?; @@ -280,7 +284,7 @@ pub fn handle_initialize_spot_market( historical_oracle_data: historical_oracle_data_default, historical_index_data: historical_index_data_default, mint: ctx.accounts.spot_market_mint.key(), - vault: *ctx.accounts.spot_market_vault.to_account_info().key, + vault: ctx.accounts.spot_market_vault.key(), revenue_pool: PoolBalance { scaled_balance: 0, market_index: spot_market_index, @@ -337,7 +341,7 @@ pub fn handle_initialize_spot_market( pool_id: 0, padding: [0; 40], insurance_fund: InsuranceFund { - vault: *ctx.accounts.insurance_fund_vault.to_account_info().key, + vault: ctx.accounts.insurance_fund_vault.key(), unstaking_period: THIRTEEN_DAY, total_factor: if_total_factor, user_factor: if_total_factor / 2, @@ -4945,25 +4949,28 @@ pub struct InitializeSpotMarket<'info> { payer = admin )] pub spot_market: AccountLoader<'info, SpotMarket>, + #[account( + mint::token_program = token_program, + )] pub spot_market_mint: Box>, #[account( init, seeds = [b"spot_market_vault".as_ref(), state.number_of_spot_markets.to_le_bytes().as_ref()], bump, payer = admin, - token::mint = spot_market_mint, - token::authority = drift_signer + space = get_vault_len(&spot_market_mint)?, + owner = token_program.key() )] - pub spot_market_vault: Box>, + pub spot_market_vault: AccountInfo<'info>, #[account( init, seeds = [b"insurance_fund_vault".as_ref(), state.number_of_spot_markets.to_le_bytes().as_ref()], bump, payer = admin, - token::mint = spot_market_mint, - token::authority = drift_signer + space = get_vault_len(&spot_market_mint)?, + owner = token_program.key() )] - pub insurance_fund_vault: Box>, + pub insurance_fund_vault: AccountInfo<'info>, #[account( constraint = state.signer.eq(&drift_signer.key()) )] diff --git a/programs/drift/src/instructions/constraints.rs b/programs/drift/src/instructions/constraints.rs index ddfb9c8bbc..6991163f7d 100644 --- a/programs/drift/src/instructions/constraints.rs +++ b/programs/drift/src/instructions/constraints.rs @@ -1,7 +1,9 @@ use anchor_lang::accounts::account::Account; use anchor_lang::accounts::account_loader::AccountLoader; use anchor_lang::accounts::signer::Signer; +use anchor_lang::prelude::*; use anchor_lang::prelude::{AccountInfo, Pubkey}; +use anchor_spl::token_interface::Mint; use crate::error::ErrorCode; use crate::msg; @@ -145,3 +147,28 @@ pub fn exchange_not_paused(state: &Account) -> anchor_lang::Result<()> { } Ok(()) } + +pub fn get_vault_len(mint: &InterfaceAccount) -> anchor_lang::Result { + let mint_info = mint.to_account_info(); + let len = if *mint_info.owner == ::anchor_spl::token_2022::Token2022::id() { + use ::anchor_spl::token_2022::spl_token_2022::extension::{ + BaseStateWithExtensions, ExtensionType, StateWithExtensions, + }; + use ::anchor_spl::token_2022::spl_token_2022::state::{Account, Mint}; + let mint_data = mint_info.try_borrow_data()?; + let mint_state = StateWithExtensions::::unpack(&mint_data)?; + let mint_extensions = match mint_state.get_extension_types() { + Ok(extensions) => extensions, + // If we cant deserialize the mint, we use the default token account length + // Init token will fail if this size doesnt work, so worst case init account just fails + Err(_) => return Ok(::anchor_spl::token::TokenAccount::LEN), + }; + let required_extensions = + ExtensionType::get_required_init_account_extensions(&mint_extensions); + ExtensionType::try_calculate_account_len::(&required_extensions)? + } else { + ::anchor_spl::token::TokenAccount::LEN + }; + + Ok(len) +} diff --git a/test-scripts/single-anchor-test.sh b/test-scripts/single-anchor-test.sh index b75e2f22a9..a9f48da728 100755 --- a/test-scripts/single-anchor-test.sh +++ b/test-scripts/single-anchor-test.sh @@ -6,7 +6,9 @@ fi export ANCHOR_WALLET=~/.config/solana/id.json -test_files=(admin.ts) +test_files=( + spotDepositWithdraw22ScaledUI.ts +) for test_file in ${test_files[@]}; do ts-mocha -t 300000 ./tests/${test_file} diff --git a/tests/fixtures/token_2022_test.so b/tests/fixtures/token_2022_test.so new file mode 100644 index 0000000000000000000000000000000000000000..0f6b74aaf94f46af7c4e1ab3a2c9572148257fea GIT binary patch literal 1382016 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 { + const chProgram = anchor.workspace.Drift as Program; + + let firstUserKeypair: Keypair; + let firstUserDriftClient: TestClient; + let firstUserTokenAccount: PublicKey; + + let admin: TestClient; + let bulkAccountLoader: TestBulkAccountLoader; + let bankrunContextWrapper: BankrunContextWrapper; + + let solOracle: PublicKey; + let usdcMint; + + const usdcAmount = new BN(10 * 10 ** 6); + const largeUsdcAmount = new BN(10_000 * 10 ** 6); + + const _solAmount = new BN(1 * 10 ** 9); + + let marketIndexes: number[]; + let spotMarketIndexes: number[]; + let oracleInfos: OracleInfo[]; + + let mintAuthority: Keypair; + let mintKeypair: Keypair; + let mint: PublicKey; + let mintOracle: PublicKey; + + before(async () => { + const context = await startAnchor( + '', + [ + { + name: 'token_2022_test', + programId: TOKEN_2022_PROGRAM_ID, + }, + ], + [] + ); + + // @ts-ignore + bankrunContextWrapper = new BankrunContextWrapper(context); + + bulkAccountLoader = new TestBulkAccountLoader( + bankrunContextWrapper.connection, + 'processed', + 1 + ); + + usdcMint = await mockUSDCMint(bankrunContextWrapper, TOKEN_2022_PROGRAM_ID); + await mockUserUSDCAccount(usdcMint, largeUsdcAmount, bankrunContextWrapper); + + solOracle = await mockOracleNoProgram(bankrunContextWrapper, 30); + + marketIndexes = []; + spotMarketIndexes = [0, 1]; + oracleInfos = [{ publicKey: solOracle, source: OracleSource.PYTH }]; + + admin = 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 admin.initialize(usdcMint.publicKey, true); + await admin.subscribe(); + + await initializeQuoteSpotMarket(admin, usdcMint.publicKey); + + let _firstUserDriftClientUSDCAccount: PublicKey; + [firstUserDriftClient, _firstUserDriftClientUSDCAccount, firstUserKeypair] = + await createUserWithUSDCAccount( + bankrunContextWrapper, + usdcMint, + chProgram, + usdcAmount, + marketIndexes, + spotMarketIndexes, + oracleInfos, + bulkAccountLoader + ); + + // create token with scaled ui amount config + mintAuthority = Keypair.generate(); + mintKeypair = Keypair.generate(); + mint = mintKeypair.publicKey; + + mintOracle = await mockOracleNoProgram(bankrunContextWrapper, 0.05); // a future we all need to believe in + + const extensions = [ExtensionType.ScaledUiAmountConfig]; + const mintLen = getMintLen(extensions); + const decimals = 6; + + console.log('here'); + + const payer = bankrunContextWrapper.provider.wallet.publicKey; + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: payer, + newAccountPubkey: mint, + space: mintLen, + lamports: LAMPORTS_PER_SOL, + programId: TOKEN_2022_PROGRAM_ID, + }), + createInitializeScaledUiAmountConfigInstruction( + mint, + mintAuthority.publicKey, + 1, + TOKEN_2022_PROGRAM_ID + ), + createInitializeMintInstruction( + mint, + decimals, + mintAuthority.publicKey, + null, + TOKEN_2022_PROGRAM_ID + ) + ); + await bankrunContextWrapper.sendTransaction(mintTransaction, [ + bankrunContextWrapper.provider.wallet.payer, + mintKeypair, + ]); + + await bankrunContextWrapper.moveTimeForward(100); + }); + + after(async () => { + await admin.unsubscribe(); + await firstUserDriftClient.unsubscribe(); + }); + + it('Can mint tokens and transfer between accounts', async () => { + // Create two user keypairs + const user2 = Keypair.generate(); + + // Create token accounts for both users + // Create token accounts for both users + const user1TokenAccountKeypair = Keypair.generate(); + const user2TokenAccountKeypair = Keypair.generate(); + firstUserTokenAccount = user1TokenAccountKeypair.publicKey; + + const mintState = await getMint( + bankrunContextWrapper.connection.toConnection(), + mint, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + const space = getAccountLenForMint(mintState); + const lamports = + await bankrunContextWrapper.connection.getMinimumBalanceForRentExemption( + space + ); + + // Create user1's token account + const createUser1AccountTx = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: bankrunContextWrapper.provider.wallet.payer.publicKey, + newAccountPubkey: user1TokenAccountKeypair.publicKey, + space, + lamports, + programId: TOKEN_2022_PROGRAM_ID, + }), + createInitializeAccountInstruction( + user1TokenAccountKeypair.publicKey, + mint, + firstUserKeypair.publicKey, + TOKEN_2022_PROGRAM_ID + ) + ); + + await bankrunContextWrapper.sendTransaction(createUser1AccountTx, [ + bankrunContextWrapper.provider.wallet.payer, + user1TokenAccountKeypair, + ]); + + // Create user2's token account + const createUser2AccountTx = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: bankrunContextWrapper.provider.wallet.payer.publicKey, + newAccountPubkey: user2TokenAccountKeypair.publicKey, + space, + lamports, + programId: TOKEN_2022_PROGRAM_ID, + }), + createInitializeAccountInstruction( + user2TokenAccountKeypair.publicKey, + mint, + user2.publicKey, + TOKEN_2022_PROGRAM_ID + ) + ); + + await bankrunContextWrapper.sendTransaction(createUser2AccountTx, [ + bankrunContextWrapper.provider.wallet.payer, + user2TokenAccountKeypair, + ]); + + // const user1TokenAccount = user1TokenAccountKeypair.publicKey; + // const user2TokenAccount = user2TokenAccountKeypair.publicKey; + + // Amount to mint and transfer (1000 tokens with 9 decimals) + const mintAmount = BigInt(1000 * 10 ** 9); + const transferAmount = BigInt(300 * 10 ** 9); + + // Mint tokens to user1's account + const mintTransaction = new Transaction().add( + createMintToInstruction( + mint, + user1TokenAccountKeypair.publicKey, + mintAuthority.publicKey, + mintAmount, + [], + TOKEN_2022_PROGRAM_ID + ) + ); + + await bankrunContextWrapper.sendTransaction(mintTransaction, [ + bankrunContextWrapper.provider.wallet.payer, + mintAuthority, + ]); + + // Check initial balances + const user1BalanceBefore = await getAccount( + bankrunContextWrapper.connection.toConnection(), + user1TokenAccountKeypair.publicKey, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + const user2BalanceBefore = await getAccount( + bankrunContextWrapper.connection.toConnection(), + user2TokenAccountKeypair.publicKey, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + + console.log( + 'User1 balance before transfer:', + user1BalanceBefore.amount.toString() + ); + console.log( + 'User2 balance before transfer:', + user2BalanceBefore.amount.toString() + ); + + assert.equal( + user1BalanceBefore.amount, + mintAmount, + 'User1 should have minted amount' + ); + assert.equal( + user2BalanceBefore.amount, + BigInt(0), + 'User2 should start with 0 balance' + ); + + // Get mint info for decimals + const mintInfo = await getMint( + bankrunContextWrapper.connection.toConnection(), + mint, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + + // Create transfer instruction + const transferInstruction = createTransferCheckedInstruction( + user1TokenAccountKeypair.publicKey, + mint, + user2TokenAccountKeypair.publicKey, + firstUserKeypair.publicKey, + transferAmount, + mintInfo.decimals, + [], + TOKEN_2022_PROGRAM_ID + ); + + const transferTransaction = new Transaction().add(transferInstruction); + + await bankrunContextWrapper.sendTransaction(transferTransaction, [ + bankrunContextWrapper.provider.wallet.payer, + firstUserKeypair, + ]); + + // Check final balances + const user1BalanceAfter = await getAccount( + bankrunContextWrapper.connection.toConnection(), + user1TokenAccountKeypair.publicKey, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + const user2BalanceAfter = await getAccount( + bankrunContextWrapper.connection.toConnection(), + user2TokenAccountKeypair.publicKey, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + + console.log( + 'User1 balance after transfer:', + user1BalanceAfter.amount.toString() + ); + console.log( + 'User2 balance after transfer:', + user2BalanceAfter.amount.toString() + ); + + // Verify the transfer worked correctly + const expectedUser1Balance = mintAmount - transferAmount; + assert.equal( + user1BalanceAfter.amount, + expectedUser1Balance, + 'User1 should have reduced balance' + ); + assert.equal( + user2BalanceAfter.amount, + transferAmount, + 'User2 should have received transfer amount' + ); + + // Verify total supply is conserved + const totalBalance = user1BalanceAfter.amount + user2BalanceAfter.amount; + assert.equal( + totalBalance, + mintAmount, + 'Total balance should equal minted amount' + ); + }); + + it('Initialize Scaled UI Amount Config SpotMarket', async () => { + await admin.initializeSpotMarket( + mint, + SPOT_MARKET_RATE_PRECISION.divn(2).toNumber(), + SPOT_MARKET_RATE_PRECISION.mul(new BN(20)).toNumber(), + SPOT_MARKET_RATE_PRECISION.mul(new BN(50)).toNumber(), + mintOracle, + OracleSource.PYTH, + SPOT_MARKET_WEIGHT_PRECISION.toNumber(), + SPOT_MARKET_WEIGHT_PRECISION.toNumber(), + SPOT_MARKET_WEIGHT_PRECISION.toNumber(), + SPOT_MARKET_WEIGHT_PRECISION.toNumber(), + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined + ); + await admin.updateWithdrawGuardThreshold( + 1, + new BN(10 ** 10).mul(QUOTE_PRECISION) + ); + await admin.fetchAccounts(); + const spotMarket = admin.getSpotMarketAccount(1); + assert(spotMarket.marketIndex === 1); + assert(admin.getStateAccount().numberOfSpotMarkets === 2); + + assert( + spotMarket.tokenProgramFlag === 1, + 'Token program flag should be 3 for token with scaled ui amount config' + ); + }); + + async function doDepositWithdrawTest() { + const userTokenBalanceBefore = await getAccount( + bankrunContextWrapper.connection.toConnection(), + firstUserTokenAccount, + 'confirmed', + TOKEN_2022_PROGRAM_ID + ); + const depositAmount = new BN(userTokenBalanceBefore.amount.toString()).divn( + 2 + ); + + await firstUserDriftClient.fetchAccounts(); + await firstUserDriftClient.deposit(depositAmount, 1, firstUserTokenAccount); + + await firstUserDriftClient.fetchAccounts(); + let spotMarket = firstUserDriftClient.getSpotMarketAccount(1)!; + let spotPos = firstUserDriftClient.getSpotPosition(1)!; + let spotBal = getTokenAmount( + spotPos.scaledBalance, + spotMarket, + spotPos.balanceType + ); + assert.equal(spotBal.toString(), depositAmount.toString()); + + await firstUserDriftClient.fetchAccounts(); + await firstUserDriftClient.withdraw( + depositAmount, + 1, + firstUserTokenAccount + ); + + await firstUserDriftClient.fetchAccounts(); + spotMarket = firstUserDriftClient.getSpotMarketAccount(1)!; + spotPos = firstUserDriftClient.getSpotPosition(1)!; + spotBal = getTokenAmount( + spotPos.scaledBalance, + spotMarket, + spotPos.balanceType + ); + assert.equal(spotBal.toString(), '0'); + } + + it('Deposit and withdraw SpotMarket', async () => { + await doDepositWithdrawTest(); + }); +}); From 7f53fe8024fc75a09164c8e07eea33f3b78e7bdd Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:07:20 +0000 Subject: [PATCH 200/216] sdk: release v2.138.0-beta.9 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index 527cc0d755..b0c8a5df9a 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.8 \ No newline at end of file +2.138.0-beta.9 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index a22403a8ff..9f2067b649 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.8", + "version": "2.138.0-beta.9", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 0715088e9ae48ff1e369819435745cb9db58bae9 Mon Sep 17 00:00:00 2001 From: wphan Date: Fri, 19 Sep 2025 13:21:21 -0700 Subject: [PATCH 201/216] fix build --- programs/drift/src/instructions/admin.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 0af2ce1c7d..330d8cb377 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -4961,6 +4961,7 @@ pub struct InitializeSpotMarket<'info> { space = get_vault_len(&spot_market_mint)?, owner = token_program.key() )] + /// CHECK: checked in `initialize_spot_market` pub spot_market_vault: AccountInfo<'info>, #[account( init, @@ -4970,6 +4971,7 @@ pub struct InitializeSpotMarket<'info> { space = get_vault_len(&spot_market_mint)?, owner = token_program.key() )] + /// CHECK: checked in `initialize_spot_market` pub insurance_fund_vault: AccountInfo<'info>, #[account( constraint = state.signer.eq(&drift_signer.key()) From d91be8f64fcac05442a86db7855fdb2aa5750035 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Fri, 19 Sep 2025 17:27:10 -0400 Subject: [PATCH 202/216] program: add log for get_vault_len --- programs/drift/src/instructions/constraints.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/programs/drift/src/instructions/constraints.rs b/programs/drift/src/instructions/constraints.rs index 6991163f7d..c983ea471f 100644 --- a/programs/drift/src/instructions/constraints.rs +++ b/programs/drift/src/instructions/constraints.rs @@ -161,7 +161,10 @@ pub fn get_vault_len(mint: &InterfaceAccount) -> anchor_lang::Result extensions, // If we cant deserialize the mint, we use the default token account length // Init token will fail if this size doesnt work, so worst case init account just fails - Err(_) => return Ok(::anchor_spl::token::TokenAccount::LEN), + Err(_) => { + msg!("Failed to deserialize mint. Falling back to default token account length"); + return Ok(::anchor_spl::token::TokenAccount::LEN); + } }; let required_extensions = ExtensionType::get_required_init_account_extensions(&mint_extensions); From c5b8e2740691a10e4c793c5115980e5affdad238 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sat, 20 Sep 2025 10:16:16 -0400 Subject: [PATCH 203/216] fix cargo build errors --- programs/drift/src/controller/amm.rs | 4 ++-- programs/drift/src/controller/insurance.rs | 2 +- programs/drift/src/controller/liquidation.rs | 8 +++---- programs/drift/src/controller/orders.rs | 24 ++++---------------- programs/drift/src/controller/pnl.rs | 3 --- programs/drift/src/controller/repeg.rs | 1 - programs/drift/src/instructions/admin.rs | 4 ---- programs/drift/src/instructions/if_staker.rs | 7 ++---- programs/drift/src/instructions/keeper.rs | 2 -- programs/drift/src/instructions/user.rs | 5 ++-- programs/drift/src/math/fees.rs | 6 ++--- programs/drift/src/math/orders.rs | 3 +-- programs/drift/src/math/position.rs | 11 ++++----- programs/drift/src/state/perp_market.rs | 18 ++++++--------- programs/drift/src/state/user.rs | 2 +- 15 files changed, 30 insertions(+), 70 deletions(-) diff --git a/programs/drift/src/controller/amm.rs b/programs/drift/src/controller/amm.rs index 558d0a9e36..1088b680e7 100644 --- a/programs/drift/src/controller/amm.rs +++ b/programs/drift/src/controller/amm.rs @@ -15,8 +15,8 @@ use crate::math::amm::calculate_quote_asset_amount_swapped; use crate::math::amm_spread::{calculate_spread_reserves, get_spread_reserves}; use crate::math::casting::Cast; use crate::math::constants::{ - CONCENTRATION_PRECISION, FEE_ADJUSTMENT_MAX, FEE_POOL_TO_REVENUE_POOL_THRESHOLD, - K_BPS_UPDATE_SCALE, MAX_CONCENTRATION_COEFFICIENT, MAX_K_BPS_INCREASE, MAX_SQRT_K, + CONCENTRATION_PRECISION, FEE_POOL_TO_REVENUE_POOL_THRESHOLD, K_BPS_UPDATE_SCALE, + MAX_CONCENTRATION_COEFFICIENT, MAX_K_BPS_INCREASE, MAX_SQRT_K, }; use crate::math::cp_curve::get_update_k_result; use crate::math::repeg::get_total_fee_lower_bound; diff --git a/programs/drift/src/controller/insurance.rs b/programs/drift/src/controller/insurance.rs index bcc3dea191..91ad374ed5 100644 --- a/programs/drift/src/controller/insurance.rs +++ b/programs/drift/src/controller/insurance.rs @@ -973,7 +973,7 @@ pub fn handle_if_begin_swap( out_insurance_fund_vault_amount: u64, in_spot_market: &mut SpotMarket, out_spot_market: &mut SpotMarket, - in_amount: u64, + _in_amount: u64, now: i64, ) -> DriftResult<()> { if now diff --git a/programs/drift/src/controller/liquidation.rs b/programs/drift/src/controller/liquidation.rs index 92d241dbc7..1fd2f548dc 100644 --- a/programs/drift/src/controller/liquidation.rs +++ b/programs/drift/src/controller/liquidation.rs @@ -50,10 +50,9 @@ use crate::math::safe_math::SafeMath; use crate::math::spot_balance::get_token_value; use crate::state::events::{ - emit_stack, LPAction, LPRecord, LiquidateBorrowForPerpPnlRecord, - LiquidatePerpPnlForDepositRecord, LiquidatePerpRecord, LiquidateSpotRecord, LiquidationRecord, - LiquidationType, OrderAction, OrderActionExplanation, OrderActionRecord, OrderRecord, - PerpBankruptcyRecord, SpotBankruptcyRecord, + LiquidateBorrowForPerpPnlRecord, LiquidatePerpPnlForDepositRecord, LiquidatePerpRecord, + LiquidateSpotRecord, LiquidationRecord, LiquidationType, OrderAction, OrderActionExplanation, + OrderActionRecord, OrderRecord, PerpBankruptcyRecord, SpotBankruptcyRecord, }; use crate::state::fill_mode::FillMode; use crate::state::margin_calculation::{MarginCalculation, MarginContext, MarketIdentifier}; @@ -65,7 +64,6 @@ use crate::state::perp_market_map::PerpMarketMap; use crate::state::spot_market::SpotBalanceType; use crate::state::spot_market_map::SpotMarketMap; use crate::state::state::State; -use crate::state::traits::Size; use crate::state::user::{MarketType, Order, OrderStatus, OrderType, User, UserStats}; use crate::state::user_map::{UserMap, UserStatsMap}; use crate::{get_then_update_id, load_mut, LST_POOL_ID}; diff --git a/programs/drift/src/controller/orders.rs b/programs/drift/src/controller/orders.rs index 6cff2d1350..e623000af3 100644 --- a/programs/drift/src/controller/orders.rs +++ b/programs/drift/src/controller/orders.rs @@ -48,9 +48,7 @@ use crate::math::spot_balance::{get_signed_token_amount, get_token_amount}; use crate::math::spot_swap::select_margin_type_for_swap; use crate::math::{amm, fees, margin::*, orders::*}; use crate::print_error; -use crate::state::events::{ - emit_stack, get_order_action_record, LPAction, LPRecord, OrderActionRecord, OrderRecord, -}; +use crate::state::events::{emit_stack, get_order_action_record, OrderActionRecord, OrderRecord}; use crate::state::events::{OrderAction, OrderActionExplanation}; use crate::state::fill_mode::FillMode; use crate::state::fulfillment::{PerpFulfillmentMethod, SpotFulfillmentMethod}; @@ -985,23 +983,12 @@ pub fn fill_perp_order( .position(|order| order.order_id == order_id && order.status == OrderStatus::Open) .ok_or_else(print_error!(ErrorCode::OrderDoesNotExist))?; - let ( - order_status, - market_index, - order_market_type, - order_price, - order_oracle_price_offset, - order_direction, - order_auction_duration, - ) = get_struct_values!( + let (order_status, market_index, order_market_type, order_direction) = get_struct_values!( user.orders[order_index], status, market_index, market_type, - price, - oracle_price_offset, - direction, - auction_duration + direction ); validate!( @@ -2246,7 +2233,7 @@ pub fn fulfill_perp_order_with_amm( filler_reward, referee_discount, referrer_reward, - fee_to_market_for_lp, + fee_to_market_for_lp: _fee_to_market_for_lp, maker_rebate, } = fees::calculate_fee_for_fulfillment_with_amm( user_stats, @@ -2263,9 +2250,6 @@ pub fn fulfill_perp_order_with_amm( user.is_high_leverage_mode(MarginRequirementType::Initial), )?; - let user_position_delta = - get_position_delta_for_fill(base_asset_amount, quote_asset_amount, order_direction)?; - // Increment the protocol's total fee variables market.amm.total_fee = market.amm.total_fee.safe_add(fee_to_market.cast()?)?; market.amm.total_exchange_fee = market.amm.total_exchange_fee.safe_add(user_fee.cast()?)?; diff --git a/programs/drift/src/controller/pnl.rs b/programs/drift/src/controller/pnl.rs index 166030d6ee..c46d46763d 100644 --- a/programs/drift/src/controller/pnl.rs +++ b/programs/drift/src/controller/pnl.rs @@ -14,14 +14,11 @@ use crate::math::oracle::{is_oracle_valid_for_action, DriftAction}; use crate::math::casting::Cast; use crate::math::margin::{ - calculate_margin_requirement_and_total_collateral_and_liability_info, meets_maintenance_margin_requirement, meets_settle_pnl_maintenance_margin_requirement, - MarginRequirementType, }; use crate::math::position::calculate_base_asset_value_with_expiry_price; use crate::math::safe_math::SafeMath; use crate::math::spot_balance::get_token_amount; -use crate::state::margin_calculation::MarginContext; use crate::msg; use crate::state::events::{OrderActionExplanation, SettlePnlExplanation, SettlePnlRecord}; diff --git a/programs/drift/src/controller/repeg.rs b/programs/drift/src/controller/repeg.rs index c8dce61000..707ccce35a 100644 --- a/programs/drift/src/controller/repeg.rs +++ b/programs/drift/src/controller/repeg.rs @@ -3,7 +3,6 @@ use std::cmp::min; use crate::math::oracle::LogMode; use crate::msg; use crate::state::oracle::MMOraclePriceData; -use crate::state::oracle::OraclePriceData; use anchor_lang::prelude::AccountInfo; use anchor_lang::prelude::*; diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 330d8cb377..e552576f63 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -1601,7 +1601,6 @@ pub fn handle_recenter_perp_market_amm_crank( depth: Option, ) -> Result<()> { let perp_market = &mut load_mut!(ctx.accounts.perp_market)?; - let spot_market = &mut load!(ctx.accounts.spot_market)?; let clock = Clock::get()?; let price_oracle = &ctx.accounts.oracle; @@ -4780,9 +4779,6 @@ pub fn handle_initialize_if_rebalance_config( ctx: Context, params: IfRebalanceConfigParams, ) -> Result<()> { - let clock = Clock::get()?; - let now = clock.unix_timestamp; - let pubkey = ctx.accounts.if_rebalance_config.to_account_info().key; let mut config = ctx.accounts.if_rebalance_config.load_init()?; diff --git a/programs/drift/src/instructions/if_staker.rs b/programs/drift/src/instructions/if_staker.rs index 83377a1d07..51e4d7fd79 100644 --- a/programs/drift/src/instructions/if_staker.rs +++ b/programs/drift/src/instructions/if_staker.rs @@ -375,9 +375,7 @@ pub fn handle_begin_insurance_fund_swap<'c: 'info, 'info>( let remaining_accounts_iter = &mut ctx.remaining_accounts.iter().peekable(); let AccountMaps { - perp_market_map, - spot_market_map, - mut oracle_map, + spot_market_map, .. } = load_maps( remaining_accounts_iter, &MarketSet::new(), @@ -602,14 +600,13 @@ pub fn handle_end_insurance_fund_swap<'c: 'info, 'info>( ) -> Result<()> { let state = &ctx.accounts.state; let clock = Clock::get()?; - let slot = clock.slot; let now = clock.unix_timestamp; let remaining_accounts = &mut ctx.remaining_accounts.iter().peekable(); let AccountMaps { - perp_market_map, spot_market_map, mut oracle_map, + .. } = load_maps( remaining_accounts, &MarketSet::new(), diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 85a4989425..dbd7127d1f 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -1,5 +1,4 @@ use std::cell::RefMut; -use std::collections::BTreeMap; use std::convert::TryFrom; use anchor_lang::prelude::*; @@ -69,7 +68,6 @@ use crate::validation::sig_verification::verify_and_decode_ed25519_msg; use crate::validation::user::{validate_user_deletion, validate_user_is_idle}; use crate::{ controller, load, math, print_error, safe_decrement, OracleSource, GOV_SPOT_MARKET_INDEX, - MARGIN_PRECISION, }; use crate::{load_mut, QUOTE_PRECISION_U64}; use crate::{math_error, ID}; diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index e6d13d6fec..2b4a90e19f 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -37,7 +37,7 @@ use crate::math::margin::calculate_margin_requirement_and_total_collateral_and_l use crate::math::margin::meets_initial_margin_requirement; use crate::math::margin::{ calculate_max_withdrawable_amount, meets_maintenance_margin_requirement, - meets_place_order_margin_requirement, validate_spot_margin_trading, MarginRequirementType, + validate_spot_margin_trading, MarginRequirementType, }; use crate::math::oracle::is_oracle_valid_for_action; use crate::math::oracle::DriftAction; @@ -61,7 +61,7 @@ use crate::state::events::OrderActionRecord; use crate::state::events::OrderRecord; use crate::state::events::{ DepositDirection, DepositExplanation, DepositRecord, FuelSeasonRecord, FuelSweepRecord, - LPAction, LPRecord, NewUserRecord, OrderActionExplanation, SwapRecord, + NewUserRecord, OrderActionExplanation, SwapRecord, }; use crate::state::fill_mode::FillMode; use crate::state::fulfillment_params::drift::MatchFulfillmentParams; @@ -76,7 +76,6 @@ use crate::state::order_params::{ PlaceOrderOptions, PostOnlyParam, }; use crate::state::paused_operations::{PerpOperation, SpotOperation}; -use crate::state::perp_market::ContractType; use crate::state::perp_market::MarketStatus; use crate::state::perp_market_map::{get_writable_perp_market_set, MarketSet}; use crate::state::protected_maker_mode_config::ProtectedMakerModeConfig; diff --git a/programs/drift/src/math/fees.rs b/programs/drift/src/math/fees.rs index da3cd3a356..3e23e718f3 100644 --- a/programs/drift/src/math/fees.rs +++ b/programs/drift/src/math/fees.rs @@ -6,9 +6,9 @@ use crate::error::DriftResult; use crate::math::casting::Cast; use crate::math::constants::{ - FIFTY_MILLION_QUOTE, FIVE_MILLION_QUOTE, ONE_HUNDRED_MILLION_QUOTE, ONE_HUNDRED_THOUSAND_QUOTE, - ONE_MILLION_QUOTE, ONE_THOUSAND_QUOTE, TEN_BPS, TEN_MILLION_QUOTE, TEN_THOUSAND_QUOTE, - TWENTY_FIVE_THOUSAND_QUOTE, TWO_HUNDRED_FIFTY_THOUSAND_QUOTE, + FIVE_MILLION_QUOTE, ONE_HUNDRED_MILLION_QUOTE, ONE_HUNDRED_THOUSAND_QUOTE, ONE_MILLION_QUOTE, + ONE_THOUSAND_QUOTE, TEN_BPS, TEN_MILLION_QUOTE, TEN_THOUSAND_QUOTE, TWENTY_FIVE_THOUSAND_QUOTE, + TWO_HUNDRED_FIFTY_THOUSAND_QUOTE, }; use crate::math::helpers::get_proportion_u128; use crate::math::safe_math::SafeMath; diff --git a/programs/drift/src/math/orders.rs b/programs/drift/src/math/orders.rs index 8f3aff9140..013563f0bd 100644 --- a/programs/drift/src/math/orders.rs +++ b/programs/drift/src/math/orders.rs @@ -1,8 +1,6 @@ use std::cmp::min; use std::ops::Sub; -use crate::math::constants::PERCENTAGE_PRECISION; -use crate::math::constants::PERCENTAGE_PRECISION_I128; use crate::msg; use crate::controller::position::PositionDelta; @@ -12,6 +10,7 @@ use crate::math::amm::calculate_amm_available_liquidity; use crate::math::casting::Cast; use crate::state::protected_maker_mode_config::ProtectedMakerParams; use crate::state::user::OrderBitFlag; +use crate::PERCENTAGE_PRECISION_I128; use crate::{ load, math, FeeTier, BASE_PRECISION_I128, FEE_ADJUSTMENT_MAX, MARGIN_PRECISION_I128, MAX_PREDICTION_MARKET_PRICE, MAX_PREDICTION_MARKET_PRICE_I64, OPEN_ORDER_MARGIN_REQUIREMENT, diff --git a/programs/drift/src/math/position.rs b/programs/drift/src/math/position.rs index 998970480d..8201123f09 100644 --- a/programs/drift/src/math/position.rs +++ b/programs/drift/src/math/position.rs @@ -1,8 +1,6 @@ -use crate::msg; - use crate::controller::amm::SwapDirection; use crate::controller::position::PositionDelta; -use crate::error::{DriftResult, ErrorCode}; +use crate::error::DriftResult; use crate::math::amm; use crate::math::amm::calculate_quote_asset_amount_swapped; use crate::math::casting::Cast; @@ -10,13 +8,12 @@ use crate::math::constants::{ AMM_RESERVE_PRECISION_I128, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO_I128, }; -use crate::math::helpers::get_proportion_u128; use crate::math::pnl::calculate_pnl; use crate::math::safe_math::SafeMath; -use crate::state::perp_market::{ContractType, PerpMarket, AMM}; +use crate::state::perp_market::{ContractType, AMM}; use crate::state::user::PerpPosition; -use crate::{validate, BASE_PRECISION, MAX_PREDICTION_MARKET_PRICE_U128}; +use crate::{BASE_PRECISION, MAX_PREDICTION_MARKET_PRICE_U128}; pub fn calculate_base_asset_value_and_pnl( base_asset_amount: i128, @@ -195,7 +192,7 @@ pub fn get_new_position_amounts( .quote_asset_amount .safe_add(delta.quote_asset_amount)?; - let mut new_base_asset_amount = position + let new_base_asset_amount = position .base_asset_amount .safe_add(delta.base_asset_amount)?; diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 9ba2d008ae..fa14d88789 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -5,31 +5,27 @@ use anchor_lang::prelude::*; use crate::state::state::{State, ValidityGuardRails}; use std::cmp::max; -use crate::controller::position::{PositionDelta, PositionDirection}; +use crate::controller::position::PositionDirection; use crate::error::{DriftResult, ErrorCode}; use crate::math::amm; use crate::math::casting::Cast; #[cfg(test)] use crate::math::constants::{AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT}; use crate::math::constants::{ - AMM_RESERVE_PRECISION_I128, AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION, - BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128, - DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, FUNDING_RATE_BUFFER_I128, - FUNDING_RATE_OFFSET_PERCENTAGE, LIQUIDATION_FEE_PRECISION, - LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, LP_FEE_SLICE_DENOMINATOR, LP_FEE_SLICE_NUMERATOR, - MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION, - PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, - PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, PRICE_PRECISION_I64, + AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, + BID_ASK_SPREAD_PRECISION_U128, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, + FUNDING_RATE_BUFFER_I128, FUNDING_RATE_OFFSET_PERCENTAGE, LIQUIDATION_FEE_PRECISION, + LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, MARGIN_PRECISION, MARGIN_PRECISION_U128, + MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, + PERCENTAGE_PRECISION_I64, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, }; -use crate::math::helpers::get_proportion_i128; use crate::math::margin::{ calculate_size_discount_asset_weight, calculate_size_premium_liability_weight, MarginRequirementType, }; use crate::math::safe_math::SafeMath; use crate::math::stats; -use crate::state::events::OrderActionExplanation; use num_integer::Roots; use crate::state::oracle::{ diff --git a/programs/drift/src/state/user.rs b/programs/drift/src/state/user.rs index 1017fd3dd5..db6755822a 100644 --- a/programs/drift/src/state/user.rs +++ b/programs/drift/src/state/user.rs @@ -4,7 +4,7 @@ use crate::math::auction::{calculate_auction_price, is_auction_complete}; use crate::math::casting::Cast; use crate::math::constants::{ EPOCH_DURATION, FUEL_OVERFLOW_THRESHOLD_U32, FUEL_START_TS, OPEN_ORDER_MARGIN_REQUIREMENT, - PRICE_TIMES_AMM_TO_QUOTE_PRECISION_RATIO, QUOTE_PRECISION, QUOTE_SPOT_MARKET_INDEX, THIRTY_DAY, + QUOTE_SPOT_MARKET_INDEX, THIRTY_DAY, }; use crate::math::margin::MarginRequirementType; use crate::math::orders::{ From ef453e0984f7edae52582f5123016bd9b0218b18 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sat, 20 Sep 2025 11:09:03 -0400 Subject: [PATCH 204/216] program: rm update devnet drift --- programs/drift/src/instructions/keeper.rs | 21 ------------------ programs/drift/src/lib.rs | 7 ------ sdk/src/driftClient.ts | 27 +---------------------- sdk/src/idl/drift.json | 21 ------------------ 4 files changed, 1 insertion(+), 75 deletions(-) diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index dbd7127d1f..4e66b0fcee 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -2732,20 +2732,6 @@ pub fn handle_update_user_gov_token_insurance_stake( Ok(()) } -pub fn handle_update_user_gov_token_insurance_stake_devnet( - ctx: Context, - gov_stake_amount: u64, -) -> Result<()> { - #[cfg(all(feature = "mainnet-beta", not(feature = "anchor-test")))] - { - panic!("Devnet function is disabled on mainnet-beta"); - } - - let user_stats = &mut load_mut!(ctx.accounts.user_stats)?; - user_stats.if_staked_gov_token_amount = gov_stake_amount; - Ok(()) -} - pub fn handle_disable_user_high_leverage_mode<'c: 'info, 'info>( ctx: Context<'_, '_, 'c, 'info, DisableUserHighLeverageMode<'info>>, disable_maintenance: bool, @@ -3602,13 +3588,6 @@ pub struct UpdateUserGovTokenInsuranceStake<'info> { pub insurance_fund_vault: Box>, } -#[derive(Accounts)] -pub struct UpdateUserGovTokenInsuranceStakeDevnet<'info> { - #[account(mut)] - pub user_stats: AccountLoader<'info, UserStats>, - pub signer: Signer<'info>, -} - #[derive(Accounts)] pub struct UpdatePrelaunchOracle<'info> { pub state: Box>, diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index 43653c8c1a..940129cfd1 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -737,13 +737,6 @@ pub mod drift { handle_update_user_gov_token_insurance_stake(ctx) } - pub fn update_user_gov_token_insurance_stake_devnet( - ctx: Context, - gov_stake_amount: u64, - ) -> Result<()> { - handle_update_user_gov_token_insurance_stake_devnet(ctx, gov_stake_amount) - } - // IF stakers pub fn initialize_insurance_fund_stake( diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index e55029c5cc..01dfa60030 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -9243,10 +9243,7 @@ export class DriftClient { txParams?: TxParams, env: DriftEnv = 'mainnet-beta' ): Promise { - const ix = - env == 'mainnet-beta' - ? await this.getUpdateUserGovTokenInsuranceStakeIx(authority) - : await this.getUpdateUserGovTokenInsuranceStakeDevnetIx(authority); + const ix = await this.getUpdateUserGovTokenInsuranceStakeIx(authority); const tx = await this.buildTransaction(ix, txParams); const { txSig } = await this.sendTransaction(tx, [], this.opts); return txSig; @@ -9281,28 +9278,6 @@ export class DriftClient { return ix; } - public async getUpdateUserGovTokenInsuranceStakeDevnetIx( - authority: PublicKey, - amount: BN = new BN(1) - ): Promise { - const userStatsPublicKey = getUserStatsAccountPublicKey( - this.program.programId, - authority - ); - - const ix = this.program.instruction.updateUserGovTokenInsuranceStakeDevnet( - amount, - { - accounts: { - userStats: userStatsPublicKey, - signer: this.wallet.publicKey, - }, - } - ); - - return ix; - } - public async settleRevenueToInsuranceFund( spotMarketIndex: number, txParams?: TxParams diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 708752bc02..3fab52decf 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -3141,27 +3141,6 @@ ], "args": [] }, - { - "name": "updateUserGovTokenInsuranceStakeDevnet", - "accounts": [ - { - "name": "userStats", - "isMut": true, - "isSigner": false - }, - { - "name": "signer", - "isMut": false, - "isSigner": true - } - ], - "args": [ - { - "name": "govStakeAmount", - "type": "u64" - } - ] - }, { "name": "initializeInsuranceFundStake", "accounts": [ From 311b0511b0ce3e414e00751279b1710cc79dd711 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sat, 20 Sep 2025 12:30:52 -0400 Subject: [PATCH 205/216] sdk: rm unused param --- sdk/src/driftClient.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 01dfa60030..2033723b86 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -9241,7 +9241,6 @@ export class DriftClient { public async updateUserGovTokenInsuranceStake( authority: PublicKey, txParams?: TxParams, - env: DriftEnv = 'mainnet-beta' ): Promise { const ix = await this.getUpdateUserGovTokenInsuranceStakeIx(authority); const tx = await this.buildTransaction(ix, txParams); From ff0d66e22f51c1daeb07987525bc27b164d75da1 Mon Sep 17 00:00:00 2001 From: jordy25519 Date: Mon, 22 Sep 2025 07:54:41 +0800 Subject: [PATCH 206/216] fix comments (#1844) --- sdk/src/swift/swiftOrderSubscriber.ts | 55 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/sdk/src/swift/swiftOrderSubscriber.ts b/sdk/src/swift/swiftOrderSubscriber.ts index bab3032795..c0555b8111 100644 --- a/sdk/src/swift/swiftOrderSubscriber.ts +++ b/sdk/src/swift/swiftOrderSubscriber.ts @@ -41,6 +41,27 @@ export type SwiftOrderSubscriberConfig = { keypair: Keypair; }; +/** + * Swift order message received from WebSocket + */ +export interface SwiftOrderMessage { + /** Hex string of the order message */ + order_message: string; + /** Base58 string of taker authority */ + taker_authority: string; + /** Base58 string of signing authority */ + signing_authority: string; + /** Base64 string containing the order signature */ + order_signature: string; + /** Swift order UUID */ + uuid: string; + /** Whether the order auction params are likely to be sanitized on submission to program */ + will_sanitize?: boolean; + /** Base64 string of a prerequisite deposit tx. The swift order_message should be bundled + * after the deposit when present */ + depositTx?: string; +} + export class SwiftOrderSubscriber { private heartbeatTimeout: ReturnType | null = null; private readonly heartbeatIntervalMs = 60000; @@ -48,7 +69,7 @@ export class SwiftOrderSubscriber { private driftClient: DriftClient; public userAccountGetter?: AccountGetter; // In practice, this for now is just an OrderSubscriber or a UserMap public onOrder: ( - orderMessageRaw: any, + orderMessageRaw: SwiftOrderMessage, signedMessage: | SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage, @@ -120,13 +141,14 @@ export class SwiftOrderSubscriber { async subscribe( onOrder: ( - orderMessageRaw: any, + orderMessageRaw: SwiftOrderMessage, signedMessage: | SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage, isDelegateSigner?: boolean ) => Promise, - acceptSanitized = false + acceptSanitized = false, + acceptDepositTrade = false ): Promise { this.onOrder = onOrder; @@ -150,13 +172,20 @@ export class SwiftOrderSubscriber { } if (message['order']) { - const order = message['order']; + const order = message['order'] as SwiftOrderMessage; // ignore likely sanitized orders by default - if (order['will_sanitize'] === true && !acceptSanitized) { + if (order.will_sanitize === true && !acceptSanitized) { return; } + // order has a prerequisite deposit tx attached + if (message['deposit']) { + order.depositTx = message['deposit']; + if (!acceptDepositTrade) { + return; + } + } const signedMsgOrderParamsBuf = Buffer.from( - order['order_message'], + order.order_message, 'hex' ); const isDelegateSigner = signedMsgOrderParamsBuf @@ -224,7 +253,7 @@ export class SwiftOrderSubscriber { } async getPlaceAndMakeSignedMsgOrderIxs( - orderMessageRaw: any, + orderMessageRaw: SwiftOrderMessage, signedMsgOrderParamsMessage: | SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage, @@ -235,7 +264,7 @@ export class SwiftOrderSubscriber { } const signedMsgOrderParamsBuf = Buffer.from( - orderMessageRaw['order_message'], + orderMessageRaw.order_message, 'hex' ); @@ -256,10 +285,8 @@ export class SwiftOrderSubscriber { isDelegateSigner ); - const takerAuthority = new PublicKey(orderMessageRaw['taker_authority']); - const signingAuthority = new PublicKey( - orderMessageRaw['signing_authority'] - ); + const takerAuthority = new PublicKey(orderMessageRaw.taker_authority); + const signingAuthority = new PublicKey(orderMessageRaw.signing_authority); const takerUserPubkey = isDelegateSigner ? (signedMessage as SignedMsgOrderParamsDelegateMessage).takerPubkey : await getUserAccountPublicKey( @@ -273,9 +300,9 @@ export class SwiftOrderSubscriber { const ixs = await this.driftClient.getPlaceAndMakeSignedMsgPerpOrderIxs( { orderParams: signedMsgOrderParamsBuf, - signature: Buffer.from(orderMessageRaw['order_signature'], 'base64'), + signature: Buffer.from(orderMessageRaw.order_signature, 'base64'), }, - decodeUTF8(orderMessageRaw['uuid']), + decodeUTF8(orderMessageRaw.uuid), { taker: takerUserPubkey, takerUserAccount, From 26d1b53fa56eeac71d2c4bb7852366a303fa6b33 Mon Sep 17 00:00:00 2001 From: lil perp Date: Mon, 22 Sep 2025 10:02:09 -0400 Subject: [PATCH 207/216] program: tweak ResizeSignedMsgUserOrders (#1898) --- programs/drift/src/instructions/user.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 2b4a90e19f..8bfdd10066 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -4008,6 +4008,9 @@ pub struct ResizeSignedMsgUserOrders<'info> { pub signed_msg_user_orders: Box>, /// CHECK: authority pub authority: AccountInfo<'info>, + #[account( + has_one = authority + )] pub user: AccountLoader<'info, User>, #[account(mut)] pub payer: Signer<'info>, From 40a2e038b5fe271142db7f3844ddf67a757c4483 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 22 Sep 2025 07:28:00 -0700 Subject: [PATCH 208/216] fix linter and cargo test --- package.json | 2 +- programs/drift/src/state/perp_market.rs | 2 +- sdk/src/driftClient.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index de9fdd10f5..854fea3020 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "prettify:fix": "prettier --write './sdk/src/**/*.ts' './tests/**.ts' './cli/**.ts'", "lint": "eslint . --ext ts --quiet --format unix", "lint:fix": "eslint . --ext ts --fix", - "update-idl": "cp target/idl/drift.json sdk/src/idl/drift.json" + "update-idl": "anchor build -- --features anchor-test && cp target/idl/drift.json sdk/src/idl/drift.json" }, "engines": { "node": ">=12" diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index fa14d88789..2d05480ce5 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -18,7 +18,7 @@ use crate::math::constants::{ LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, - SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, + PRICE_PRECISION_I64, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, }; use crate::math::margin::{ calculate_size_discount_asset_weight, calculate_size_premium_liability_weight, diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 2033723b86..4d39ae6d0f 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -9240,7 +9240,7 @@ export class DriftClient { public async updateUserGovTokenInsuranceStake( authority: PublicKey, - txParams?: TxParams, + txParams?: TxParams ): Promise { const ix = await this.getUpdateUserGovTokenInsuranceStakeIx(authority); const tx = await this.buildTransaction(ix, txParams); From f201d6a79342d9ec5c0be672a0c2d8639c41c715 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 22 Sep 2025 07:35:53 -0700 Subject: [PATCH 209/216] fix cargo build errors --- programs/drift/src/state/perp_market.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 2d05480ce5..a84f764a94 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -10,7 +10,9 @@ use crate::error::{DriftResult, ErrorCode}; use crate::math::amm; use crate::math::casting::Cast; #[cfg(test)] -use crate::math::constants::{AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT}; +use crate::math::constants::{ + AMM_RESERVE_PRECISION, MAX_CONCENTRATION_COEFFICIENT, PRICE_PRECISION_I64, +}; use crate::math::constants::{ AMM_TO_QUOTE_PRECISION_RATIO, BID_ASK_SPREAD_PRECISION, BID_ASK_SPREAD_PRECISION_I128, BID_ASK_SPREAD_PRECISION_U128, DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT, @@ -18,7 +20,7 @@ use crate::math::constants::{ LIQUIDATION_FEE_TO_MARGIN_PRECISION_RATIO, MARGIN_PRECISION, MARGIN_PRECISION_U128, MAX_LIQUIDATION_MULTIPLIER, PEG_PRECISION, PERCENTAGE_PRECISION, PERCENTAGE_PRECISION_I128, PERCENTAGE_PRECISION_I64, PERCENTAGE_PRECISION_U64, PRICE_PRECISION, PRICE_PRECISION_I128, - PRICE_PRECISION_I64, SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, + SPOT_WEIGHT_PRECISION, TWENTY_FOUR_HOUR, }; use crate::math::margin::{ calculate_size_discount_asset_weight, calculate_size_premium_liability_weight, From c653c67024e5bf9f2ef779acb0860225ef227fb6 Mon Sep 17 00:00:00 2001 From: wphan Date: Mon, 22 Sep 2025 07:36:26 -0700 Subject: [PATCH 210/216] v2.138.0 --- CHANGELOG.md | 9 +++++++++ Cargo.lock | 2 +- programs/drift/Cargo.toml | 2 +- sdk/package.json | 2 +- sdk/src/idl/drift.json | 7 ++----- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0dbcfc239..aca1ca772c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +### Fixes + +### Breaking + +## [2.138.0] - 2025-09-22 + +### Features + - program: support scaled ui extension ([#1894](https://github.com/drift-labs/protocol-v2/pull/1894)) +- Revert "Crispeaney/revert swift max margin ratio ([#1877](https://github.com/drift-labs/protocol-v2/pull/1877)) ### Fixes diff --git a/Cargo.lock b/Cargo.lock index 58aef0bce6..9ca9b1301f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "drift" -version = "2.137.0" +version = "2.138.0" dependencies = [ "ahash 0.8.6", "anchor-lang", diff --git a/programs/drift/Cargo.toml b/programs/drift/Cargo.toml index 945e055dee..39d0571545 100644 --- a/programs/drift/Cargo.toml +++ b/programs/drift/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift" -version = "2.137.0" +version = "2.138.0" description = "Created with Anchor" edition = "2018" diff --git a/sdk/package.json b/sdk/package.json index 9f2067b649..c5f4056243 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0-beta.9", + "version": "2.138.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 3fab52decf..6c25dcaf1c 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.137.0", + "version": "2.138.0", "name": "drift", "instructions": [ { @@ -16218,8 +16218,5 @@ "name": "InvalidIfRebalanceSwap", "msg": "Invalid If Rebalance Swap" } - ], - "metadata": { - "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" - } + ] } \ No newline at end of file From 1d03a2ae17700401ab857771454b84c71522aad8 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:45:00 +0000 Subject: [PATCH 211/216] sdk: release v2.139.0-beta.0 --- sdk/VERSION | 2 +- sdk/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/VERSION b/sdk/VERSION index b0c8a5df9a..2f0921947c 100644 --- a/sdk/VERSION +++ b/sdk/VERSION @@ -1 +1 @@ -2.138.0-beta.9 \ No newline at end of file +2.139.0-beta.0 \ No newline at end of file diff --git a/sdk/package.json b/sdk/package.json index c5f4056243..3f18133b90 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@drift-labs/sdk", - "version": "2.138.0", + "version": "2.139.0-beta.0", "main": "lib/node/index.js", "types": "lib/node/index.d.ts", "browser": "./lib/browser/index.js", From 2fb1389169719a6fb759e98334164e3ad34e7b73 Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:43:31 -0400 Subject: [PATCH 212/216] test update --- programs/drift/src/controller/position/tests.rs | 8 ++++---- programs/drift/src/math/amm_spread/tests.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index ab6cf1034c..b25efb70a8 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -599,7 +599,7 @@ fn amm_ref_price_decay_tail_test() { perp_market.amm.curve_update_intensity = 200; let max_ref_offset = perp_market.amm.get_max_reference_price_offset().unwrap(); - assert_eq!(max_ref_offset, 10000); + assert_eq!(max_ref_offset, 5000); let liquidity_ratio = crate::math::amm_spread::calculate_inventory_liquidity_ratio( perp_market.amm.base_asset_amount_with_amm, @@ -814,7 +814,7 @@ fn amm_ref_price_offset_decay_logic() { max_ref_offset, ) .unwrap(); - assert_eq!(res, 10000); + assert_eq!(res, 750); let mut now = perp_market.amm.last_mark_price_twap_ts + 10; let mut clock_slot = 353317544 + 20; // todo @@ -988,7 +988,7 @@ fn amm_negative_ref_price_offset_decay_logic() { max_ref_offset, ) .unwrap(); - assert_eq!(res, 10000); + assert_eq!(res, 750); let mut now = perp_market.amm.last_mark_price_twap_ts + 10; let mut clock_slot = 353317544 + 20; // todo @@ -1210,7 +1210,7 @@ fn amm_perp_ref_offset() { let r = perp_market.amm.reserve_price().unwrap(); let (b, a) = perp_market.amm.bid_ask_price(r).unwrap(); - assert_eq!(b, 7098999); + assert_eq!(b, 7102635); assert_eq!(a, 7106129); assert_eq!( perp_market.amm.historical_oracle_data.last_oracle_price, diff --git a/programs/drift/src/math/amm_spread/tests.rs b/programs/drift/src/math/amm_spread/tests.rs index 5953db1e04..74f6bd4d52 100644 --- a/programs/drift/src/math/amm_spread/tests.rs +++ b/programs/drift/src/math/amm_spread/tests.rs @@ -59,7 +59,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, 158); // 237*2/3); // 1 penny divergence + assert_eq!(res, 182); // 237*2/3); // 1 penny divergence let res = calculate_reference_price_offset( rev_price, 1, @@ -393,7 +393,7 @@ mod test { market.amm.curve_update_intensity = 110; let max_ref_offset = market.amm.get_max_reference_price_offset().unwrap(); - assert_eq!(max_ref_offset, 1000); // 10 bps + assert_eq!(max_ref_offset, 500); // 5 bps market.amm.curve_update_intensity = 200; let max_ref_offset = market.amm.get_max_reference_price_offset().unwrap(); From 4a6400ab7f4d50db60496291214bee80b07e237b Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:39:42 -0500 Subject: [PATCH 213/216] update ref price smoothing logic (wip tests) --- programs/drift/src/controller/amm.rs | 5 +- .../drift/src/controller/position/tests.rs | 46 +++++++++---------- programs/drift/src/math/amm_spread/tests.rs | 11 +++-- programs/drift/src/state/perp_market.rs | 3 +- sdk/src/math/amm.ts | 2 +- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/programs/drift/src/controller/amm.rs b/programs/drift/src/controller/amm.rs index 7162e9d2dd..d18a8cf636 100644 --- a/programs/drift/src/controller/amm.rs +++ b/programs/drift/src/controller/amm.rs @@ -276,10 +276,9 @@ pub fn update_spreads( market.amm.short_spread = short_spread; let do_reference_price_smooth = { - let sign_changed: bool = - reference_price_offset.signum() != market.amm.reference_price_offset.signum(); + let val_changed: bool = reference_price_offset != market.amm.reference_price_offset; - sign_changed && market.amm.curve_update_intensity > 100 + val_changed && market.amm.curve_update_intensity > 100 }; if do_reference_price_smooth { diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index ae256324c4..1598a73a55 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -586,7 +586,7 @@ fn amm_ref_price_decay_tail_test() { perp_market.amm.curve_update_intensity = 200; let max_ref_offset = perp_market.amm.get_max_reference_price_offset().unwrap(); - assert_eq!(max_ref_offset, 5000); + assert_eq!(max_ref_offset, 10000); let liquidity_ratio = crate::math::amm_spread::calculate_inventory_liquidity_ratio( perp_market.amm.base_asset_amount_with_amm, @@ -627,7 +627,7 @@ fn amm_ref_price_decay_tail_test() { assert_eq!(res, 0); let mut now = perp_market.amm.last_mark_price_twap_ts + 1; - let mut clock_slot = 354806508 + 1; // todo + let mut clock_slot = perp_market.amm.last_update_slot; let state = State::default(); let oracle_price_data = OraclePriceData { price: 3610241, @@ -653,7 +653,7 @@ fn amm_ref_price_decay_tail_test() { .unwrap(); assert_eq!(perp_market.amm.last_update_slot, clock_slot); assert_eq!(perp_market.amm.last_oracle_valid, true); - assert_eq!(perp_market.amm.reference_price_offset, -236093); + assert_eq!(perp_market.amm.reference_price_offset, -236183); // Run decay steps let mut offsets = Vec::new(); @@ -696,10 +696,10 @@ fn amm_ref_price_decay_tail_test() { assert_eq!( offsets, [ - -212475, -191219, -172089, -154872, -139376, -125430, -125410, -125390, -125370, - -125350, -112806, -101517, -91357, -82213, -73983, -66576, -59910, -53910, -48510, - -43650, -39276, -35340, -31797, -28609, -25740, -23157, -20833, -18741, -16858, -15164, - -13639, -12267, -11032, -9920, -8919, -8019, -7209, -6480, -5823, -5232, -4700, -4221, + -212556, -191292, -172154, -154930, -139428, -125477, -125457, -125437, -125417, + -125397, -112849, -101556, -91392, -82244, -74011, -66601, -59932, -53930, -48528, + -43667, -39292, -35354, -31810, -28620, -25749, -23166, -20841, -18748, -16865, -15170, + -13644, -12271, -11035, -9923, -8922, -8021, -7210, -6480, -5823, -5232, -4700, -4221, -3790, -3402, -3053, -2739, -2457, -2203, -1974, -1768, -1583, -1416, -1266, -1131, -1009, -900, -801, -712, -632, -560 ] @@ -707,19 +707,19 @@ fn amm_ref_price_decay_tail_test() { assert_eq!( lspreads, [ - 212587, 191331, 172201, 154984, 139488, 125542, 125522, 125502, 125482, 125462, 112918, - 101629, 91469, 82325, 74095, 66688, 60022, 54022, 48622, 43762, 39388, 35452, 31909, - 28721, 25852, 23269, 20945, 18853, 16970, 15276, 13751, 12379, 11144, 10032, 9031, - 8131, 7321, 6592, 5935, 5344, 4812, 4333, 3902, 3514, 3165, 2851, 2569, 2315, 2086, + 212668, 191404, 172266, 155042, 139540, 125589, 125569, 125549, 125529, 125509, 112961, + 101668, 91504, 82356, 74123, 66713, 60044, 54042, 48640, 43779, 39404, 35466, 31922, + 28732, 25861, 23278, 20953, 18860, 16977, 15282, 13756, 12383, 11147, 10035, 9034, + 8133, 7322, 6592, 5935, 5344, 4812, 4333, 3902, 3514, 3165, 2851, 2569, 2315, 2086, 1880, 1695, 1528, 1378, 1243, 1121, 1012, 913, 824, 744, 672 ] ); assert_eq!( sspreads, [ - 23633, 21271, 19145, 17232, 15511, 13961, 35, 35, 35, 35, 12559, 11304, 10175, 9159, - 8245, 7422, 6681, 6015, 5415, 4875, 4389, 3951, 3558, 3203, 2884, 2598, 2339, 2107, - 1898, 1709, 1540, 1387, 1250, 1127, 1016, 915, 825, 744, 672, 606, 547, 494, 446, 403, + 23642, 21279, 19153, 17239, 15517, 13966, 35, 35, 35, 35, 12563, 11308, 10179, 9163, + 8248, 7425, 6684, 6017, 5417, 4876, 4390, 3953, 3559, 3205, 2886, 2598, 2340, 2108, + 1898, 1710, 1541, 1388, 1251, 1127, 1016, 916, 826, 745, 672, 606, 547, 494, 446, 403, 364, 329, 297, 269, 244, 221, 200, 182, 165, 150, 137, 124, 114, 104, 95, 87 ] ); @@ -801,10 +801,10 @@ fn amm_ref_price_offset_decay_logic() { max_ref_offset, ) .unwrap(); - assert_eq!(res, 750); + assert_eq!(res, 10000); let mut now = perp_market.amm.last_mark_price_twap_ts + 10; - let mut clock_slot = 353317544 + 20; // todo + let mut clock_slot = perp_market.amm.last_update_slot; let state = State::default(); let oracle_price_data = OraclePriceData { price: 120003893646, @@ -830,7 +830,7 @@ fn amm_ref_price_offset_decay_logic() { .unwrap(); assert_eq!(perp_market.amm.last_update_slot, clock_slot); assert_eq!(perp_market.amm.last_oracle_valid, true); - assert_eq!(perp_market.amm.reference_price_offset, 4458); + assert_eq!(perp_market.amm.reference_price_offset, 20); perp_market.amm.last_mark_price_twap_5min = (perp_market .amm @@ -976,10 +976,10 @@ fn amm_negative_ref_price_offset_decay_logic() { max_ref_offset, ) .unwrap(); - assert_eq!(res, 750); + assert_eq!(res, 10000); let mut now = perp_market.amm.last_mark_price_twap_ts + 10; - let mut clock_slot = 353317544 + 20; // todo + let mut clock_slot = perp_market.amm.last_update_slot; let state = State::default(); let oracle_price_data = OraclePriceData { price: 120003893646, @@ -1005,7 +1005,7 @@ fn amm_negative_ref_price_offset_decay_logic() { .unwrap(); assert_eq!(perp_market.amm.last_update_slot, clock_slot); assert_eq!(perp_market.amm.last_oracle_valid, true); - assert_eq!(perp_market.amm.reference_price_offset, 4458); + assert_eq!(perp_market.amm.reference_price_offset, 20); perp_market.amm.last_mark_price_twap_5min = (perp_market .amm @@ -1195,13 +1195,13 @@ fn amm_perp_ref_offset() { let r = perp_market.amm.reserve_price().unwrap(); let (b, a) = perp_market.amm.bid_ask_price(r).unwrap(); - assert_eq!(b, 7102635); - assert_eq!(a, 7106129); + assert_eq!(b, 7098048); + assert_eq!(a, 7232935); assert_eq!( perp_market.amm.historical_oracle_data.last_oracle_price, 7101600 ); - assert_eq!(perp_market.amm.reference_price_offset, 134); + assert_eq!(perp_market.amm.reference_price_offset, 17890); assert_eq!(perp_market.amm.max_spread, 90000); assert_eq!(r, 7101599); diff --git a/programs/drift/src/math/amm_spread/tests.rs b/programs/drift/src/math/amm_spread/tests.rs index 3a364b9356..6ae42d1b99 100644 --- a/programs/drift/src/math/amm_spread/tests.rs +++ b/programs/drift/src/math/amm_spread/tests.rs @@ -73,7 +73,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, 237 * 2); // 3 penny divergence + assert_eq!(res, 814); let res = calculate_reference_price_offset( rev_price, @@ -87,7 +87,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, -517); // counter acting 24h_avg sign + assert_eq!(res, 0); // disregard 24h_avg sign let res = calculate_reference_price_offset( rev_price, @@ -248,8 +248,11 @@ mod test { market.amm.last_mark_price_twap = 4216 * 10000 - 2 * 10000; market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 8 / 20) as i128 * -1; let (_l, _s) = update_spreads(&mut market, reserve_price as u64, None).unwrap(); - println!("ref offset: {}", market.amm.reference_price_offset); - assert!(market.amm.reference_price_offset < 0); + println!( + "ref offset: {}, {}, {}", + market.amm.reference_price_offset, _l, _s + ); + assert!(market.amm.reference_price_offset - (_s as i32) < 0); // Same for short pos market.amm.base_asset_amount_with_amm = (AMM_RESERVE_PRECISION * 6 / 20) as i128 * -1; diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 3be244d086..099285f935 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -1456,7 +1456,8 @@ impl AMM { if self.curve_update_intensity <= 100 { return Ok(0); } else if self.curve_update_intensity >= 200 { - return Ok(self.max_spread.cast::()? / 2); + // mimic old max behavior with 100 bps + return Ok((self.max_spread.cast::()? / 2).max(10_000)); } let lower_bound_multiplier: i64 = diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index 88a9ec9f5b..1a2f1cbfee 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -1086,7 +1086,7 @@ export function calculateSpreadReserves( ); const doReferencePricOffsetSmooth = - Math.sign(referencePriceOffset) !== Math.sign(amm.referencePriceOffset) && + referencePriceOffset !== amm.referencePriceOffset && amm.curveUpdateIntensity > 100; if (doReferencePricOffsetSmooth) { From dff154cca603286499d5e7da3fc5a30247836251 Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:42:43 -0500 Subject: [PATCH 214/216] unsigned liquidity frac mul --- programs/drift/src/math/amm_spread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/drift/src/math/amm_spread.rs b/programs/drift/src/math/amm_spread.rs index ab219d0a79..ec91265ac0 100644 --- a/programs/drift/src/math/amm_spread.rs +++ b/programs/drift/src/math/amm_spread.rs @@ -641,7 +641,7 @@ pub fn calculate_reference_price_offset( || (mark_premium_avg_pct <= 0 && liquidity_fraction <= 0) { mark_premium_avg_pct - .safe_mul(liquidity_fraction.cast::()?)? + .safe_mul(liquidity_fraction.unsigned_abs().cast::()?)? .safe_div(5)? } else { 0 From 2652d65c9092b14c6085d5880165b4f00ea59e5c Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:58:19 -0500 Subject: [PATCH 215/216] rm val_change --- programs/drift/src/controller/amm.rs | 22 ++--- .../drift/src/controller/position/tests.rs | 84 +++++++++++-------- programs/drift/src/math/amm_spread.rs | 2 +- programs/drift/src/math/amm_spread/tests.rs | 12 +-- programs/drift/src/state/amm_cache.rs | 5 +- programs/drift/src/state/perp_market.rs | 1 + 6 files changed, 69 insertions(+), 57 deletions(-) diff --git a/programs/drift/src/controller/amm.rs b/programs/drift/src/controller/amm.rs index 6ffd105695..0579d0f6de 100644 --- a/programs/drift/src/controller/amm.rs +++ b/programs/drift/src/controller/amm.rs @@ -277,9 +277,11 @@ pub fn update_spreads( market.amm.short_spread = short_spread; let do_reference_price_smooth = { - let val_changed: bool = reference_price_offset != market.amm.reference_price_offset; + // let val_changed: bool = reference_price_offset != market.amm.reference_price_offset; + let sign_changed: bool = + reference_price_offset.signum() != market.amm.reference_price_offset.signum(); - val_changed && market.amm.curve_update_intensity > 100 + sign_changed && market.amm.curve_update_intensity > 100 }; if do_reference_price_smooth { @@ -291,18 +293,18 @@ pub fn update_spreads( .saturating_sub(market.amm.reference_price_offset.cast::()?); let raw = full_offset_delta .abs() - .cast::()? - .min(slots_passed.cast::()?.safe_mul(1000)?) - .cast::()? + .min(slots_passed.cast::()?.safe_mul(1000_i128)?) .safe_div(10_i128)? .cast::()?; full_offset_delta.signum().cast::()? - * (raw.max(10).min(if market.amm.reference_price_offset != 0 { - market.amm.reference_price_offset.abs() - } else { - reference_price_offset.abs() - })) + * (raw + .max(10_i32) + .min(if market.amm.reference_price_offset != 0 { + market.amm.reference_price_offset.abs() + } else { + reference_price_offset.abs() + })) }; market.amm.reference_price_offset = market diff --git a/programs/drift/src/controller/position/tests.rs b/programs/drift/src/controller/position/tests.rs index 1598a73a55..b16f14d096 100644 --- a/programs/drift/src/controller/position/tests.rs +++ b/programs/drift/src/controller/position/tests.rs @@ -830,7 +830,7 @@ fn amm_ref_price_offset_decay_logic() { .unwrap(); assert_eq!(perp_market.amm.last_update_slot, clock_slot); assert_eq!(perp_market.amm.last_oracle_valid, true); - assert_eq!(perp_market.amm.reference_price_offset, 20); + assert_eq!(perp_market.amm.reference_price_offset, 10000); perp_market.amm.last_mark_price_twap_5min = (perp_market .amm @@ -880,28 +880,30 @@ fn amm_ref_price_offset_decay_logic() { assert_eq!( offsets, [ - 4248, 4038, 3828, 3618, 3408, 3198, 3178, 3158, 3138, 3118, 2908, 2698, 2488, 2278, - 2068, 1858, 1664, 1489, 1332, 1190, 1062, 947, 844, 751, 667, 592, 524, 463, 408, 359, - 315, 275, 239, 207, 178, 152, 128, 107, 87, 67, 47, 27, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 9790, 9580, 9370, 9160, 8950, 8740, 8720, 8700, 8680, 8660, 8450, 8240, 8030, 7820, + 7610, 7400, 7190, 6980, 6770, 6560, 6350, 6140, 5930, 5720, 5510, 5300, 5090, 4880, + 4670, 4460, 4250, 4040, 3830, 3620, 3410, 3200, 2990, 2780, 2570, 2360, 2150, 1940, + 1737, 1555, 1391, 1243, 1110, 990, 882, 785, 698, 620, 549, 486, 429, 378, 332, 290, + 252, 218 ] ); assert_eq!( lspreads, [ - 726, 726, 726, 726, 726, 726, 536, 536, 536, 536, 726, 726, 726, 726, 726, 726, 710, - 691, 673, 658, 644, 631, 619, 609, 600, 591, 584, 577, 571, 565, 560, 556, 552, 548, - 545, 542, 540, 537, 536, 536, 536, 536, 536, 526, 526, 526, 526, 526, 526, 526, 526, - 526, 526, 526, 526, 526, 526, 526, 526, 526 + 726, 726, 726, 726, 726, 726, 536, 536, 536, 536, 726, 726, 726, 726, 726, 726, 726, + 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, + 726, 726, 726, 726, 726, 726, 726, 726, 719, 698, 680, 664, 649, 636, 624, 613, 603, + 594, 587, 579, 573, 567, 562, 558, 554, 550 ] ); assert_eq!( sspreads, [ - 4258, 4048, 3838, 3628, 3418, 3208, 3188, 3168, 3148, 3128, 2918, 2708, 2498, 2288, - 2078, 1868, 1674, 1499, 1342, 1200, 1072, 957, 854, 761, 677, 602, 534, 473, 418, 369, - 325, 285, 249, 217, 188, 162, 138, 117, 97, 77, 57, 37, 17, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 + 9800, 9590, 9380, 9170, 8960, 8750, 8730, 8710, 8690, 8670, 8460, 8250, 8040, 7830, + 7620, 7410, 7200, 6990, 6780, 6570, 6360, 6150, 5940, 5730, 5520, 5310, 5100, 4890, + 4680, 4470, 4260, 4050, 3840, 3630, 3420, 3210, 3000, 2790, 2580, 2370, 2160, 1950, + 1747, 1565, 1401, 1253, 1120, 1000, 892, 795, 708, 630, 559, 496, 439, 388, 342, 300, + 262, 228 ] ); } @@ -1005,7 +1007,7 @@ fn amm_negative_ref_price_offset_decay_logic() { .unwrap(); assert_eq!(perp_market.amm.last_update_slot, clock_slot); assert_eq!(perp_market.amm.last_oracle_valid, true); - assert_eq!(perp_market.amm.reference_price_offset, 20); + assert_eq!(perp_market.amm.reference_price_offset, 10000); perp_market.amm.last_mark_price_twap_5min = (perp_market .amm @@ -1051,36 +1053,46 @@ fn amm_negative_ref_price_offset_decay_logic() { offsets.push(perp_market.amm.reference_price_offset); lspreads.push(perp_market.amm.long_spread); sspreads.push(perp_market.amm.short_spread); + + // if perp_market.amm.reference_price_offset == 0 { + // assert_eq!(i, 1); + // } } + // assert_eq!(lspreads[52], 0); + // assert_eq!(lspreads[51], 0); // when offset flips + assert_eq!( offsets, [ - -4248, -4038, -3828, -3618, -3408, -3198, -3178, -3158, -3138, -3118, -2908, -2698, - -2488, -2278, -2068, -1858, -1648, -1438, -1228, -1018, -808, -598, -388, -178, 0, - 7654, 7652, 7651, 7649, 7648, 7646, 7645, 7643, 7641, 7640, 7638, 7637, 7635, 7634, - 7632, 7631, 7629, 7628, 7626, 7625, 7623, 7622, 7620, 7619, 7618, 7616, 7615, 7613, - 7612, 7610, 7609, 7607, 7606, 7605, 7603, 7602, 7600, 7599, 7597, 7596, 7595, 7593, - 7592, 7591, 7589, 7588, 7586, 7585, 7584, 7582, 7581, 7580, 7578, 7577, 7576 + -9790, -9580, -9370, -9160, -8950, -8740, -8720, -8700, -8680, -8660, -8450, -8240, + -8030, -7820, -7610, -7400, -7190, -6980, -6770, -6560, -6350, -6140, -5930, -5720, + -5510, -5300, -5090, -4880, -4670, -4460, -4250, -4040, -3830, -3620, -3410, -3200, + -2990, -2780, -2570, -2360, -2150, -1940, -1730, -1520, -1310, -1100, -890, -680, -470, + -260, -50, 0, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, + 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, + 10000, 10000, 10000, 10000, 10000, 10000 ] ); assert_eq!( sspreads, [ 210, 210, 210, 210, 210, 210, 20, 20, 20, 20, 210, 210, 210, 210, 210, 210, 210, 210, - 210, 210, 210, 210, 210, 210, 178, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 50, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 + 10, 10, 10, 10, 10 ] ); assert_eq!( lspreads, [ - 4774, 4564, 4354, 4144, 3934, 3724, 3704, 3684, 3664, 3644, 3434, 3224, 3014, 2804, - 2594, 2384, 2174, 1964, 1754, 1544, 1334, 1124, 914, 704, 526, 526, 526, 526, 526, 526, - 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, + 10316, 10106, 9896, 9686, 9476, 9266, 9246, 9226, 9206, 9186, 8976, 8766, 8556, 8346, + 8136, 7926, 7716, 7506, 7296, 7086, 6876, 6666, 6456, 6246, 6036, 5826, 5616, 5406, + 5196, 4986, 4776, 4566, 4356, 4146, 3936, 3726, 3516, 3306, 3096, 2886, 2676, 2466, + 2256, 2046, 1836, 1626, 1416, 1206, 996, 786, 576, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, - 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526 + 526, 526, 526, 526, 526 ] ); } @@ -1195,18 +1207,18 @@ fn amm_perp_ref_offset() { let r = perp_market.amm.reserve_price().unwrap(); let (b, a) = perp_market.amm.bid_ask_price(r).unwrap(); - assert_eq!(b, 7098048); - assert_eq!(a, 7232935); + assert_eq!(b, 7108594); + assert_eq!(a, 7115724); assert_eq!( perp_market.amm.historical_oracle_data.last_oracle_price, 7101600 ); - assert_eq!(perp_market.amm.reference_price_offset, 17890); + assert_eq!(perp_market.amm.reference_price_offset, 1485); assert_eq!(perp_market.amm.max_spread, 90000); assert_eq!(r, 7101599); - assert_eq!(perp_market.amm.bid_base_asset_reserve, 4675159724262455); - assert_eq!(perp_market.amm.ask_base_asset_reserve, 4672813088646692); + assert_eq!(perp_market.amm.bid_base_asset_reserve, 4672004879737647); + assert_eq!(perp_market.amm.ask_base_asset_reserve, 4669662283322685); crate::validation::perp_market::validate_perp_market(&perp_market).unwrap(); @@ -1233,10 +1245,10 @@ fn amm_perp_ref_offset() { .amm .bid_ask_price(reserve_price_mm_offset) .unwrap(); - assert_eq!(perp_market.amm.reference_price_offset, 133); + assert_eq!(perp_market.amm.reference_price_offset, 1462); assert_eq!(reserve_price_mm_offset, 7137107); - assert_eq!(b2, 7101549); - assert_eq!(a2, 7174591); + assert_eq!(b2, 7111035); + assert_eq!(a2, 7184076); // Uses the original oracle if the slot is old, ignoring MM oracle perp_market.amm.mm_oracle_price = mm_oracle_price_data.get_price() * 995 / 1000; @@ -1256,8 +1268,8 @@ fn amm_perp_ref_offset() { .bid_ask_price(reserve_price_mm_offset_3) .unwrap(); assert_eq!(reserve_price_mm_offset_3, r); - assert_eq!(b3, 7066225); - assert_eq!(a3, 7138903); + assert_eq!(b3, 7075820); + assert_eq!(a3, 7148497); } #[test] diff --git a/programs/drift/src/math/amm_spread.rs b/programs/drift/src/math/amm_spread.rs index ec91265ac0..047be54631 100644 --- a/programs/drift/src/math/amm_spread.rs +++ b/programs/drift/src/math/amm_spread.rs @@ -642,7 +642,7 @@ pub fn calculate_reference_price_offset( { mark_premium_avg_pct .safe_mul(liquidity_fraction.unsigned_abs().cast::()?)? - .safe_div(5)? + .safe_div(2)? } else { 0 }; diff --git a/programs/drift/src/math/amm_spread/tests.rs b/programs/drift/src/math/amm_spread/tests.rs index 6ae42d1b99..322013318c 100644 --- a/programs/drift/src/math/amm_spread/tests.rs +++ b/programs/drift/src/math/amm_spread/tests.rs @@ -60,7 +60,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, 182); // 237*2/3); // 1 penny divergence + assert_eq!(res, 455); // 237*2/3); // 1 penny divergence let res = calculate_reference_price_offset( rev_price, 1, @@ -73,7 +73,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, 814); + assert_eq!(res, 2035); let res = calculate_reference_price_offset( rev_price, @@ -101,7 +101,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, -542); // counteracting 24h_avg / base inventory sign + assert_eq!(res, -2500); // counteracting 24h_avg / base inventory sign let res = calculate_reference_price_offset( rev_price, @@ -115,7 +115,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, -1149); // flipped + assert_eq!(res, -2500); // flipped let res = calculate_reference_price_offset( rev_price, @@ -129,7 +129,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, 1660 * 2 / 3); // 7 penny divergence + assert_eq!(res, 2500); // 7 penny divergence let res = calculate_reference_price_offset( rev_price, @@ -187,7 +187,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, 0); + assert_eq!(res, -32); } #[test] diff --git a/programs/drift/src/state/amm_cache.rs b/programs/drift/src/state/amm_cache.rs index 1e485b2289..d576c12646 100644 --- a/programs/drift/src/state/amm_cache.rs +++ b/programs/drift/src/state/amm_cache.rs @@ -112,10 +112,7 @@ impl CacheInfo { pub fn update_perp_market_fields(&mut self, perp_market: &PerpMarket) -> DriftResult<()> { self.oracle = perp_market.amm.oracle; self.oracle_source = u8::from(perp_market.amm.oracle_source); - self.position = perp_market - .amm - .get_protocol_owned_position()? - .safe_mul(-1)?; + self.position = perp_market.amm.get_protocol_owned_position()?; self.lp_status_for_perp_market = perp_market.lp_status; Ok(()) } diff --git a/programs/drift/src/state/perp_market.rs b/programs/drift/src/state/perp_market.rs index 099285f935..298891520a 100644 --- a/programs/drift/src/state/perp_market.rs +++ b/programs/drift/src/state/perp_market.rs @@ -1448,6 +1448,7 @@ impl AMM { )) } + // this is where pub fn get_protocol_owned_position(self) -> DriftResult { self.base_asset_amount_with_amm.cast::() } From b42c2c819aadace2615c96cf291e55a1f00f33c6 Mon Sep 17 00:00:00 2001 From: 0xbigz <83473873+0xbigz@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:01:07 -0500 Subject: [PATCH 216/216] update tests --- programs/drift/src/math/amm_spread/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/drift/src/math/amm_spread/tests.rs b/programs/drift/src/math/amm_spread/tests.rs index 322013318c..eaf55b5333 100644 --- a/programs/drift/src/math/amm_spread/tests.rs +++ b/programs/drift/src/math/amm_spread/tests.rs @@ -187,7 +187,7 @@ mod test { max_offset, ) .unwrap(); - assert_eq!(res, -32); + assert_eq!(res, 0); } #[test]