Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 25 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
[workspace]
members = [".", "crates/drift-gateway-types"]
resolver = "2"

[workspace.dependencies]
drift-rs = { git = "https://github.com/drift-labs/drift-rs", rev = "6eeebd8" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
rust_decimal = "1"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
thiserror = "2"
nanoid = "0.4"
faster-hex = "0.10"

[package]
name = "drift-gateway"
version = "1.5.4"
Expand All @@ -6,25 +21,26 @@ edition = "2021"
[dependencies]
actix-web = "*"
argh = "*"
drift-rs = { git = "https://github.com/drift-labs/drift-rs", rev = "6eeebd8" }
drift-rs = { workspace = true }
base64 = "0.22.1"
env_logger = "*"
faster-hex = "0.10.0"
faster-hex = { workspace = true }
futures-util = "*"
log = "*"
nanoid = "0.4.0"
reqwest = { version = "*", features = ["json"] }
rust_decimal = "*"
serde = { version = "*", features = ["derive"] }
serde_json = "*"
nanoid = { workspace = true }
reqwest = { workspace = true }
rust_decimal = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sha256 = "1.6.0"
solana-account-decoder-client-types = "2"
solana-rpc-client-api = "2"
solana-sdk = "2"
solana-transaction-status = "2"
thiserror = "*"
tokio = {version ="*", features = ["full"]}
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-tungstenite = "*"
drift-gateway-types = { path = "crates/drift-gateway-types" }

[profile.release]
panic = 'abort'
19 changes: 19 additions & 0 deletions crates/drift-gateway-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "drift-gateway-types"
version = "0.1.0"
edition = "2021"
description = "Shared types for Drift Gateway API"
license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/drift-labs/gateway"
homepage = "https://drift.trade"
categories = ["cryptography::cryptocurrencies", "api-bindings"]
keywords = ["solana", "dex", "drift", "sdk"]

[dependencies]
drift-rs = { workspace = true }
faster-hex = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
rust_decimal = { workspace = true }
nanoid = { workspace = true }
294 changes: 294 additions & 0 deletions crates/drift-gateway-types/src/account_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
use drift_rs::types::{MarketType, Order, OrderType, PositionDirection};
use rust_decimal::Decimal;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::PRICE_DECIMALS;

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub enum AccountEvent {
Fill(FillEvent),
Trigger { order_id: u32, oracle_price: u64 },
OrderCreate(OrderCreateEvent),
OrderCancel(OrderCancelEvent),
OrderCancelMissing(OrderCancelMissingEvent),
OrderExpire(OrderExpireEvent),
FundingPayment(FundingPaymentEvent),
Swap(SwapEvent),
}

#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct FillEvent {
pub side: Side,
pub fee: Decimal,
pub amount: Decimal,
pub price: Decimal,
pub oracle_price: Decimal,
pub order_id: u32,
pub market_index: u16,
#[serde(
serialize_with = "crate::types::ser_market_type",
deserialize_with = "crate::types::de_market_type"
)]
pub market_type: MarketType,
pub ts: u64,
pub tx_idx: usize,
pub signature: String,
pub maker: Option<String>,
pub maker_order_id: Option<u32>,
pub maker_fee: Option<Decimal>,
pub taker: Option<String>,
pub taker_order_id: Option<u32>,
pub taker_fee: Option<Decimal>,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderCreateEvent {
pub order: OrderWithDecimals,
pub ts: u64,
pub signature: String,
pub tx_idx: usize,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderCancelEvent {
pub order_id: u32,
pub ts: u64,
pub signature: String,
pub tx_idx: usize,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderCancelMissingEvent {
pub user_order_id: u8,
pub order_id: u32,
pub signature: String,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderExpireEvent {
pub order_id: u32,
pub fee: Decimal,
pub ts: u64,
pub signature: String,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct FundingPaymentEvent {
pub amount: Decimal,
pub market_index: u16,
pub ts: u64,
pub signature: String,
pub tx_idx: usize,
}

impl AccountEvent {
pub fn fill(
side: PositionDirection,
fee: i64,
base_amount: u64,
quote_amount: u64,
oracle_price: i64,
order_id: u32,
ts: u64,
decimals: u32,
signature: &String,
tx_idx: usize,
market_index: u16,
market_type: MarketType,
maker: Option<String>,
maker_order_id: Option<u32>,
maker_fee: Option<i64>,
taker: Option<String>,
taker_order_id: Option<u32>,
taker_fee: Option<i64>,
) -> Self {
let base_amount = Decimal::new(base_amount as i64, decimals);
let price = Decimal::new(quote_amount as i64, PRICE_DECIMALS) / base_amount;
let f = FillEvent {
side: if let PositionDirection::Long = side {
Side::Buy
} else {
Side::Sell
},
price: price.normalize(),
oracle_price: Decimal::new(oracle_price, PRICE_DECIMALS).normalize(),
fee: Decimal::new(fee, PRICE_DECIMALS).normalize(),
order_id,
amount: base_amount.normalize(),
ts,
signature: signature.to_string(),
market_index,
market_type,
tx_idx,
maker,
maker_order_id,
maker_fee: maker_fee.map(|x| Decimal::new(x, PRICE_DECIMALS)),
taker,
taker_order_id,
taker_fee: taker_fee.map(|x| Decimal::new(x, PRICE_DECIMALS)),
};
Self::Fill(f)
}
}

#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub enum Side {
#[default]
Buy,
Sell,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderWithDecimals {
/// The slot the order was placed
pub slot: u64,
/// The limit price for the order (can be 0 for market orders)
/// For orders with an auction, this price isn't used until the auction is complete
pub price: Decimal,
/// The size of the order
pub amount: Decimal,
/// The amount of the order filled
pub filled: Decimal,
/// At what price the order will be triggered. Only relevant for trigger orders
pub trigger_price: Decimal,
/// The start price for the auction. Only relevant for market/oracle orders
pub auction_start_price: Decimal,
/// The end price for the auction. Only relevant for market/oracle orders
pub auction_end_price: Decimal,
/// The time when the order will expire
pub max_ts: i64,
/// If set, the order limit price is the oracle price + this offset
pub oracle_price_offset: Decimal,
/// The id for the order. Each users has their own order id space
pub order_id: u32,
/// The perp/spot market index
pub market_index: u16,
/// The type of order
#[serde(serialize_with = "ser_order_type", deserialize_with = "de_order_type")]
pub order_type: OrderType,
/// Whether market is spot or perp
#[serde(
serialize_with = "crate::types::ser_market_type",
deserialize_with = "crate::types::de_market_type"
)]
pub market_type: MarketType,
/// User generated order id. Can make it easier to place/cancel orders
pub user_order_id: u8,
#[serde(
serialize_with = "ser_position_direction",
deserialize_with = "de_position_direction"
)]
pub direction: PositionDirection,
/// Whether the order is allowed to only reduce position size
pub reduce_only: bool,
/// Whether the order must be a maker
pub post_only: bool,
/// Whether the order must be canceled the same slot it is placed
pub immediate_or_cancel: bool,
/// How many slots the auction lasts
pub auction_duration: u8,
}

impl OrderWithDecimals {
pub fn from_order(value: Order, decimals: u32) -> Self {
Self {
slot: value.slot,
price: Decimal::new(value.price as i64, PRICE_DECIMALS).normalize(),
amount: Decimal::new(value.base_asset_amount as i64, decimals).normalize(),
filled: Decimal::new(value.base_asset_amount_filled as i64, decimals).normalize(),
trigger_price: Decimal::new(value.trigger_price as i64, PRICE_DECIMALS).normalize(),
auction_start_price: Decimal::new(value.auction_start_price, PRICE_DECIMALS)
.normalize(),
auction_end_price: Decimal::new(value.auction_end_price, PRICE_DECIMALS).normalize(),
oracle_price_offset: Decimal::new(value.oracle_price_offset as i64, PRICE_DECIMALS)
.normalize(),
max_ts: value.max_ts,
order_id: value.order_id,
market_index: value.market_index,
order_type: value.order_type,
market_type: value.market_type,
user_order_id: value.user_order_id,
direction: value.direction,
reduce_only: value.reduce_only,
post_only: value.post_only,
immediate_or_cancel: value.immediate_or_cancel,
auction_duration: value.auction_duration,
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SwapEvent {
pub amount_in: Decimal,
pub amount_out: Decimal,
pub market_in: u16,
pub market_out: u16,
pub ts: u64,
pub tx_idx: usize,
pub signature: String,
}

fn ser_order_type<S>(x: &OrderType, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(match x {
OrderType::Limit => "limit",
OrderType::Market => "market",
OrderType::Oracle => "oracle",
OrderType::TriggerLimit => "triggerLimit",
OrderType::TriggerMarket => "triggerMarket",
})
}

fn ser_position_direction<S>(x: &PositionDirection, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(match x {
PositionDirection::Long => "buy",
PositionDirection::Short => "sell",
})
}

fn de_position_direction<'de, D>(deserializer: D) -> Result<PositionDirection, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"buy" => Ok(PositionDirection::Long),
"sell" => Ok(PositionDirection::Short),
_ => Err(serde::de::Error::custom(format!(
"unknown position direction: {}",
s
))),
}
}

fn de_order_type<'de, D>(deserializer: D) -> Result<OrderType, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"limit" => Ok(OrderType::Limit),
"market" => Ok(OrderType::Market),
"oracle" => Ok(OrderType::Oracle),
"triggerLimit" => Ok(OrderType::TriggerLimit),
"triggerMarket" => Ok(OrderType::TriggerMarket),
_ => Err(serde::de::Error::custom(format!(
"unknown order type: {}",
s
))),
}
}
Loading
Loading