cosm_tome_wasm_deploy_fork/chain/
response.rs

1use cosmrs::proto::{
2    cosmos::base::abci::v1beta1::TxResponse as CosmosResponse,
3    tendermint::abci::{Event as ProtoEvent, EventAttribute},
4};
5use cosmrs::rpc::abci::{
6    tag::{Key, Tag as TendermintProtoTag, Value},
7    Code as TendermintCode, Event as TendermintEvent,
8};
9use cosmrs::rpc::endpoint::{
10    abci_query::AbciQuery,
11    broadcast::tx_async::Response as AsyncTendermintResponse,
12    broadcast::tx_commit::{Response as BlockingTendermintResponse, TxResult},
13    broadcast::tx_sync::Response as SyncTendermintResponse,
14};
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17use std::str::FromStr;
18
19use super::error::{ChainError, DeserializeError};
20
21#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Default)]
22pub struct ChainResponse {
23    pub code: Code,
24    pub data: Option<Vec<u8>>,
25    pub log: String,
26}
27
28impl ChainResponse {
29    pub fn data<'a, T: Deserialize<'a>>(&'a self) -> Result<T, DeserializeError> {
30        let r: T = serde_json::from_slice(
31            self.data
32                .as_ref()
33                .ok_or(DeserializeError::EmptyResponse)?
34                .as_slice(),
35        )?;
36        Ok(r)
37    }
38}
39
40impl From<AbciQuery> for ChainResponse {
41    fn from(res: AbciQuery) -> ChainResponse {
42        ChainResponse {
43            code: res.code.into(),
44            data: Some(res.value),
45            log: res.log.to_string(),
46        }
47    }
48}
49
50impl From<TxResult> for ChainResponse {
51    fn from(res: TxResult) -> ChainResponse {
52        ChainResponse {
53            code: res.code.into(),
54            data: res.data.map(|d| d.into()),
55            log: res.log.to_string(),
56        }
57    }
58}
59
60impl From<tonic::Status> for ChainResponse {
61    fn from(res: tonic::Status) -> ChainResponse {
62        ChainResponse {
63            code: res.code().into(),
64            data: Some(res.details().into()),
65            log: res.message().into(),
66        }
67    }
68}
69
70/// AsyncChainTxResponse is returned from the async `tx_broadcast()` api.
71#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Default)]
72pub struct AsyncChainTxResponse {
73    pub res: ChainResponse,
74    pub tx_hash: String,
75}
76
77impl AsRef<ChainResponse> for AsyncChainTxResponse {
78    fn as_ref(&self) -> &ChainResponse {
79        &self.res
80    }
81}
82
83impl From<CosmosResponse> for AsyncChainTxResponse {
84    fn from(res: CosmosResponse) -> Self {
85        Self {
86            res: ChainResponse {
87                code: res.code.into(),
88                data: Some(res.data.into()), // TODO
89                log: res.raw_log,
90            },
91            tx_hash: res.txhash,
92        }
93    }
94}
95
96impl From<AsyncTendermintResponse> for AsyncChainTxResponse {
97    fn from(res: AsyncTendermintResponse) -> Self {
98        Self {
99            res: ChainResponse {
100                code: res.code.into(),
101                data: Some(res.data.into()),
102                log: res.log.to_string(),
103            },
104            tx_hash: res.hash.to_string(),
105        }
106    }
107}
108
109impl From<SyncTendermintResponse> for AsyncChainTxResponse {
110    fn from(res: SyncTendermintResponse) -> Self {
111        Self {
112            res: ChainResponse {
113                code: res.code.into(),
114                data: Some(res.data.into()),
115                log: res.log.to_string(),
116            },
117            tx_hash: res.hash.to_string(),
118        }
119    }
120}
121
122/// ChainTxResponse is returned from the blocking `tx_broadcast_block()` api.
123/// Since we wait for the tx to be commited in the next block, we get the full tx data.
124#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Default)]
125pub struct ChainTxResponse {
126    pub res: ChainResponse,
127    pub events: Vec<Event>,
128    pub gas_wanted: u64,
129    pub gas_used: u64,
130    pub tx_hash: String,
131    pub height: u64,
132}
133
134impl ChainTxResponse {
135    pub fn find_event_tags(&self, event_type: String, key_name: String) -> Vec<&Tag> {
136        let mut events = vec![];
137        for event in &self.events {
138            if event.type_str == event_type {
139                for attr in &event.attributes {
140                    if attr.key == key_name {
141                        events.push(attr);
142                    }
143                }
144            }
145        }
146        events
147    }
148}
149
150impl AsRef<ChainResponse> for ChainTxResponse {
151    fn as_ref(&self) -> &ChainResponse {
152        &self.res
153    }
154}
155
156impl From<BlockingTendermintResponse> for ChainTxResponse {
157    fn from(res: BlockingTendermintResponse) -> Self {
158        ChainTxResponse {
159            res: ChainResponse {
160                code: res.deliver_tx.code.into(),
161                data: res.deliver_tx.data.map(|d| d.into()),
162                log: res.deliver_tx.log.to_string(),
163            },
164            events: res.deliver_tx.events.into_iter().map(Into::into).collect(),
165            gas_used: res.deliver_tx.gas_used.into(),
166            gas_wanted: res.deliver_tx.gas_wanted.into(),
167            tx_hash: res.hash.to_string(),
168            height: res.height.into(),
169        }
170    }
171}
172
173impl TryFrom<CosmosResponse> for ChainTxResponse {
174    type Error = ChainError;
175
176    fn try_from(res: CosmosResponse) -> Result<Self, Self::Error> {
177        Ok(ChainTxResponse {
178            res: ChainResponse {
179                code: res.code.into(),
180                data: Some(res.data.into()), // TODO
181                log: res.raw_log,
182            },
183            events: res
184                .events
185                .into_iter()
186                .map(TryInto::try_into)
187                .collect::<Result<Vec<_>, _>>()?,
188            gas_wanted: res.gas_wanted as u64,
189            gas_used: res.gas_used as u64,
190            tx_hash: res.txhash,
191            height: res.height as u64,
192        })
193    }
194}
195
196#[derive(
197    Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
198)]
199pub enum Code {
200    Ok,
201    Err(u32),
202}
203
204impl Default for Code {
205    fn default() -> Code {
206        Code::Ok
207    }
208}
209
210impl Code {
211    pub fn is_ok(self) -> bool {
212        match self {
213            Code::Ok => true,
214            Code::Err(_) => false,
215        }
216    }
217
218    pub fn is_err(self) -> bool {
219        !self.is_ok()
220    }
221
222    pub fn value(self) -> u32 {
223        u32::from(self)
224    }
225}
226
227impl From<u32> for Code {
228    fn from(value: u32) -> Code {
229        match value {
230            0 => Code::Ok,
231            err => Code::Err(err),
232        }
233    }
234}
235
236impl From<Code> for u32 {
237    fn from(code: Code) -> u32 {
238        match code {
239            Code::Ok => 0,
240            Code::Err(err) => err,
241        }
242    }
243}
244
245impl From<u16> for Code {
246    fn from(value: u16) -> Code {
247        match value {
248            0 => Code::Ok,
249            err => Code::Err(err.into()),
250        }
251    }
252}
253
254impl From<u8> for Code {
255    fn from(value: u8) -> Code {
256        match value {
257            0 => Code::Ok,
258            err => Code::Err(err.into()),
259        }
260    }
261}
262
263impl From<TendermintCode> for Code {
264    fn from(value: TendermintCode) -> Code {
265        match value {
266            TendermintCode::Ok => Code::Ok,
267            TendermintCode::Err(err) => Code::Err(err.into()),
268        }
269    }
270}
271
272impl From<tonic::Code> for Code {
273    fn from(value: tonic::Code) -> Code {
274        // NOTE: `value` is an isize, so we are just manually
275        // matching them to avoid any casting errors in the future
276        match value {
277            tonic::Code::Ok => Code::Ok,
278            tonic::Code::Cancelled => Code::Err(1),
279            tonic::Code::Unknown => Code::Err(2),
280            tonic::Code::InvalidArgument => Code::Err(3),
281            tonic::Code::DeadlineExceeded => Code::Err(4),
282            tonic::Code::NotFound => Code::Err(5),
283            tonic::Code::AlreadyExists => Code::Err(6),
284            tonic::Code::PermissionDenied => Code::Err(7),
285            tonic::Code::ResourceExhausted => Code::Err(8),
286            tonic::Code::FailedPrecondition => Code::Err(9),
287            tonic::Code::Aborted => Code::Err(10),
288            tonic::Code::OutOfRange => Code::Err(11),
289            tonic::Code::Unimplemented => Code::Err(12),
290            tonic::Code::Internal => Code::Err(13),
291            tonic::Code::Unavailable => Code::Err(14),
292            tonic::Code::DataLoss => Code::Err(15),
293            tonic::Code::Unauthenticated => Code::Err(16),
294        }
295    }
296}
297
298#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Hash)]
299pub struct Event {
300    pub type_str: String,
301    pub attributes: Vec<Tag>,
302}
303
304impl From<TendermintEvent> for Event {
305    fn from(e: TendermintEvent) -> Self {
306        Self {
307            type_str: e.type_str,
308            attributes: e.attributes.into_iter().map(Into::into).collect(),
309        }
310    }
311}
312
313impl TryFrom<Event> for TendermintEvent {
314    type Error = ChainError;
315
316    fn try_from(e: Event) -> Result<Self, Self::Error> {
317        Ok(Self {
318            type_str: e.type_str,
319            attributes: e
320                .attributes
321                .into_iter()
322                .map(TryInto::try_into)
323                .collect::<Result<Vec<_>, _>>()?,
324        })
325    }
326}
327
328impl TryFrom<ProtoEvent> for Event {
329    type Error = ChainError;
330
331    fn try_from(e: ProtoEvent) -> Result<Self, Self::Error> {
332        Ok(Self {
333            type_str: e.r#type,
334            attributes: e
335                .attributes
336                .into_iter()
337                .map(TryInto::try_into)
338                .collect::<Result<Vec<_>, _>>()?,
339        })
340    }
341}
342
343impl From<Event> for ProtoEvent {
344    fn from(e: Event) -> Self {
345        Self {
346            r#type: e.type_str,
347            attributes: e.attributes.into_iter().map(Into::into).collect(),
348        }
349    }
350}
351
352#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Hash)]
353pub struct Tag {
354    pub key: String,
355    pub value: String,
356}
357
358impl From<TendermintProtoTag> for Tag {
359    fn from(tag: TendermintProtoTag) -> Self {
360        Self {
361            key: tag.key.to_string(),
362            value: tag.value.to_string(),
363        }
364    }
365}
366
367impl TryFrom<Tag> for TendermintProtoTag {
368    type Error = ChainError;
369
370    fn try_from(tag: Tag) -> Result<Self, Self::Error> {
371        Ok(Self {
372            key: Key::from_str(&tag.key)?,
373            value: Value::from_str(&tag.value)?,
374        })
375    }
376}
377
378impl From<Tag> for EventAttribute {
379    fn from(tag: Tag) -> Self {
380        Self {
381            key: tag.key.into_bytes().into(),
382            value: tag.value.into_bytes().into(),
383            index: true,
384        }
385    }
386}
387
388impl TryFrom<EventAttribute> for Tag {
389    type Error = ChainError;
390
391    fn try_from(attr: EventAttribute) -> Result<Self, Self::Error> {
392        Ok(Self {
393            key: String::from_utf8(attr.key.into()).map_err(|e| ChainError::ProtoDecoding {
394                message: e.to_string(),
395            })?,
396            value: String::from_utf8(attr.value.into()).map_err(|e| ChainError::ProtoDecoding {
397                message: e.to_string(),
398            })?,
399        })
400    }
401}