Skip to content

Commit

Permalink
Merge branch 'master' into crispheaney/anchor-29
Browse files Browse the repository at this point in the history
  • Loading branch information
crispheaney committed Jun 10, 2024
2 parents d994bd5 + 15a7c68 commit 57a3a33
Show file tree
Hide file tree
Showing 27 changed files with 6,518 additions and 249 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/on-sdk-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '16.x'
node-version: '18.x'
registry-url: 'https://registry.npmjs.org'

- name: Determine sdk version
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Release
on:
release:
types: [published]
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/[email protected]

- name: Set up Python
uses: actions/[email protected]
with:
python-version: '3.10.10'

- name: Install and configure Poetry
run: |
cd python
curl -sSL https://install.python-poetry.org | python3 -
env:
POETRY_VERSION: 1.4.2

- name: Build
run: poetry build
working-directory: python

- name: Publish
run: poetry publish --username=__token__ --password=${{ secrets.PYPI_TOKEN }}
working-directory: python
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ python/poetry.lock
__pycache__/
*.pyc
*.pyo
*.pyd
*.pyd
rust/.env
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
members = [
"programs/*"
]
exclude = [
"rust"
]

[profile.release]
overflow-checks = true
Expand Down
2 changes: 2 additions & 0 deletions programs/jit-proxy/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ pub enum ErrorCode {
UnprofitableArb,
#[msg("PositionLimitBreached")]
PositionLimitBreached,
#[msg("NoFill")]
NoFill,
}
163 changes: 136 additions & 27 deletions programs/jit-proxy/src/instructions/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ pub fn jit<'c: 'info, 'info>(
let market_index = taker_order.market_index;
let taker_direction = taker_order.direction;

let slots_left = taker_order
.slot
.safe_add(taker_order.auction_duration.cast()?)?
.cast::<i64>()?
.safe_sub(slot.cast()?)?;
msg!(
"slot = {} auction duration = {} slots_left = {}",
slot,
taker_order.auction_duration,
slots_left
);

msg!(
"taker order type {:?} auction start {} auction end {} limit price {} oracle price offset {}",
taker_order.order_type,
taker_order.auction_start_price,
taker_order.auction_end_price,
taker_order.price,
taker_order.oracle_price_offset
);

let remaining_accounts_iter = &mut ctx.remaining_accounts.iter().peekable();
let AccountMaps {
perp_market_map,
Expand All @@ -45,20 +66,45 @@ pub fn jit<'c: 'info, 'info>(
None,
)?;

let (oracle_price, tick_size) = if market_type == DriftMarketType::Perp {
let (oracle_price, tick_size, min_order_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)
(
oracle_price,
perp_market.amm.order_tick_size,
perp_market.amm.min_order_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)
(
oracle_price,
spot_market.order_tick_size,
spot_market.min_order_size,
)
};

let taker_price =
taker_order.force_get_limit_price(Some(oracle_price), None, slot, tick_size)?;
match taker_order.get_limit_price(Some(oracle_price), None, slot, tick_size)? {
Some(price) => price,
None if market_type == DriftMarketType::Perp => {
msg!("taker order didnt have price. deriving fallback");
// if the order doesn't have a price, drift users amm price for taker price
let perp_market = perp_market_map.get_ref(&market_index)?;
let reserve_price = perp_market.amm.reserve_price()?;
match taker_direction {
PositionDirection::Long => perp_market.amm.ask_price(reserve_price)?,
PositionDirection::Short => perp_market.amm.bid_price(reserve_price)?,
}
}
None => {
// Shouldnt be possible for spot
msg!("taker order didnt have price");
return Err(ErrorCode::TakerOrderNotFound.into());
}
};

let maker_direction = taker_direction.opposite();
let maker_worst_price = params.get_worst_price(oracle_price, taker_direction)?;
Expand All @@ -84,9 +130,39 @@ pub fn jit<'c: 'info, 'info>(
}
}
}
let maker_price = taker_price;

let taker_base_asset_amount_unfilled = taker_order.get_base_asset_amount_unfilled(None)?;
let maker_price = if market_type == DriftMarketType::Perp {
let perp_market = perp_market_map.get_ref(&market_index)?;
let reserve_price = perp_market.amm.reserve_price()?;

match maker_direction {
PositionDirection::Long => {
let amm_bid_price = perp_market.amm.bid_price(reserve_price)?;

// if amm price is better than maker, use amm price to ensure fill
if taker_price <= amm_bid_price {
amm_bid_price.min(maker_worst_price)
} else {
taker_price
}
}
PositionDirection::Short => {
let amm_ask_price = perp_market.amm.ask_price(reserve_price)?;

if taker_price >= amm_ask_price {
amm_ask_price.max(maker_worst_price)
} else {
taker_price
}
}
}
} else {
taker_price
};

let taker_base_asset_amount_unfilled = taker_order
.get_base_asset_amount_unfilled(None)?
.max(min_order_size);
let maker_existing_position = if market_type == DriftMarketType::Perp {
let perp_market = perp_market_map.get_ref(&market_index)?;
let perp_position = maker.get_perp_position(market_index);
Expand All @@ -111,6 +187,7 @@ pub fn jit<'c: 'info, 'info>(
maker_direction,
taker_base_asset_amount_unfilled,
maker_existing_position,
min_order_size,
) {
Ok(size) => size,
Err(e) => {
Expand All @@ -129,7 +206,7 @@ pub fn jit<'c: 'info, 'info>(
reduce_only: false,
post_only: params
.post_only
.unwrap_or(PostOnlyParam::Slide)
.unwrap_or(PostOnlyParam::MustPostOnly)
.to_drift_param(),
immediate_or_cancel: true,
max_ts: None,
Expand All @@ -144,7 +221,36 @@ pub fn jit<'c: 'info, 'info>(
drop(taker);
drop(maker);

place_and_make(ctx, params.taker_order_id, order_params)?;
place_and_make(&ctx, params.taker_order_id, order_params)?;

let taker = ctx.accounts.taker.load()?;

let taker_base_asset_amount_unfilled_after = match taker.get_order(params.taker_order_id) {
Some(order) => order.get_base_asset_amount_unfilled(None)?,
None => 0,
};

if taker_base_asset_amount_unfilled_after == taker_base_asset_amount_unfilled {
// taker order failed to fill
msg!(
"taker price = {} maker price = {} oracle price = {}",
taker_price,
maker_price,
oracle_price
);
msg!("jit params {:?}", params);
if market_type == DriftMarketType::Perp {
let perp_market = perp_market_map.get_ref(&market_index)?;
let reserve_price = perp_market.amm.reserve_price()?;
let (bid_price, ask_price) = perp_market.amm.bid_ask_price(reserve_price)?;
msg!(
"vamm bid price = {} vamm ask price = {}",
bid_price,
ask_price
);
}
return Err(ErrorCode::NoFill.into());
}

Ok(())
}
Expand Down Expand Up @@ -213,15 +319,17 @@ fn check_position_limits(
maker_direction: PositionDirection,
taker_base_asset_amount_unfilled: u64,
maker_existing_position: i64,
min_order_size: u64,
) -> Result<u64> {
if maker_direction == PositionDirection::Long {
let size = params.max_position.safe_sub(maker_existing_position)?;

if size <= 0 {
if size <= min_order_size.cast()? {
msg!(
"maker existing position {} >= max position {}",
"maker existing position {} >= max position {} + min order size {}",
maker_existing_position,
params.max_position
params.max_position,
min_order_size
);
return Err(ErrorCode::PositionLimitBreached.into());
}
Expand All @@ -230,11 +338,12 @@ fn check_position_limits(
} else {
let size = maker_existing_position.safe_sub(params.min_position)?;

if size <= 0 {
if size <= min_order_size.cast()? {
msg!(
"maker existing position {} <= min position {}",
"maker existing position {} <= min position {} + min order size {}",
maker_existing_position,
params.min_position
params.min_position,
min_order_size
);
return Err(ErrorCode::PositionLimitBreached.into());
}
Expand All @@ -256,55 +365,55 @@ mod tests {
};

// same direction, doesn't breach
let result = check_position_limits(params, PositionDirection::Long, 10, 40);
let result = check_position_limits(params, PositionDirection::Long, 10, 40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 10);
let result = check_position_limits(params, PositionDirection::Short, 10, -40);
let result = check_position_limits(params, PositionDirection::Short, 10, -40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 10);

// same direction, whole order breaches, only takes enough to hit limit
let result = check_position_limits(params, PositionDirection::Long, 100, 40);
let result = check_position_limits(params, PositionDirection::Long, 100, 40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 60);
let result = check_position_limits(params, PositionDirection::Short, 100, -40);
let result = check_position_limits(params, PositionDirection::Short, 100, -40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 60);

// opposite direction, doesn't breach
let result = check_position_limits(params, PositionDirection::Long, 10, -40);
let result = check_position_limits(params, PositionDirection::Long, 10, -40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 10);
let result = check_position_limits(params, PositionDirection::Short, 10, 40);
let result = check_position_limits(params, PositionDirection::Short, 10, 40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 10);

// opposite direction, whole order breaches, only takes enough to take flipped limit
let result = check_position_limits(params, PositionDirection::Long, 200, -40);
let result = check_position_limits(params, PositionDirection::Long, 200, -40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 140);
let result = check_position_limits(params, PositionDirection::Short, 200, 40);
let result = check_position_limits(params, PositionDirection::Short, 200, 40, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 140);

// opposite direction, maker already breached, allows reducing
let result = check_position_limits(params, PositionDirection::Long, 200, -150);
let result = check_position_limits(params, PositionDirection::Long, 200, -150, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 200);
let result = check_position_limits(params, PositionDirection::Short, 200, 150);
let result = check_position_limits(params, PositionDirection::Short, 200, 150, 0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 200);

// same direction, maker already breached, errors
let result = check_position_limits(params, PositionDirection::Long, 200, 150);
let result = check_position_limits(params, PositionDirection::Long, 200, 150, 0);
assert!(result.is_err());
let result = check_position_limits(params, PositionDirection::Short, 200, -150);
let result = check_position_limits(params, PositionDirection::Short, 200, -150, 0);
assert!(result.is_err());
}
}

fn place_and_make<'info>(
ctx: Context<'_, '_, '_, 'info, Jit<'info>>,
ctx: &Context<'_, '_, '_, 'info, Jit<'info>>,
taker_order_id: u32,
order_params: OrderParams,
) -> Result<()> {
Expand Down
12 changes: 6 additions & 6 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "jit_proxy"
version = "0.1.0"
name = "drift_jit_proxy"
version = "0.1.5"
description = "python sdk for drift protocol v2 jit proxy program"
authors = ["Frank <[email protected]>"]
readme = "README.md"
Expand All @@ -11,12 +11,12 @@ packages = [
[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.0.0"
solana = "^0.30.1"
anchorpy = "^0.17.1"
driftpy = "0.7.17"
solana = "^0.34.0"
anchorpy = "^0.20.1"
driftpy = "^0.7.52"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

#
#
2 changes: 1 addition & 1 deletion python/sdk/jit_proxy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.0"
__version__ = "0.1.1"
Loading

0 comments on commit 57a3a33

Please sign in to comment.