hypercall_api/directives/
models.rs1use crate::directives::json_types::{Bytes32Hex, JsonU128, JsonU32, JsonU64, JsonU8};
2use crate::error::ApiError;
3use alloy::sol_types::SolValue;
4use hypercall_types::directives::{
5 ActionKey, CancelOrderByCloid, CancelOrderByOid, LimitOrder, UpdateApiWallet,
6};
7use hypercall_types::WalletAddress;
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Deserialize)]
11#[serde(deny_unknown_fields)]
12pub struct TypedDataRequest {
13 pub account: WalletAddress,
14 pub nonce: JsonU64,
15 pub action: sonic_rs::Value,
16}
17
18#[derive(Debug, Clone, Deserialize)]
19#[serde(deny_unknown_fields)]
20pub struct SubmitRequest {
21 pub account: WalletAddress,
22 pub nonce: JsonU64,
23 pub action: sonic_rs::Value,
24 pub signature: String,
25}
26
27#[derive(Debug, Clone, Deserialize, Serialize)]
28#[serde(deny_unknown_fields, rename_all = "camelCase")]
29pub struct HlLimitOrderAction {
30 pub asset: JsonU32,
31 pub is_buy: bool,
32 pub limit_px: JsonU64,
33 pub sz: JsonU64,
34 pub reduce_only: bool,
35 pub encoded_tif: JsonU8,
36 pub cloid: JsonU128,
37}
38
39#[derive(Debug, Clone, Deserialize, Serialize)]
40#[serde(deny_unknown_fields, rename_all = "camelCase")]
41pub struct HlCancelByOidAction {
42 pub asset: JsonU32,
43 pub oid: JsonU64,
44}
45
46#[derive(Debug, Clone, Deserialize, Serialize)]
47#[serde(deny_unknown_fields, rename_all = "camelCase")]
48pub struct HlCancelByCloidAction {
49 pub asset: JsonU32,
50 pub cloid: JsonU128,
51}
52
53#[derive(Debug, Clone, Deserialize, Serialize)]
54#[serde(deny_unknown_fields, rename_all = "camelCase")]
55pub struct HcUpdateApiWalletAction {
56 pub name: Bytes32Hex,
57 pub addr: WalletAddress,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum EncodedTif {
62 Alo,
63 Gtc,
64 Ioc,
65}
66
67impl EncodedTif {
68 pub fn parse(value: u8) -> Result<Self, ApiError> {
69 match value {
70 1 => Ok(Self::Alo),
71 2 => Ok(Self::Gtc),
72 3 => Ok(Self::Ioc),
73 _ => Err(ApiError::invalid_field(
74 "action.encodedTif must be one of 1, 2, or 3",
75 )),
76 }
77 }
78}
79
80impl HlLimitOrderAction {
81 pub fn encoded_tif_kind(&self) -> Result<EncodedTif, ApiError> {
82 EncodedTif::parse(self.encoded_tif.into_inner())
83 }
84
85 pub fn validate(&self) -> Result<(), ApiError> {
86 self.encoded_tif_kind().map(|_| ())
87 }
88}
89
90impl From<&HlLimitOrderAction> for LimitOrder {
91 fn from(value: &HlLimitOrderAction) -> Self {
92 Self {
93 asset: value.asset.into_inner(),
94 isBuy: value.is_buy,
95 limitPx: value.limit_px.into_inner(),
96 sz: value.sz.into_inner(),
97 reduceOnly: value.reduce_only,
98 encodedTif: value.encoded_tif.into_inner(),
99 cloid: value.cloid.into_inner(),
100 }
101 }
102}
103
104impl From<&HlCancelByOidAction> for CancelOrderByOid {
105 fn from(value: &HlCancelByOidAction) -> Self {
106 Self {
107 asset: value.asset.into_inner(),
108 oid: value.oid.into_inner(),
109 }
110 }
111}
112
113impl From<&HlCancelByCloidAction> for CancelOrderByCloid {
114 fn from(value: &HlCancelByCloidAction) -> Self {
115 Self {
116 asset: value.asset.into_inner(),
117 cloid: value.cloid.into_inner(),
118 }
119 }
120}
121
122impl From<&HcUpdateApiWalletAction> for UpdateApiWallet {
123 fn from(value: &HcUpdateApiWalletAction) -> Self {
124 Self {
125 name: value.name.into_inner().into(),
126 addr: value.addr.inner(),
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
132pub enum ParsedAction {
133 HlLimitOrder(HlLimitOrderAction),
134 HlCancelByOid(HlCancelByOidAction),
135 HlCancelByCloid(HlCancelByCloidAction),
136 HcUpdateApiWallet(HcUpdateApiWalletAction),
137}
138
139impl ParsedAction {
140 pub fn parse(action_key: ActionKey, value: sonic_rs::Value) -> Result<Self, ApiError> {
141 match action_key {
142 ActionKey::HlLimitOrder => {
143 let action: HlLimitOrderAction = sonic_rs::from_value(&value)
144 .map_err(|e| ApiError::invalid_field(e.to_string()))?;
145 action.validate()?;
146 Ok(Self::HlLimitOrder(action))
147 }
148 ActionKey::HlCancelByOid => {
149 let action: HlCancelByOidAction = sonic_rs::from_value(&value)
150 .map_err(|e| ApiError::invalid_field(e.to_string()))?;
151 Ok(Self::HlCancelByOid(action))
152 }
153 ActionKey::HlCancelByCloid => {
154 let action: HlCancelByCloidAction = sonic_rs::from_value(&value)
155 .map_err(|e| ApiError::invalid_field(e.to_string()))?;
156 Ok(Self::HlCancelByCloid(action))
157 }
158 ActionKey::HcUpdateApiWallet => {
159 let action: HcUpdateApiWalletAction = sonic_rs::from_value(&value)
160 .map_err(|e| ApiError::invalid_field(e.to_string()))?;
161 Ok(Self::HcUpdateApiWallet(action))
162 }
163 _ => Err(ApiError::unsupported_action(format!(
164 "Action '{}' is not supported in MVP",
165 action_key.as_str()
166 ))),
167 }
168 }
169
170 pub fn to_json_value(&self) -> sonic_rs::Value {
171 let value = match self {
172 Self::HlLimitOrder(action) => sonic_rs::to_value(action),
173 Self::HlCancelByOid(action) => sonic_rs::to_value(action),
174 Self::HlCancelByCloid(action) => sonic_rs::to_value(action),
175 Self::HcUpdateApiWallet(action) => sonic_rs::to_value(action),
176 };
177 value.expect("action serialization should always succeed")
178 }
179
180 pub fn encode_inner_abi(&self) -> Vec<u8> {
181 match self {
182 Self::HlLimitOrder(action) => LimitOrder::from(action).abi_encode(),
183 Self::HlCancelByOid(action) => CancelOrderByOid::from(action).abi_encode(),
184 Self::HlCancelByCloid(action) => CancelOrderByCloid::from(action).abi_encode(),
185 Self::HcUpdateApiWallet(action) => UpdateApiWallet::from(action).abi_encode(),
186 }
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
192#[serde(rename_all = "snake_case")]
193pub enum DirectiveStage {
194 Rejected,
196 Enqueued,
198 Submitted,
200}
201
202#[derive(Debug, Clone, Serialize)]
204#[serde(rename_all = "camelCase")]
205pub struct DirectiveFill {
206 pub coin: String,
207 pub side: String,
208 pub size: String,
209 pub price: String,
210 pub time: u64,
211}
212
213#[derive(Debug, Clone, Serialize)]
216#[serde(rename_all = "camelCase")]
217pub struct SubmitResponse {
218 pub stage: DirectiveStage,
220 pub directive_id: String,
222 pub action_key: String,
224 pub account: WalletAddress,
226 pub nonce: JsonU64,
228 pub recovered_signer: Option<WalletAddress>,
230 pub tx_hash: Option<String>,
232 pub rejection: Option<DirectiveRejection>,
234 pub fills: Option<Vec<DirectiveFill>>,
236}
237
238#[derive(Debug, Clone, Serialize)]
239#[serde(rename_all = "camelCase")]
240pub struct DirectiveRejection {
241 pub code: String,
242 pub message: String,
243}