levana_perpswap_cosmos/contracts/market/
order.rs1use crate::prelude::*;
3use cosmwasm_std::{Addr, StdResult};
4use cw_storage_plus::{IntKey, Key, KeyDeserialize, Prefixer, PrimaryKey};
5use std::fmt;
6use std::hash::Hash;
7use std::num::ParseIntError;
8
9#[cw_serde]
11pub struct LimitOrder {
12 pub order_id: OrderId,
14 pub owner: Addr,
16 pub trigger_price: PriceBaseInQuote,
18 pub collateral: NonZero<Collateral>,
20 pub leverage: LeverageToBase,
22 pub direction: DirectionToNotional,
24 #[deprecated(note = "Use take_profit instead")]
26 pub max_gains: Option<MaxGainsInQuote>,
27 pub stop_loss_override: Option<PriceBaseInQuote>,
29 #[serde(alias = "take_profit_override")]
33 pub take_profit: Option<TakeProfitTrader>,
34 #[serde(default)]
36 pub crank_fee_collateral: Collateral,
37 #[serde(default)]
39 pub crank_fee_usd: Usd,
40}
41
42#[cw_serde]
44#[derive(Copy, PartialOrd, Ord, Eq)]
45pub struct OrderId(Uint64);
46
47#[cfg(feature = "arbitrary")]
48impl<'a> arbitrary::Arbitrary<'a> for OrderId {
49 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
50 u64::arbitrary(u).map(|x| OrderId(Uint64::new(x)))
51 }
52}
53
54#[allow(clippy::derived_hash_with_manual_eq)]
55impl Hash for OrderId {
56 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
57 self.u64().hash(state);
58 }
59}
60
61impl OrderId {
62 pub fn new(x: u64) -> Self {
64 OrderId(x.into())
65 }
66
67 pub fn u64(self) -> u64 {
69 self.0.u64()
70 }
71}
72
73impl<'a> PrimaryKey<'a> for OrderId {
74 type Prefix = ();
75 type SubPrefix = ();
76 type Suffix = Self;
77 type SuperSuffix = Self;
78
79 fn key(&self) -> Vec<Key> {
80 vec![Key::Val64(self.0.u64().to_cw_bytes())]
81 }
82}
83
84impl<'a> Prefixer<'a> for OrderId {
85 fn prefix(&self) -> Vec<Key> {
86 vec![Key::Val64(self.0.u64().to_cw_bytes())]
87 }
88}
89
90impl KeyDeserialize for OrderId {
91 type Output = OrderId;
92
93 const KEY_ELEMS: u16 = 1;
94
95 #[inline(always)]
96 fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
97 u64::from_vec(value).map(|x| OrderId(Uint64::new(x)))
98 }
99}
100
101impl fmt::Display for OrderId {
102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 write!(f, "{}", self.0)
104 }
105}
106
107impl FromStr for OrderId {
108 type Err = ParseIntError;
109 fn from_str(src: &str) -> Result<Self, ParseIntError> {
110 src.parse().map(|x| OrderId(Uint64::new(x)))
111 }
112}
113
114pub mod events {
116 use crate::constants::{event_key, event_val};
117 use crate::contracts::market::order::OrderId;
118 use crate::contracts::market::position::PositionId;
119 use crate::prelude::MarketType::{CollateralIsBase, CollateralIsQuote};
120 use crate::prelude::*;
121
122 pub struct PlaceLimitOrderEvent {
124 pub order_id: OrderId,
126 pub owner: Addr,
128 pub trigger_price: PriceBaseInQuote,
130 pub market_type: MarketType,
132 pub collateral: NonZero<Collateral>,
134 pub collateral_usd: NonZero<Usd>,
136 pub leverage: SignedLeverageToBase,
138 pub direction: DirectionToBase,
140 #[deprecated(note = "Use take_profit_override instead")]
142 pub max_gains: Option<MaxGainsInQuote>,
143 pub stop_loss_override: Option<PriceBaseInQuote>,
145 pub take_profit_override: Option<TakeProfitTrader>,
147 }
148
149 impl From<PlaceLimitOrderEvent> for Event {
150 fn from(src: PlaceLimitOrderEvent) -> Self {
151 let mut event = Event::new(event_key::PLACE_LIMIT_ORDER)
152 .add_attribute(
153 event_key::MARKET_TYPE,
154 match src.market_type {
155 CollateralIsQuote => event_val::NOTIONAL_BASE,
156 CollateralIsBase => event_val::COLLATERAL_BASE,
157 },
158 )
159 .add_attribute(event_key::ORDER_ID, src.order_id.to_string())
160 .add_attribute(event_key::POS_OWNER, src.owner.to_string())
161 .add_attribute(event_key::TRIGGER_PRICE, src.trigger_price.to_string())
162 .add_attribute(event_key::DEPOSIT_COLLATERAL, src.collateral.to_string())
163 .add_attribute(
164 event_key::DEPOSIT_COLLATERAL_USD,
165 src.collateral_usd.to_string(),
166 )
167 .add_attribute(event_key::LEVERAGE_TO_BASE, src.leverage.to_string())
168 .add_attribute(event_key::DIRECTION, src.direction.as_str());
169
170 if let Some(stop_loss_override) = src.stop_loss_override {
171 event = event.add_attribute(
172 event_key::STOP_LOSS_OVERRIDE,
173 stop_loss_override.to_string(),
174 );
175 }
176
177 if let Some(take_profit_override) = src.take_profit_override {
178 event = event.add_attribute(
179 event_key::TAKE_PROFIT_OVERRIDE,
180 take_profit_override.to_string(),
181 );
182 }
183 #[allow(deprecated)]
184 if let Some(max_gains) = src.max_gains {
185 event = event.add_attribute(event_key::MAX_GAINS, max_gains.to_string());
186 }
187
188 event
189 }
190 }
191 impl TryFrom<Event> for PlaceLimitOrderEvent {
192 type Error = anyhow::Error;
193
194 fn try_from(evt: Event) -> Result<Self, Self::Error> {
195 #[allow(deprecated)]
196 Ok(Self {
197 market_type: evt.map_attr_result(event_key::MARKET_TYPE, |s| match s {
198 event_val::NOTIONAL_BASE => Ok(CollateralIsQuote),
199 event_val::COLLATERAL_BASE => Ok(CollateralIsBase),
200 _ => Err(PerpError::unimplemented().into()),
201 })?,
202 collateral: evt
203 .string_attr(event_key::DEPOSIT_COLLATERAL)?
204 .as_str()
205 .try_into()?,
206 collateral_usd: evt
207 .string_attr(event_key::DEPOSIT_COLLATERAL_USD)?
208 .as_str()
209 .try_into()?,
210 leverage: SignedLeverageToBase::from_str(
211 &(evt.string_attr(event_key::LEVERAGE_TO_BASE)?),
212 )?,
213 direction: evt.direction_attr(event_key::DIRECTION)?,
214 order_id: OrderId::new(evt.u64_attr(event_key::ORDER_ID)?),
215 owner: evt.unchecked_addr_attr(event_key::POS_OWNER)?,
216 trigger_price: PriceBaseInQuote::try_from_number(
217 evt.number_attr(event_key::TRIGGER_PRICE)?,
218 )?,
219 stop_loss_override: match evt.try_number_attr(event_key::STOP_LOSS_OVERRIDE)? {
220 None => None,
221 Some(stop_loss_override) => {
222 Some(PriceBaseInQuote::try_from_number(stop_loss_override)?)
223 }
224 },
225 take_profit_override: evt
226 .try_map_attr(event_key::TAKE_PROFIT_OVERRIDE, |s| {
227 TakeProfitTrader::try_from(s)
228 })
229 .transpose()?,
230
231 max_gains: evt
232 .try_map_attr(event_key::MAX_GAINS, MaxGainsInQuote::from_str)
233 .transpose()?,
234 })
235 }
236 }
237
238 pub struct CancelLimitOrderEvent {
240 pub order_id: OrderId,
242 }
243
244 impl From<CancelLimitOrderEvent> for Event {
245 fn from(src: CancelLimitOrderEvent) -> Self {
246 Event::new(event_key::PLACE_LIMIT_ORDER)
247 .add_attribute(event_key::ORDER_ID, src.order_id.to_string())
248 }
249 }
250 impl TryFrom<Event> for CancelLimitOrderEvent {
251 type Error = anyhow::Error;
252
253 fn try_from(evt: Event) -> Result<Self, Self::Error> {
254 Ok(Self {
255 order_id: OrderId::new(evt.u64_attr(event_key::ORDER_ID)?),
256 })
257 }
258 }
259
260 pub struct ExecuteLimitOrderEvent {
262 pub order_id: OrderId,
264 pub pos_id: Option<PositionId>,
266 pub error: Option<String>,
268 }
269
270 impl From<ExecuteLimitOrderEvent> for Event {
271 fn from(src: ExecuteLimitOrderEvent) -> Self {
272 let mut event = Event::new(event_key::EXECUTE_LIMIT_ORDER)
273 .add_attribute(event_key::ORDER_ID, src.order_id.to_string());
274
275 if let Some(pos_id) = src.pos_id {
276 event = event.add_attribute(event_key::POS_ID, pos_id.to_string());
277 }
278
279 if let Some(error) = src.error {
280 event = event.add_attribute(event_key::EXECUTE_LIMIT_ORDER_ERROR, error);
281 }
282
283 event
284 }
285 }
286 impl TryFrom<Event> for ExecuteLimitOrderEvent {
287 type Error = anyhow::Error;
288
289 fn try_from(evt: Event) -> Result<Self, Self::Error> {
290 Ok(Self {
291 order_id: OrderId::new(evt.u64_attr(event_key::ORDER_ID)?),
292 pos_id: evt.try_u64_attr(event_key::POS_ID)?.map(PositionId::new),
293 error: evt.try_map_attr(event_key::EXECUTE_LIMIT_ORDER_ERROR, |x| x.to_owned()),
294 })
295 }
296 }
297}