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, 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 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}