From d31d2379f54214fb3b49aae1b10f0c6ef70b2829 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sat, 22 Jul 2023 21:57:20 -0400 Subject: [PATCH 1/2] add take ix --- programs/jit-proxy/src/lib.rs | 416 +++++++++++++++++++++++++++++----- ts/sdk/src/jitProxyClient.ts | 97 ++++++++ ts/sdk/src/types/jit_proxy.ts | 206 +++++++++++++++++ 3 files changed, 657 insertions(+), 62 deletions(-) diff --git a/programs/jit-proxy/src/lib.rs b/programs/jit-proxy/src/lib.rs index 1d23957..4b5ac80 100644 --- a/programs/jit-proxy/src/lib.rs +++ b/programs/jit-proxy/src/lib.rs @@ -1,24 +1,30 @@ use anchor_lang::prelude::*; use borsh::{BorshDeserialize, BorshSerialize}; use drift::controller::position::PositionDirection; -use drift::cpi::accounts::PlaceAndMake; +use drift::cpi::accounts::{PlaceAndMake, PlaceAndTake}; use drift::error::DriftResult; use drift::instructions::optional_accounts::{load_maps, AccountMaps}; use drift::instructions::OrderParams; use drift::instructions::PostOnlyParam as DriftPostOnlyParam; +use drift::math::casting::Cast; +use drift::math::matching::do_orders_cross; +use drift::math::orders::find_maker_orders; use drift::math::safe_math::SafeMath; use drift::program::Drift; -use drift::state::perp_market_map::MarketSet; +use drift::state::oracle_map::OracleMap; +use drift::state::perp_market_map::{MarketSet, PerpMarketMap}; +use drift::state::spot_market_map::SpotMarketMap; use drift::state::state::State; -use drift::state::user::{MarketType, OrderTriggerCondition, OrderType}; +use drift::state::user::{MarketType as DriftMarketType, OrderTriggerCondition, OrderType}; use drift::state::user::{User, UserStats}; +use drift::state::user_map::load_user_maps; declare_id!("J1TnP8zvVxbtF5KFp5xRmWuvG9McnhzmBd9XGfCyuxFP"); #[program] pub mod jit_proxy { use super::*; - use drift::math::casting::Cast; + use std::cell::Ref; pub fn jit<'info>( ctx: Context<'_, '_, '_, 'info, Jit<'info>>, @@ -50,17 +56,13 @@ pub mod jit_proxy { None, )?; - let (oracle_price, tick_size) = if market_type == MarketType::Perp { - let perp_market = perp_market_map.get_ref(&market_index)?; - let oracle_price = oracle_map.get_price_data(&perp_market.amm.oracle)?.price; - - (oracle_price, perp_market.amm.order_tick_size) - } else { - let spot_market = spot_market_map.get_ref(&market_index)?; - let oracle_price = oracle_map.get_price_data(&spot_market.oracle)?.price; - - (oracle_price, spot_market.order_tick_size) - }; + let (oracle_price, tick_size) = get_oracle_price_and_tick_size( + market_index, + market_type, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + )?; let taker_price = taker_order.force_get_limit_price(Some(oracle_price), None, slot, tick_size)?; @@ -70,13 +72,21 @@ pub mod jit_proxy { match maker_direction { PositionDirection::Long => { if taker_price > maker_worst_price { - msg!("taker price {} > worst bid {}", taker_price, maker_worst_price); + msg!( + "taker price {} > worst bid {}", + taker_price, + maker_worst_price + ); return Err(ErrorCode::BidNotCrossed.into()); } } PositionDirection::Short => { if taker_price < maker_worst_price { - msg!("taker price {} < worst ask {}", taker_price, maker_worst_price); + msg!( + "taker price {} < worst ask {}", + taker_price, + maker_worst_price + ); return Err(ErrorCode::AskNotCrossed.into()); } } @@ -84,50 +94,18 @@ pub mod jit_proxy { let maker_price = taker_price; let taker_base_asset_amount_unfilled = taker_order.get_base_asset_amount_unfilled(None)?; - let maker_existing_position = if market_type == MarketType::Perp { - let perp_market = perp_market_map.get_ref(&market_index)?; - let perp_position = maker.get_perp_position(market_index); - match perp_position { - Ok(perp_position) => { - perp_position - .simulate_settled_lp_position(&perp_market, oracle_price)? - .base_asset_amount - } - Err(_) => 0, - } - } else { - let spot_market = spot_market_map.get_ref(&market_index)?; - maker - .get_spot_position(market_index) - .map_or(0, |p| p.get_signed_token_amount(&spot_market).unwrap()) - .cast::()? - }; - - let maker_base_asset_amount = if maker_direction == PositionDirection::Long { - let size = params.max_position.safe_sub(maker_existing_position)?; - - if size <= 0 { - msg!( - "maker existing position {} >= max position {}", - maker_existing_position, - params.max_position - ); - } - - size.unsigned_abs().min(taker_base_asset_amount_unfilled) - } else { - let size = maker_existing_position.safe_sub(params.min_position)?; - - if size <= 0 { - msg!( - "maker existing position {} <= max position {}", - maker_existing_position, - params.max_position - ); - } - - size.unsigned_abs().min(taker_base_asset_amount_unfilled) - }; + let maker_base_asset_amount = calculate_max_order_size( + &maker, + maker_direction, + Some(taker_base_asset_amount_unfilled), + params.min_position, + params.max_position, + market_index, + market_type, + oracle_price, + &perp_market_map, + &spot_market_map, + )?; let order_params = OrderParams { order_type: OrderType::Limit, @@ -159,6 +137,235 @@ pub mod jit_proxy { Ok(()) } + + pub fn take<'info>( + ctx: Context<'_, '_, '_, 'info, Take<'info>>, + params: TakeParams, + ) -> Result<()> { + let clock = Clock::get()?; + let slot = clock.slot; + + let taker = ctx.accounts.user.load()?; + + let market_index = params.market_index; + let market_type = params.market_type.to_drift_param(); + + let remaining_accounts_iter = &mut ctx.remaining_accounts.iter().peekable(); + let AccountMaps { + perp_market_map, + spot_market_map, + mut oracle_map, + } = load_maps( + remaining_accounts_iter, + &MarketSet::new(), + &MarketSet::new(), + slot, + None, + )?; + + let (makers, _) = load_user_maps(remaining_accounts_iter)?; + + let (oracle_price, tick_size) = get_oracle_price_and_tick_size( + market_index, + market_type, + &perp_market_map, + &spot_market_map, + &mut oracle_map, + )?; + + let can_take = |taker_direction: PositionDirection, taker_price: u64| -> Result { + let mut best_maker = match taker_direction { + PositionDirection::Long => 0_u64, + PositionDirection::Short => u64::MAX, + }; + + let maker_direction = taker_direction.opposite(); + for (_, maker_account_loader) in makers.0.iter() { + let maker: Ref = maker_account_loader.load()?; + + let maker_order_price_and_indexes = find_maker_orders( + &maker, + &maker_direction, + &market_type, + market_index, + Some(oracle_price), + slot, + tick_size, + )?; + + // check if taker crosses any maker orders + for (_, maker_price) in maker_order_price_and_indexes { + if do_orders_cross(maker_direction, maker_price, taker_price) { + return Ok(true); + } + + best_maker = match taker_direction { + PositionDirection::Long => best_maker.max(maker_price), + PositionDirection::Short => best_maker.min(maker_price), + }; + } + } + + msg!( + "taker price {:?} direction {}", + taker_direction, + taker_price + ); + msg!("best maker price: {}", best_maker); + + Ok(false) + }; + + let bid = params.get_worst_price(oracle_price, PositionDirection::Long)?; + let bid_crosses = can_take(PositionDirection::Long, bid)?; + + let ask = params.get_worst_price(oracle_price, PositionDirection::Short)?; + let ask_crosses = can_take(PositionDirection::Short, ask)?; + + if !bid_crosses && !ask_crosses { + msg!("did not cross any maker orders"); + return Err(ErrorCode::DidNotCrossMakers.into()); + } + + let get_order_params = + |taker_direction: PositionDirection, taker_price: u64| -> Result { + let taker_base_asset_amount = calculate_max_order_size( + &taker, + taker_direction, + None, + params.min_position, + params.max_position, + market_index, + market_type, + oracle_price, + &perp_market_map, + &spot_market_map, + )?; + + let order_params = OrderParams { + order_type: OrderType::Limit, + market_type, + direction: taker_direction, + user_order_id: 0, + base_asset_amount: taker_base_asset_amount, + price: taker_price, + market_index, + reduce_only: false, + post_only: PostOnlyParam::None.to_drift_param(), + immediate_or_cancel: true, + max_ts: None, + trigger_price: None, + trigger_condition: OrderTriggerCondition::Above, + oracle_price_offset: None, + auction_duration: None, + auction_start_price: None, + auction_end_price: None, + }; + + Ok(order_params) + }; + + let mut orders_params = Vec::with_capacity(2); + + if bid_crosses { + msg!("Trying to buy. Bid {}", bid); + orders_params.push(get_order_params(PositionDirection::Long, bid)?); + } + + if ask_crosses { + msg!("Trying to sell. Ask {}", ask); + orders_params.push(get_order_params(PositionDirection::Short, ask)?); + } + + drop(taker); + + place_and_take(ctx, orders_params)?; + + Ok(()) + } +} + +fn get_oracle_price_and_tick_size( + market_index: u16, + market_type: DriftMarketType, + perp_market_map: &PerpMarketMap, + spot_market_map: &SpotMarketMap, + oracle_map: &mut OracleMap, +) -> DriftResult<(i64, u64)> { + let (oracle_price, tick_size) = if market_type == DriftMarketType::Perp { + let perp_market = perp_market_map.get_ref(&market_index)?; + let oracle_price = oracle_map.get_price_data(&perp_market.amm.oracle)?.price; + + (oracle_price, perp_market.amm.order_tick_size) + } else { + let spot_market = spot_market_map.get_ref(&market_index)?; + let oracle_price = oracle_map.get_price_data(&spot_market.oracle)?.price; + + (oracle_price, spot_market.order_tick_size) + }; + + Ok((oracle_price, tick_size)) +} + +fn calculate_max_order_size( + user: &User, + direction: PositionDirection, + counter_party_size: Option, + min_position: i64, + max_position: i64, + market_index: u16, + market_type: DriftMarketType, + oracle_price: i64, + perp_market_map: &PerpMarketMap, + spot_market_map: &SpotMarketMap, +) -> DriftResult { + let user_existing_position = if market_type == DriftMarketType::Perp { + let perp_market = perp_market_map.get_ref(&market_index)?; + let perp_position = user.get_perp_position(market_index); + match perp_position { + Ok(perp_position) => { + perp_position + .simulate_settled_lp_position(&perp_market, oracle_price)? + .base_asset_amount + } + Err(_) => 0, + } + } else { + let spot_market = spot_market_map.get_ref(&market_index)?; + user.get_spot_position(market_index) + .map_or(0, |p| p.get_signed_token_amount(&spot_market).unwrap()) + .cast::()? + }; + + let user_base_asset_amount = if direction == PositionDirection::Long { + let size = max_position.safe_sub(user_existing_position)?; + + if size <= 0 { + msg!( + "user existing position {} >= max position {}", + user_existing_position, + max_position + ); + } + + size.unsigned_abs() + .min(counter_party_size.unwrap_or(u64::MAX)) + } else { + let size = user_existing_position.safe_sub(min_position)?; + + if size <= 0 { + msg!( + "user existing position {} <= min position {}", + user_existing_position, + min_position + ); + } + + size.unsigned_abs() + .min(counter_party_size.unwrap_or(u64::MAX)) + }; + + Ok(user_base_asset_amount) } #[derive(Accounts)] @@ -238,6 +445,8 @@ pub enum ErrorCode { AskNotCrossed, #[msg("TakerOrderNotFound")] TakerOrderNotFound, + #[msg("DidNotCrossMakers")] + DidNotCrossMakers, } fn place_and_make<'info>( @@ -258,7 +467,7 @@ fn place_and_make<'info>( let cpi_context = CpiContext::new(drift_program, cpi_accounts) .with_remaining_accounts(ctx.remaining_accounts.into()); - if order_params.market_type == MarketType::Perp { + if order_params.market_type == DriftMarketType::Perp { drift::cpi::place_and_make_perp_order(cpi_context, order_params, taker_order_id)?; } else { drift::cpi::place_and_make_spot_order(cpi_context, order_params, taker_order_id, None)?; @@ -266,3 +475,86 @@ fn place_and_make<'info>( Ok(()) } + +#[derive(Accounts)] +pub struct Take<'info> { + pub state: Box>, + #[account(mut)] + pub user: AccountLoader<'info, User>, + #[account(mut)] + pub user_stats: AccountLoader<'info, UserStats>, + pub authority: Signer<'info>, + pub drift_program: Program<'info, Drift>, +} + +#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)] +pub struct TakeParams { + pub market_index: u16, + pub market_type: MarketType, + pub max_position: i64, + pub min_position: i64, + pub bid: i64, + pub ask: i64, + pub price_type: PriceType, + pub fulfillment_method: Option, +} + +impl TakeParams { + pub fn get_worst_price( + self, + oracle_price: i64, + taker_direction: PositionDirection, + ) -> DriftResult { + match (taker_direction, self.price_type) { + (PositionDirection::Long, PriceType::Limit) => Ok(self.bid.unsigned_abs()), + (PositionDirection::Short, PriceType::Limit) => Ok(self.ask.unsigned_abs()), + (PositionDirection::Long, PriceType::Oracle) => { + Ok(oracle_price.safe_add(self.bid)?.unsigned_abs()) + } + (PositionDirection::Short, PriceType::Oracle) => { + Ok(oracle_price.safe_add(self.ask)?.unsigned_abs()) + } + } + } +} + +#[derive(Clone, Copy, BorshSerialize, BorshDeserialize, PartialEq, Debug, Eq)] +pub enum MarketType { + Perp, + Spot, +} + +impl MarketType { + pub fn to_drift_param(self) -> DriftMarketType { + match self { + MarketType::Perp => DriftMarketType::Perp, + MarketType::Spot => DriftMarketType::Spot, + } + } +} + +fn place_and_take<'info>( + ctx: Context<'_, '_, '_, 'info, Take<'info>>, + orders_params: Vec, +) -> Result<()> { + for order_params in orders_params { + let drift_program = ctx.accounts.drift_program.to_account_info().clone(); + let cpi_accounts = PlaceAndTake { + state: ctx.accounts.state.to_account_info().clone(), + user: ctx.accounts.user.to_account_info().clone(), + user_stats: ctx.accounts.user_stats.to_account_info().clone(), + authority: ctx.accounts.authority.to_account_info().clone(), + }; + + let cpi_context = CpiContext::new(drift_program, cpi_accounts) + .with_remaining_accounts(ctx.remaining_accounts.into()); + + if order_params.market_type == DriftMarketType::Perp { + drift::cpi::place_and_take_perp_order(cpi_context, order_params, None)?; + } else { + drift::cpi::place_and_take_spot_order(cpi_context, order_params, None, None)?; + } + } + + Ok(()) +} diff --git a/ts/sdk/src/jitProxyClient.ts b/ts/sdk/src/jitProxyClient.ts index c39c3c5..f66bd6d 100644 --- a/ts/sdk/src/jitProxyClient.ts +++ b/ts/sdk/src/jitProxyClient.ts @@ -2,6 +2,8 @@ import { BN, DriftClient, isVariant, + MakerInfo, + MarketType, PostOnlyParams, QUOTE_SPOT_MARKET_INDEX, TxParams, @@ -30,6 +32,18 @@ export class PriceType { static readonly ORACLE = { oracle: {} }; } +export type TakeIxParams = { + makerInfos: MakerInfo[]; + marketIndex: number; + marketType: MarketType; + maxPosition: BN; + minPosition: BN; + bid: BN; + ask: BN; + priceType?: PriceType; + fulfillmentMethod: null; +}; + export class JitProxyClient { private driftClient: DriftClient; private program: Program; @@ -113,4 +127,87 @@ export class JitProxyClient { .remainingAccounts(remainingAccounts) .instruction(); } + + public async take( + params: TakeIxParams, + txParams?: TxParams + ): Promise { + const ix = await this.getTakeIx(params); + const tx = await this.driftClient.buildTransaction([ix], txParams); + return await this.driftClient.sendTransaction(tx); + } + + public async getTakeIx({ + makerInfos, + marketIndex, + marketType, + maxPosition, + minPosition, + bid, + ask, + priceType = PriceType.LIMIT, + }: TakeIxParams): Promise { + const userAccounts = [this.driftClient.getUserAccount()]; + for (const makerInfo of makerInfos) { + userAccounts.push(makerInfo.makerUserAccount); + } + + const remainingAccounts = this.driftClient.getRemainingAccounts({ + userAccounts, + writableSpotMarketIndexes: isVariant(marketType, 'spot') + ? [marketIndex, QUOTE_SPOT_MARKET_INDEX] + : [], + writablePerpMarketIndexes: isVariant(marketType, 'perp') + ? [marketIndex] + : [], + }); + + for (const makerInfo of makerInfos) { + remainingAccounts.push({ + pubkey: makerInfo.maker, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: makerInfo.makerStats, + isWritable: true, + isSigner: false, + }); + } + + if (isVariant(marketType, 'spot')) { + remainingAccounts.push({ + pubkey: this.driftClient.getSpotMarketAccount(marketIndex).vault, + isWritable: false, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: this.driftClient.getQuoteSpotMarketAccount().vault, + isWritable: false, + isSigner: false, + }); + } + + const takeParams = { + marketIndex, + marketType, + maxPosition, + minPosition, + bid, + ask, + priceType, + fulfillmentMethod: null, + }; + + return this.program.methods + .take(takeParams) + .accounts({ + state: await this.driftClient.getStatePublicKey(), + user: await this.driftClient.getUserAccountPublicKey(), + userStats: this.driftClient.getUserStatsAccountPublicKey(), + driftProgram: this.driftClient.program.programId, + }) + .remainingAccounts(remainingAccounts) + .instruction(); + } } diff --git a/ts/sdk/src/types/jit_proxy.ts b/ts/sdk/src/types/jit_proxy.ts index c157c31..44665d7 100644 --- a/ts/sdk/src/types/jit_proxy.ts +++ b/ts/sdk/src/types/jit_proxy.ts @@ -49,6 +49,44 @@ export type JitProxy = { }; } ]; + }, + { + name: 'take'; + accounts: [ + { + name: 'state'; + isMut: false; + isSigner: false; + }, + { + name: 'user'; + isMut: true; + isSigner: false; + }, + { + name: 'userStats'; + isMut: true; + isSigner: false; + }, + { + name: 'authority'; + isMut: false; + isSigner: true; + }, + { + name: 'driftProgram'; + isMut: false; + isSigner: false; + } + ]; + args: [ + { + name: 'params'; + type: { + defined: 'TakeParams'; + }; + } + ]; } ]; types: [ @@ -94,6 +132,52 @@ export type JitProxy = { ]; }; }, + { + name: 'TakeParams'; + type: { + kind: 'struct'; + fields: [ + { + name: 'marketIndex'; + type: 'u16'; + }, + { + name: 'marketType'; + type: { + defined: 'MarketType'; + }; + }, + { + name: 'maxPosition'; + type: 'i64'; + }, + { + name: 'minPosition'; + type: 'i64'; + }, + { + name: 'bid'; + type: 'i64'; + }, + { + name: 'ask'; + type: 'i64'; + }, + { + name: 'priceType'; + type: { + defined: 'PriceType'; + }; + }, + { + name: 'fulfillmentMethod'; + type: { + option: 'u8'; + }; + } + ]; + }; + }, { name: 'PostOnlyParam'; type: { @@ -124,6 +208,20 @@ export type JitProxy = { } ]; }; + }, + { + name: 'MarketType'; + type: { + kind: 'enum'; + variants: [ + { + name: 'Perp'; + }, + { + name: 'Spot'; + } + ]; + }; } ]; errors: [ @@ -141,6 +239,11 @@ export type JitProxy = { code: 6002; name: 'TakerOrderNotFound'; msg: 'TakerOrderNotFound'; + }, + { + code: 6003; + name: 'DidNotCrossMakers'; + msg: 'DidNotCrossMakers'; } ]; }; @@ -197,6 +300,44 @@ export const IDL: JitProxy = { }, ], }, + { + name: 'take', + accounts: [ + { + name: 'state', + isMut: false, + isSigner: false, + }, + { + name: 'user', + isMut: true, + isSigner: false, + }, + { + name: 'userStats', + isMut: true, + isSigner: false, + }, + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'driftProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'params', + type: { + defined: 'TakeParams', + }, + }, + ], + }, ], types: [ { @@ -241,6 +382,52 @@ export const IDL: JitProxy = { ], }, }, + { + name: 'TakeParams', + type: { + kind: 'struct', + fields: [ + { + name: 'marketIndex', + type: 'u16', + }, + { + name: 'marketType', + type: { + defined: 'MarketType', + }, + }, + { + name: 'maxPosition', + type: 'i64', + }, + { + name: 'minPosition', + type: 'i64', + }, + { + name: 'bid', + type: 'i64', + }, + { + name: 'ask', + type: 'i64', + }, + { + name: 'priceType', + type: { + defined: 'PriceType', + }, + }, + { + name: 'fulfillmentMethod', + type: { + option: 'u8', + }, + }, + ], + }, + }, { name: 'PostOnlyParam', type: { @@ -272,6 +459,20 @@ export const IDL: JitProxy = { ], }, }, + { + name: 'MarketType', + type: { + kind: 'enum', + variants: [ + { + name: 'Perp', + }, + { + name: 'Spot', + }, + ], + }, + }, ], errors: [ { @@ -289,5 +490,10 @@ export const IDL: JitProxy = { name: 'TakerOrderNotFound', msg: 'TakerOrderNotFound', }, + { + code: 6003, + name: 'DidNotCrossMakers', + msg: 'DidNotCrossMakers', + }, ], }; From 50e900952459b5b62846f13d9494c3557a79a8fd Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Sun, 23 Jul 2023 10:52:01 -0400 Subject: [PATCH 2/2] arber init --- ts/sdk/src/arber.ts | 117 +++++++++++++++++++++++++++++++++++++++++++ ts/sdk/src/jitter.ts | 3 +- 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 ts/sdk/src/arber.ts diff --git a/ts/sdk/src/arber.ts b/ts/sdk/src/arber.ts new file mode 100644 index 0000000..35d8ba3 --- /dev/null +++ b/ts/sdk/src/arber.ts @@ -0,0 +1,117 @@ +import { + BN, + DLOBSource, + DriftClient, + getUserStatsAccountPublicKey, + isVariant, + MakerInfo, + MarketType, + SlotSource, + UserMap +} from "@drift-labs/sdk"; +import {JitProxyClient, PriceType} from "./jitProxyClient"; +import {JitParams} from "./jitter"; + +export type TakeParams = { + bid: BN; + ask: BN; + minPosition: BN; + maxPosition: BN; + priceType: PriceType; +}; + +export class Arber { + driftClient: DriftClient; + jitProxyClient: JitProxyClient; + slotSource: SlotSource; + userMap: UserMap; + frequency: number; + + perpParams = new Map(); + spotParams = new Map(); + + intervalId: NodeJS.Timeout | undefined; + + constructor({ + driftClient, + jitProxyClient, + slotSource, + userMap, + frequency = 1000, + }: { + driftClient: DriftClient; + jitProxyClient: JitProxyClient; + slotSource: SlotSource; + userMap: UserMap; + frequency?: number; + }) { + this.driftClient = driftClient; + this.slotSource = slotSource; + this.userMap = userMap; + this.frequency = frequency; + } + + public subscribe(): void { + if (this.intervalId) { + return; + } + + this.intervalId = setInterval(this.tryArb.bind(this), this.frequency); + } + + async tryArb() : Promise { + const slot = this.slotSource.getSlot(); + for (const [marketIndex, params] of this.perpParams) { + const dlob = await this.userMap.getDLOB(slot); + const oraclePriceData = this.driftClient.getOracleDataForPerpMarket(marketIndex); + const restingBids = dlob.getRestingLimitBids(marketIndex, slot, MarketType.PERP, oraclePriceData); + + const takerAsk = this.getPriceFromParams(params, 'ask', oraclePriceData.price); + for (const restingBid of restingBids) { + const makerBid = restingBid.getPrice(oraclePriceData, slot); + if (takerAsk.lte(makerBid)) { + const makerUser = this.userMap.get(restingBid.userAccount.toString()); + if (!makerUser) { + continue; + } + const makerInfo : MakerInfo = { + maker: restingBid.userAccount, + makerUserAccount: makerUser.getUserAccount(), + makerStats: getUserStatsAccountPublicKey(this.driftClient.program.programId, makerUser.getUserAccount().authority), + } + } else { + break + } + } + } + } + + public unsubscribe(): void { + if (this.intervalId) { + clearInterval(this.intervalId); + this.intervalId = undefined; + } + } + + public updatePerpParams(marketIndex: number, params: TakeParams): void { + this.perpParams.set(marketIndex, params); + } + + public updateSpotParams(marketIndex: number, params: TakeParams): void { + this.spotParams.set(marketIndex, params); + } + + getPriceFromParams(params: TakeParams, side: 'bid' | 'ask', oraclePrice: BN) : BN { + if (side === 'bid') { + if (isVariant(params.priceType, 'oracle')) { + return oraclePrice.add(params.bid); + } + return params.bid; + } else { + if (isVariant(params.priceType, 'oracle')) { + return oraclePrice.add(params.ask); + } + return params.ask; + } + } +} \ No newline at end of file diff --git a/ts/sdk/src/jitter.ts b/ts/sdk/src/jitter.ts index e21719b..285962b 100644 --- a/ts/sdk/src/jitter.ts +++ b/ts/sdk/src/jitter.ts @@ -16,11 +16,12 @@ export type UserFilter = ( userKey: string, order: Order ) => boolean; + export type JitParams = { bid: BN; ask: BN; minPosition: BN; - maxPosition; + maxPosition : BN; priceType: PriceType; };