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#[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()), 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#[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()), 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,
198 Clone,
199 Debug,
200 Default,
201 Eq,
202 Hash,
203 PartialEq,
204 PartialOrd,
205 Ord,
206 Serialize,
207 Deserialize,
208 JsonSchema,
209)]
210pub enum Code {
211 #[default]
212 Ok,
213 Err(u32),
214}
215
216impl Code {
217 pub fn is_ok(self) -> bool {
218 match self {
219 Code::Ok => true,
220 Code::Err(_) => false,
221 }
222 }
223
224 pub fn is_err(self) -> bool {
225 !self.is_ok()
226 }
227
228 pub fn value(self) -> u32 {
229 u32::from(self)
230 }
231}
232
233impl From<u32> for Code {
234 fn from(value: u32) -> Code {
235 match value {
236 0 => Code::Ok,
237 err => Code::Err(err),
238 }
239 }
240}
241
242impl From<Code> for u32 {
243 fn from(code: Code) -> u32 {
244 match code {
245 Code::Ok => 0,
246 Code::Err(err) => err,
247 }
248 }
249}
250
251impl From<u16> for Code {
252 fn from(value: u16) -> Code {
253 match value {
254 0 => Code::Ok,
255 err => Code::Err(err.into()),
256 }
257 }
258}
259
260impl From<u8> for Code {
261 fn from(value: u8) -> Code {
262 match value {
263 0 => Code::Ok,
264 err => Code::Err(err.into()),
265 }
266 }
267}
268
269impl From<TendermintCode> for Code {
270 fn from(value: TendermintCode) -> Code {
271 match value {
272 TendermintCode::Ok => Code::Ok,
273 TendermintCode::Err(err) => Code::Err(err.into()),
274 }
275 }
276}
277
278impl From<tonic::Code> for Code {
279 fn from(value: tonic::Code) -> Code {
280 match value {
283 tonic::Code::Ok => Code::Ok,
284 tonic::Code::Cancelled => Code::Err(1),
285 tonic::Code::Unknown => Code::Err(2),
286 tonic::Code::InvalidArgument => Code::Err(3),
287 tonic::Code::DeadlineExceeded => Code::Err(4),
288 tonic::Code::NotFound => Code::Err(5),
289 tonic::Code::AlreadyExists => Code::Err(6),
290 tonic::Code::PermissionDenied => Code::Err(7),
291 tonic::Code::ResourceExhausted => Code::Err(8),
292 tonic::Code::FailedPrecondition => Code::Err(9),
293 tonic::Code::Aborted => Code::Err(10),
294 tonic::Code::OutOfRange => Code::Err(11),
295 tonic::Code::Unimplemented => Code::Err(12),
296 tonic::Code::Internal => Code::Err(13),
297 tonic::Code::Unavailable => Code::Err(14),
298 tonic::Code::DataLoss => Code::Err(15),
299 tonic::Code::Unauthenticated => Code::Err(16),
300 }
301 }
302}
303
304#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Hash)]
305pub struct Event {
306 pub type_str: String,
307 pub attributes: Vec<Tag>,
308}
309
310impl From<TendermintEvent> for Event {
311 fn from(e: TendermintEvent) -> Self {
312 Self {
313 type_str: e.type_str,
314 attributes: e.attributes.into_iter().map(Into::into).collect(),
315 }
316 }
317}
318
319impl TryFrom<Event> for TendermintEvent {
320 type Error = ChainError;
321
322 fn try_from(e: Event) -> Result<Self, Self::Error> {
323 Ok(Self {
324 type_str: e.type_str,
325 attributes: e
326 .attributes
327 .into_iter()
328 .map(TryInto::try_into)
329 .collect::<Result<Vec<_>, _>>()?,
330 })
331 }
332}
333
334impl TryFrom<ProtoEvent> for Event {
335 type Error = ChainError;
336
337 fn try_from(e: ProtoEvent) -> Result<Self, Self::Error> {
338 Ok(Self {
339 type_str: e.r#type,
340 attributes: e
341 .attributes
342 .into_iter()
343 .map(TryInto::try_into)
344 .collect::<Result<Vec<_>, _>>()?,
345 })
346 }
347}
348
349impl From<Event> for ProtoEvent {
350 fn from(e: Event) -> Self {
351 Self {
352 r#type: e.type_str,
353 attributes: e.attributes.into_iter().map(Into::into).collect(),
354 }
355 }
356}
357
358#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, Eq, PartialEq, Hash)]
359pub struct Tag {
360 pub key: String,
361 pub value: String,
362}
363
364impl From<TendermintProtoTag> for Tag {
365 fn from(tag: TendermintProtoTag) -> Self {
366 Self {
367 key: tag.key.to_string(),
368 value: tag.value.to_string(),
369 }
370 }
371}
372
373impl TryFrom<Tag> for TendermintProtoTag {
374 type Error = ChainError;
375
376 fn try_from(tag: Tag) -> Result<Self, Self::Error> {
377 Ok(Self {
378 key: Key::from_str(&tag.key)?,
379 value: Value::from_str(&tag.value)?,
380 })
381 }
382}
383
384impl From<Tag> for EventAttribute {
385 fn from(tag: Tag) -> Self {
386 Self {
387 key: tag.key.into_bytes().into(),
388 value: tag.value.into_bytes().into(),
389 index: true,
390 }
391 }
392}
393
394impl TryFrom<EventAttribute> for Tag {
395 type Error = ChainError;
396
397 fn try_from(attr: EventAttribute) -> Result<Self, Self::Error> {
398 Ok(Self {
399 key: String::from_utf8(attr.key.into()).map_err(|e| ChainError::ProtoDecoding {
400 message: e.to_string(),
401 })?,
402 value: String::from_utf8(attr.value.into()).map_err(|e| ChainError::ProtoDecoding {
403 message: e.to_string(),
404 })?,
405 })
406 }
407}