levana_perpswap_cosmos/contracts/market/
crank.rs1use super::deferred_execution::{DeferredExecId, DeferredExecTarget};
3use super::position::PositionId;
4use crate::contracts::market::order::OrderId;
5use crate::contracts::market::position::LiquidationReason;
6use crate::prelude::*;
7
8#[cw_serde]
10pub enum CrankWorkInfo {
11 CloseAllPositions {
13 position: PositionId,
15 },
16 ResetLpBalances {},
18 Liquifunding {
20 position: PositionId,
22 },
23 Liquidation {
27 position: PositionId,
29 liquidation_reason: LiquidationReason,
31 },
32 DeferredExec {
34 deferred_exec_id: DeferredExecId,
36 target: DeferredExecTarget,
38 },
39 LimitOrder {
41 order_id: OrderId,
43 },
44 Completed {},
46}
47
48impl CrankWorkInfo {
49 pub fn receives_crank_rewards(&self) -> bool {
58 match self {
59 CrankWorkInfo::CloseAllPositions { .. }
60 | CrankWorkInfo::ResetLpBalances {}
61 | CrankWorkInfo::Completed { .. } => false,
62 CrankWorkInfo::Liquifunding { .. }
63 | CrankWorkInfo::Liquidation { .. }
64 | CrankWorkInfo::DeferredExec { .. }
65 | CrankWorkInfo::LimitOrder { .. } => true,
66 }
67 }
68}
69
70pub mod events {
72 use std::borrow::Cow;
73
74 use super::*;
75 use cosmwasm_std::Event;
76
77 pub struct CrankExecBatchEvent {
79 pub requested: u64,
81 pub paying: u64,
83 pub actual: Vec<(CrankWorkInfo, PricePoint)>,
85 }
86
87 impl From<CrankExecBatchEvent> for Event {
88 fn from(
89 CrankExecBatchEvent {
90 requested,
91 paying,
92 actual,
93 }: CrankExecBatchEvent,
94 ) -> Self {
95 let mut event = Event::new("crank-batch-exec")
96 .add_attribute("requested", requested.to_string())
97 .add_attribute("actual", actual.len().to_string())
98 .add_attribute("paying", paying.to_string());
99
100 for (idx, (work, price_point)) in actual.into_iter().enumerate() {
101 event = event.add_attribute(
102 format!("work-{}", idx + 1),
103 match work {
104 CrankWorkInfo::CloseAllPositions { .. } => {
105 Cow::Borrowed("close-all-positions")
106 }
107 CrankWorkInfo::ResetLpBalances {} => "reset-lp-balances".into(),
108 CrankWorkInfo::Liquifunding { position, .. } => {
109 format!("liquifund {position}").into()
110 }
111 CrankWorkInfo::Liquidation { position, .. } => {
112 format!("liquidation {position}").into()
113 }
114 CrankWorkInfo::DeferredExec {
115 deferred_exec_id, ..
116 } => format!("deferred exec {deferred_exec_id}").into(),
117 CrankWorkInfo::LimitOrder { order_id, .. } => {
118 format!("limit order {order_id}").into()
119 }
120 CrankWorkInfo::Completed {} => {
121 format!("completed {}", price_point.timestamp).into()
122 }
123 },
124 )
125 }
126
127 event
128 }
129 }
130
131 pub struct CrankWorkInfoEvent {
133 pub work_info: CrankWorkInfo,
135 pub price_point: PricePoint,
137 }
138
139 impl From<CrankWorkInfoEvent> for Event {
140 fn from(
141 CrankWorkInfoEvent {
142 work_info,
143 price_point,
144 }: CrankWorkInfoEvent,
145 ) -> Self {
146 let mut event = Event::new("crank-work")
147 .add_attribute(
148 "kind",
149 match work_info {
150 CrankWorkInfo::CloseAllPositions { .. } => "close-all-positions",
151 CrankWorkInfo::ResetLpBalances { .. } => "reset-lp-balances",
152 CrankWorkInfo::Completed { .. } => "completed",
153 CrankWorkInfo::Liquidation { .. } => "liquidation",
154 CrankWorkInfo::Liquifunding { .. } => "liquifunding",
155 CrankWorkInfo::DeferredExec { .. } => "deferred-exec",
156 CrankWorkInfo::LimitOrder { .. } => "limit-order",
157 },
158 )
159 .add_attribute("price-point-timestamp", price_point.timestamp.to_string())
162 .add_attribute("price-point", serde_json::to_string(&price_point).unwrap());
163
164 let (position_id, order_id) = match work_info {
165 CrankWorkInfo::CloseAllPositions { position } => (Some(position), None),
166 CrankWorkInfo::ResetLpBalances {} => (None, None),
167 CrankWorkInfo::Completed {} => (None, None),
168 CrankWorkInfo::Liquidation {
169 position,
170 liquidation_reason: _,
171 } => (Some(position), None),
172 CrankWorkInfo::Liquifunding { position } => (Some(position), None),
173 CrankWorkInfo::DeferredExec {
174 deferred_exec_id: _,
175 target,
176 } => (target.position_id(), target.order_id()),
177 CrankWorkInfo::LimitOrder { order_id } => (None, Some(order_id)),
178 };
179
180 if let Some(position_id) = position_id {
181 event = event.add_attribute("pos-id", position_id.to_string());
182 }
183
184 if let CrankWorkInfo::Liquidation {
185 liquidation_reason, ..
186 } = work_info
187 {
188 event = event.add_attribute("liquidation-reason", liquidation_reason.to_string());
189 }
190
191 if let CrankWorkInfo::DeferredExec {
192 deferred_exec_id,
193 target,
194 } = work_info
195 {
196 event = event
197 .add_attribute("deferred-exec-id", deferred_exec_id.to_string())
198 .add_attribute(
199 "deferred-exec-target",
200 match target {
201 DeferredExecTarget::DoesNotExist => "not-exist",
202 DeferredExecTarget::Position { .. } => "position",
203 DeferredExecTarget::Order { .. } => "order",
204 },
205 );
206 }
207
208 if let Some(order_id) = order_id {
209 event = event.add_attribute("order-id", order_id.to_string());
210 }
211
212 event
213 }
214 }
215
216 impl TryFrom<Event> for CrankWorkInfoEvent {
217 type Error = anyhow::Error;
218
219 fn try_from(evt: Event) -> anyhow::Result<Self> {
220 let get_position_id =
221 || -> anyhow::Result<PositionId> { Ok(PositionId::new(evt.u64_attr("pos-id")?)) };
222 let get_order_id =
223 || -> anyhow::Result<OrderId> { Ok(OrderId::new(evt.u64_attr("order-id")?)) };
224
225 let get_liquidation_reason = || -> anyhow::Result<LiquidationReason> {
226 match evt.string_attr("liquidation-reason")?.as_str() {
227 "liquidated" => Ok(LiquidationReason::Liquidated),
228 "take-profit" => Ok(LiquidationReason::TakeProfit),
229 _ => Err(PerpError::unimplemented().into()),
230 }
231 };
232
233 let work_info = evt.map_attr_result("kind", |s| match s {
234 "completed" => Ok(CrankWorkInfo::Completed {}),
235 "liquifunding" => Ok(CrankWorkInfo::Liquifunding {
236 position: get_position_id()?,
237 }),
238 "liquidation" => Ok(CrankWorkInfo::Liquidation {
239 position: get_position_id()?,
240 liquidation_reason: get_liquidation_reason()?,
241 }),
242 "limit-order" => Ok(CrankWorkInfo::LimitOrder {
243 order_id: get_order_id()?,
244 }),
245 "close-all-positions" => Ok(CrankWorkInfo::CloseAllPositions {
246 position: get_position_id()?,
247 }),
248 "reset-lp-balances" => Ok(CrankWorkInfo::ResetLpBalances {}),
249 "deferred-exec" => Ok(CrankWorkInfo::DeferredExec {
250 deferred_exec_id: DeferredExecId::from_u64(evt.u64_attr("deferred-exec-id")?),
251 target: evt.map_attr_result(
252 "deferred-exec-target",
253 |x| -> Result<DeferredExecTarget> {
254 match x {
255 "not-exist" => Ok(DeferredExecTarget::DoesNotExist),
256 "position" => get_position_id().map(DeferredExecTarget::Position),
257 "order" => get_order_id().map(DeferredExecTarget::Order),
258 _ => Err(PerpError::unimplemented().into()),
259 }
260 },
261 )?,
262 }),
263
264 _ => Err(PerpError::unimplemented().into()),
265 })?;
266
267 Ok(Self {
268 work_info,
269 price_point: evt.json_attr("price-point")?,
270 })
271 }
272 }
273
274 impl TryFrom<Event> for CrankWorkInfo {
276 type Error = anyhow::Error;
277
278 fn try_from(evt: Event) -> anyhow::Result<Self> {
279 CrankWorkInfoEvent::try_from(evt).map(|x| x.work_info)
280 }
281 }
282}