1use crate::common::order_status::OrderStatus;
2use crate::common::order_type::OrderType;
3use crate::common::side::Side;
4use crate::common::tif::TimeInForce;
5use crate::transaction::ActionMeta;
6use serde::{Deserialize, Serialize};
7use solana_pubkey::Pubkey;
8use std::collections::HashMap;
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
15pub struct Faucet {
16 #[serde(with = "crate::msgs::serde_pubkey", rename = "u")]
17 pub user: Pubkey,
18 pub amount: Option<f64>,
19
20 #[serde(skip)]
21 pub meta: ActionMeta,
22}
23
24#[derive(Clone, Debug, Serialize, Deserialize)]
29pub struct WhitelistFaucet {
30 #[serde(with = "crate::msgs::serde_pubkey")]
31 pub target: Pubkey,
32 pub whitelist: bool,
33
34 #[serde(skip)]
35 pub meta: ActionMeta,
36}
37
38#[derive(Clone, Debug, Serialize, Deserialize)]
43pub struct AgentWalletCreation {
44 #[serde(with = "crate::msgs::serde_pubkey", rename = "a")]
45 pub agent: Pubkey,
46 #[serde(rename = "d")]
47 pub delete: bool,
48
49 #[serde(skip)]
50 pub meta: ActionMeta,
51}
52
53#[derive(Clone, Debug, Serialize, Deserialize)]
54pub struct ApproveCommissionFee {
55 #[serde(with = "crate::msgs::serde_pubkey", rename = "to")]
56 pub to: Pubkey,
57 #[serde(rename = "fee")]
58 pub max_fee: u8,
59
60 #[serde(skip)]
61 pub meta: ActionMeta,
62}
63
64pub type ApproveBuilderCode = ApproveCommissionFee;
65
66#[derive(Clone, Debug, Serialize, Deserialize)]
67pub struct RevokeCommissionFee {
68 #[serde(with = "crate::msgs::serde_pubkey", rename = "to")]
69 pub to: Pubkey,
70
71 #[serde(skip)]
72 pub meta: ActionMeta,
73}
74
75pub type RevokeBuilderCode = RevokeCommissionFee;
76
77#[derive(Clone, Debug, Serialize, Deserialize)]
82pub struct UpdateUserSettings {
83 #[serde(rename = "m")]
84 pub max_leverage: HashMap<String, f64>,
85
86 #[serde(skip)]
87 pub meta: ActionMeta,
88}
89
90#[derive(Debug, Clone, Default, Deserialize)]
96#[allow(unused)]
97pub struct Margin {
98 #[serde(rename = "totalBalance")]
99 pub total_balance: f64,
100 #[serde(rename = "availableBalance")]
101 pub available_balance: f64,
102 #[serde(rename = "marginUsed")]
103 pub margin_used: f64,
104 pub notional: f64,
105 #[serde(rename = "realizedPnl")]
106 pub realized_pnl: f64,
107 #[serde(rename = "unrealizedPnl")]
108 pub unrealized_pnl: f64,
109 pub fees: f64,
110 pub funding: f64,
111}
112
113#[derive(Debug, Clone, Deserialize)]
123#[allow(unused)]
124pub struct PositionInfo {
125 #[serde(alias = "coin")]
126 pub symbol: String,
127 pub size: f64,
128 pub price: f64,
129 #[serde(rename = "fairPrice", default)]
130 pub fair_price: f64,
131 #[serde(default)]
132 pub notional: f64,
133 #[serde(rename = "realizedPnl", default)]
134 pub realized_pnl: f64,
135 #[serde(rename = "unrealizedPnl", default)]
136 pub unrealized_pnl: f64,
137 #[serde(default)]
138 pub leverage: f64,
139 #[serde(rename = "liquidationPrice", default)]
140 pub liquidation_price: f64,
141 #[serde(rename = "maintenanceMargin", default)]
142 pub maintenance_margin: f64,
143}
144
145#[derive(Debug, Serialize, Deserialize, Clone)]
150#[serde(rename_all = "camelCase")]
151pub struct TriggerSpec {
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub is_above: Option<bool>,
154 pub px: f64,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub lim: Option<f64>,
157 pub oco: Option<String>,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub px_hi: Option<f64>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub lim_hi: Option<f64>,
162 #[serde(rename = "trb", skip_serializing_if = "Option::is_none")]
163 pub trail_bps: Option<u32>,
164 #[serde(rename = "stb", skip_serializing_if = "Option::is_none")]
165 pub step_bps: Option<u32>,
166}
167
168#[derive(Debug, Clone, Deserialize)]
174#[allow(unused)]
175pub struct OrderState {
176 #[serde(rename = "ot")]
177 pub order_type: OrderType,
178 pub status: OrderStatus,
179 #[serde(rename = "sym")]
180 pub symbol: String,
181 #[serde(rename = "oid")]
182 pub order_id: String,
183 #[serde(rename = "px")]
184 pub price: f64,
185 #[serde(rename = "origSz")]
186 pub original_size: f64,
187 #[serde(rename = "sz")]
188 pub signed_size: f64,
189 #[serde(rename = "fillSz")]
190 pub filled_size: f64,
191 pub vwap: f64,
192 pub tif: TimeInForce,
193 #[serde(rename = "r")]
194 pub reduce_only: bool,
195 #[serde(rename = "mk")]
196 pub maker: bool,
197 #[serde(default)]
198 pub trigger: Option<TriggerSpec>,
199 #[serde(rename = "ts")]
200 pub timestamp: u64,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub reason: Option<String>,
203}
204
205impl OrderState {
206 pub fn side(&self) -> Side {
208 if self.signed_size < 0.0 {
209 Side::Sell
210 } else {
211 Side::Buy
212 }
213 }
214
215 pub fn amount(&self) -> f64 {
217 self.signed_size.abs()
218 }
219}
220
221#[derive(Debug, Clone, Deserialize)]
226#[allow(unused)]
227pub struct Fill {
228 pub timestamp: u64,
229 #[serde(alias = "coin")]
230 pub symbol: String,
231 #[serde(rename = "orderId")]
232 pub order_id: String,
233 pub price: f64,
234 pub size: f64,
235 #[serde(rename = "isBuy")]
236 pub side: Side,
237 #[serde(rename = "maker", default)]
238 pub is_maker: bool,
239 #[serde(rename = "counterpartyHint", default)]
240 pub cpty: String,
241 pub reason: Option<String>,
242}
243
244#[derive(Debug, Clone, Deserialize)]
249#[allow(unused)]
250pub struct LeverageSetting {
251 #[serde(alias = "coin")]
252 pub symbol: String,
253 pub leverage: f64,
254}
255
256#[derive(Debug, Clone, Deserialize)]
257#[serde(rename_all = "camelCase")]
258#[allow(unused)]
259pub struct CommissionApproval {
260 #[serde(with = "crate::msgs::serde_pubkey")]
261 pub recipient: Pubkey,
262 pub max_fee: u8,
263}
264
265#[derive(Debug, Clone, Deserialize)]
272#[serde(rename_all = "camelCase")]
273#[allow(unused)]
274pub struct AccountData {
275 pub positions: Vec<PositionInfo>,
276 pub open_orders: Vec<OrderState>,
277 pub margin: Margin,
278 pub leverage_settings: Vec<LeverageSetting>,
279 #[serde(default, rename = "builderCodeApprovals")]
280 pub commission_approvals: Option<Vec<CommissionApproval>>,
281}
282
283#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_order_state_rejected_risk_limit() {
293 let json = r#"{
294 "ts": 1770918312787284000,
295 "ot": "limit",
296 "status": "rejectedRiskLimit",
297 "sym": "BTC-USD",
298 "oid": "EF2bxQ5pp3CDFAwRi44ExXb32sRmByByYxjwLYBfvRKQ",
299 "px": 100001.37,
300 "origSz": -0.02474,
301 "sz": -0.02474,
302 "fillSz": 0.0,
303 "vwap": 0.0,
304 "mk": true,
305 "r": false,
306 "tif": "gtc",
307 "reason": "no oracle / fair price reference yet for: BTC-USD"
308 }"#;
309
310 let order: OrderState = serde_json::from_str(json).unwrap();
311
312 assert_eq!(order.symbol, "BTC-USD");
313 assert_eq!(
314 order.order_id,
315 "EF2bxQ5pp3CDFAwRi44ExXb32sRmByByYxjwLYBfvRKQ"
316 );
317 assert_eq!(order.status, OrderStatus::RejectedRiskLimit);
318 assert!(order.signed_size < 0.0);
319 assert!((order.price - 100001.37).abs() < 1e-6);
320 assert!((order.original_size.abs() - 0.02474).abs() < 1e-8);
321 assert!((order.signed_size.abs() - 0.02474).abs() < 1e-8);
322 assert_eq!(order.filled_size, 0.0);
323 assert!(order.maker);
324 assert_eq!(order.timestamp, 1770918312787284000);
325 assert_eq!(
326 order.reason.as_deref(),
327 Some("no oracle / fair price reference yet for: BTC-USD")
328 );
329
330 assert!(order.status.is_terminal());
332 assert!(order.status.is_rejected());
333 }
334}