levana_perpswap_cosmos/
event.rs1use std::str::FromStr;
3
4use crate::direction::DirectionToBase;
5use crate::leverage::LeverageToBase;
6use crate::prelude::*;
7use anyhow::Context;
8use cosmwasm_std::Event;
9use serde::de::DeserializeOwned;
10
11use crate::error::{ErrorDomain, ErrorId};
12
13pub trait CosmwasmEventExt {
15 fn has_attr(&self, key: &str) -> bool;
20
21 fn try_map_attr<B>(&self, key: &str, f: impl Fn(&str) -> B) -> Option<B>;
23
24 fn try_json_attr<B: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<B>> {
26 match self.try_map_attr(key, |s| serde_json::from_str(s)) {
27 None => Ok(None),
28 Some(x) => Ok(Some(x?)),
29 }
30 }
31
32 fn json_attr<B: DeserializeOwned>(&self, key: &str) -> anyhow::Result<B> {
34 self.map_attr_result(key, |s| {
35 serde_json::from_str(s).map_err(anyhow::Error::from)
36 })
37 }
38
39 fn u64_attr(&self, key: &str) -> anyhow::Result<u64> {
41 self.map_attr_result(key, |s| s.parse().map_err(anyhow::Error::from))
42 }
43
44 fn try_u64_attr(&self, key: &str) -> anyhow::Result<Option<u64>> {
46 match self.try_map_attr(key, |s| s.parse()) {
47 None => Ok(None),
48 Some(x) => Ok(Some(x?)),
49 }
50 }
51
52 fn timestamp_attr(&self, key: &str) -> anyhow::Result<Timestamp> {
54 self.map_attr_result(key, |s| Timestamp::from_str(s).map_err(|x| x.into()))
55 }
56
57 fn try_timestamp_attr(&self, key: &str) -> anyhow::Result<Option<Timestamp>> {
59 self.try_map_attr(key, |s| Timestamp::from_str(s).map_err(|x| x.into()))
60 .transpose()
61 }
62
63 fn decimal_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<T> {
65 self.map_attr_result(key, |s| {
66 s.parse()
67 .ok()
68 .with_context(|| format!("decimal_attr failed on key {key} and value {s}"))
69 })
70 }
71
72 fn non_zero_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<NonZero<T>> {
74 self.map_attr_result(key, |s| {
75 s.parse()
76 .ok()
77 .with_context(|| format!("non_zero_attr failed on key {key} and value {s}"))
78 })
79 }
80
81 fn signed_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Signed<T>> {
83 self.map_attr_result(key, |s| {
84 s.parse()
85 .ok()
86 .with_context(|| format!("signed_attr failed on key {key} and value {s}"))
87 })
88 }
89
90 fn number_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Signed<T>> {
92 self.map_attr_result(key, |s| s.parse())
93 }
94
95 fn try_number_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Option<Signed<T>>> {
97 self.try_map_attr(key, |s| {
98 s.parse()
99 .ok()
100 .with_context(|| format!("try_number_attr failed parse on key {key} and value {s}"))
101 })
102 .transpose()
103 }
104
105 fn try_decimal_attr<T: UnsignedDecimal>(&self, key: &str) -> anyhow::Result<Option<T>> {
107 self.try_map_attr(key, |s| {
108 s.parse().ok().with_context(|| {
109 format!("try_decimal_attr failed parse on key {key} and value {s}")
110 })
111 })
112 .transpose()
113 }
114
115 fn try_price_base_in_quote(&self, key: &str) -> anyhow::Result<Option<PriceBaseInQuote>> {
117 self.try_map_attr(key, |s| {
118 s.parse().ok().with_context(|| {
119 format!("try_price_base_in_quote failed parse on key {key} and value {s}")
120 })
121 })
122 .transpose()
123 }
124
125 fn string_attr(&self, key: &str) -> anyhow::Result<String> {
127 self.map_attr_ok(key, |s| s.to_string())
128 }
129
130 fn bool_attr(&self, key: &str) -> anyhow::Result<bool> {
132 self.string_attr(key)
133 .and_then(|s| s.parse::<bool>().map_err(|err| err.into()))
134 }
135
136 fn direction_attr(&self, key: &str) -> anyhow::Result<DirectionToBase> {
138 self.map_attr_result(key, |s| match s {
139 "long" => Ok(DirectionToBase::Long),
140 "short" => Ok(DirectionToBase::Short),
141 _ => Err(anyhow::anyhow!("Invalid direction: {s}")),
142 })
143 }
144
145 fn leverage_to_base_attr(&self, key: &str) -> anyhow::Result<LeverageToBase> {
147 self.map_attr_result(key, LeverageToBase::from_str)
148 }
149
150 fn try_leverage_to_base_attr(&self, key: &str) -> anyhow::Result<Option<LeverageToBase>> {
152 self.try_map_attr(key, LeverageToBase::from_str).transpose()
153 }
154
155 fn unchecked_addr_attr(&self, key: &str) -> anyhow::Result<Addr> {
157 self.map_attr_ok(key, |s| Addr::unchecked(s))
158 }
159
160 fn try_unchecked_addr_attr(&self, key: &str) -> anyhow::Result<Option<Addr>> {
162 self.try_map_attr(key, |s| Ok(Addr::unchecked(s)))
163 .transpose()
164 }
165
166 fn map_attr_ok<B>(&self, key: &str, f: impl Fn(&str) -> B) -> anyhow::Result<B> {
168 match self.try_map_attr(key, f) {
169 Some(x) => Ok(x),
170 None => Err(anyhow!(PerpError::new(
171 ErrorId::Any,
172 ErrorDomain::Default,
173 format!("no such key {key}")
174 ))),
175 }
176 }
177
178 fn map_attr_result<B>(
180 &self,
181 key: &str,
182 f: impl Fn(&str) -> anyhow::Result<B>,
183 ) -> anyhow::Result<B> {
184 self.map_attr_ok(key, f)?
186 }
187}
188
189impl CosmwasmEventExt for Event {
190 fn has_attr(&self, key: &str) -> bool {
191 self.attributes.iter().any(|a| a.key == key)
192 }
193 fn try_map_attr<B>(&self, key: &str, f: impl Fn(&str) -> B) -> Option<B> {
194 self.attributes.iter().find_map(|a| {
195 if a.key == key {
196 Some(f(a.value.as_str()))
197 } else {
198 None
199 }
200 })
201 }
202}