Skip to main content

celestia_types/state/
tx.rs

1use std::fmt;
2
3use celestia_proto::cosmos::base::abci::v1beta1::AbciMessageLog;
4use serde::{Deserialize, Serialize};
5use serde_repr::Deserialize_repr;
6use serde_repr::Serialize_repr;
7use tendermint_proto::Protobuf;
8use tendermint_proto::google::protobuf::Any;
9use tendermint_proto::v0_38::abci::Event;
10#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
11use wasm_bindgen::prelude::*;
12
13#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
14use crate::any::JsAny;
15use crate::bail_validation;
16use crate::hash::Hash;
17#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
18use crate::signature::JsSignature;
19use crate::state::bit_array::BitVector;
20#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
21use crate::state::bit_array::JsBitVector;
22use crate::state::{Address, Coin};
23use crate::{Error, Height};
24
25pub use celestia_proto::cosmos::base::abci::v1beta1::TxResponse as RawTxResponse;
26pub use celestia_proto::cosmos::tx::v1beta1::AuthInfo as RawAuthInfo;
27pub use celestia_proto::cosmos::tx::v1beta1::Fee as RawFee;
28pub use celestia_proto::cosmos::tx::v1beta1::ModeInfo as RawModeInfo;
29pub use celestia_proto::cosmos::tx::v1beta1::SignerInfo as RawSignerInfo;
30pub use celestia_proto::cosmos::tx::v1beta1::Tx as RawTx;
31pub use celestia_proto::cosmos::tx::v1beta1::TxBody as RawTxBody;
32pub use celestia_proto::cosmos::tx::v1beta1::mode_info::Sum as RawSum;
33pub use celestia_proto::cosmos::tx::v1beta1::mode_info::{Multi, Single};
34
35pub type Signature = Vec<u8>;
36
37/// [`BOND_DENOM`] defines the native staking denomination
38pub const BOND_DENOM: &str = "utia";
39
40/// [`Tx`] is the standard type used for broadcasting transactions.
41#[derive(Debug, Clone)]
42#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
43#[cfg_attr(
44    all(target_arch = "wasm32", feature = "wasm-bindgen"),
45    wasm_bindgen(getter_with_clone)
46)]
47pub struct Tx {
48    /// Processable content of the transaction
49    pub body: TxBody,
50
51    /// Authorization related content of the transaction, specifically signers, signer modes
52    /// and [`Fee`].
53    pub auth_info: AuthInfo,
54
55    /// List of signatures that matches the length and order of [`AuthInfo`]’s `signer_info`s to
56    /// allow connecting signature meta information like public key and signing mode by position.
57    ///
58    /// Signatures are provided as raw bytes so as to support current and future signature types.
59    /// [`AuthInfo`] should be introspected to determine the signature algorithm used.
60    #[cfg_attr(
61        all(target_arch = "wasm32", feature = "wasm-bindgen"),
62        wasm_bindgen(skip)
63    )]
64    pub signatures: Vec<Signature>,
65}
66
67/// [`TxBody`] of a transaction that all signers sign over.
68#[derive(Debug, Clone)]
69#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
70#[cfg_attr(
71    all(target_arch = "wasm32", feature = "wasm-bindgen"),
72    wasm_bindgen(getter_with_clone)
73)]
74pub struct TxBody {
75    /// `messages` is a list of messages to be executed. The required signers of
76    /// those messages define the number and order of elements in `AuthInfo`'s
77    /// signer_infos and Tx's signatures. Each required signer address is added to
78    /// the list only the first time it occurs.
79    ///
80    /// By convention, the first required signer (usually from the first message)
81    /// is referred to as the primary signer and pays the fee for the whole
82    /// transaction.
83    #[cfg_attr(
84        all(target_arch = "wasm32", feature = "wasm-bindgen"),
85        wasm_bindgen(skip)
86    )]
87    pub messages: Vec<Any>,
88    /// `memo` is any arbitrary memo to be added to the transaction.
89    pub memo: String,
90    /// `timeout` is the block height after which this transaction will not
91    /// be processed by the chain
92    #[cfg_attr(
93        all(target_arch = "wasm32", feature = "wasm-bindgen"),
94        wasm_bindgen(skip)
95    )]
96    pub timeout_height: Height,
97    /// `extension_options` are arbitrary options that can be added by chains
98    /// when the default options are not sufficient. If any of these are present
99    /// and can't be handled, the transaction will be rejected
100    #[cfg_attr(
101        all(target_arch = "wasm32", feature = "wasm-bindgen"),
102        wasm_bindgen(skip)
103    )]
104    pub extension_options: Vec<Any>,
105    /// `extension_options` are arbitrary options that can be added by chains
106    /// when the default options are not sufficient. If any of these are present
107    /// and can't be handled, they will be ignored
108    #[cfg_attr(
109        all(target_arch = "wasm32", feature = "wasm-bindgen"),
110        wasm_bindgen(skip)
111    )]
112    pub non_critical_extension_options: Vec<Any>,
113}
114
115/// Response to a tx query
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
118#[cfg_attr(
119    all(target_arch = "wasm32", feature = "wasm-bindgen"),
120    wasm_bindgen(getter_with_clone)
121)]
122pub struct TxResponse {
123    /// The block height
124    #[cfg_attr(
125        all(target_arch = "wasm32", feature = "wasm-bindgen"),
126        wasm_bindgen(skip)
127    )]
128    pub height: Height,
129
130    /// The transaction hash.
131    #[serde(with = "crate::serializers::hash")]
132    #[cfg_attr(
133        all(target_arch = "wasm32", feature = "wasm-bindgen"),
134        wasm_bindgen(skip)
135    )]
136    pub txhash: Hash,
137
138    /// Namespace for the Code
139    pub codespace: String,
140
141    /// Response code.
142    pub code: ErrorCode,
143
144    /// Result bytes, if any.
145    pub data: String,
146
147    /// The output of the application's logger (raw string). May be
148    /// non-deterministic.
149    pub raw_log: String,
150
151    /// The output of the application's logger (typed). May be non-deterministic.
152    pub logs: Vec<AbciMessageLog>,
153
154    /// Additional information. May be non-deterministic.
155    pub info: String,
156
157    /// Amount of gas requested for transaction.
158    pub gas_wanted: i64,
159
160    /// Amount of gas consumed by transaction.
161    pub gas_used: i64,
162
163    /// The request transaction bytes.
164    #[cfg_attr(
165        all(target_arch = "wasm32", feature = "wasm-bindgen"),
166        wasm_bindgen(skip)
167    )]
168    pub tx: Option<Any>,
169
170    /// Time of the previous block. For heights > 1, it's the weighted median of
171    /// the timestamps of the valid votes in the block.LastCommit. For height == 1,
172    /// it's genesis time.
173    pub timestamp: String,
174
175    /// Events defines all the events emitted by processing a transaction. Note,
176    /// these events include those emitted by processing all the messages and those
177    /// emitted from the ante. Whereas Logs contains the events, with
178    /// additional metadata, emitted only by processing the messages.
179    #[cfg_attr(
180        all(target_arch = "wasm32", feature = "wasm-bindgen"),
181        wasm_bindgen(skip)
182    )]
183    pub events: Vec<Event>,
184}
185
186#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
187#[wasm_bindgen]
188impl TxResponse {
189    /// The block height
190    #[wasm_bindgen(getter)]
191    pub fn height(&self) -> u64 {
192        self.height.value()
193    }
194
195    /// Events defines all the events emitted by processing a transaction. Note,
196    /// these events include those emitted by processing all the messages and those
197    /// emitted from the ante. Whereas Logs contains the events, with
198    /// additional metadata, emitted only by processing the messages.
199    #[wasm_bindgen(getter)]
200    pub fn events(&self) -> Vec<JsEvent> {
201        self.events.iter().cloned().map(Into::into).collect()
202    }
203}
204
205/// [`AuthInfo`] describes the fee and signer modes that are used to sign a transaction.
206#[derive(Debug, Clone)]
207#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
208#[cfg_attr(
209    all(target_arch = "wasm32", feature = "wasm-bindgen"),
210    wasm_bindgen(getter_with_clone)
211)]
212pub struct AuthInfo {
213    /// Defines the signing modes for the required signers.
214    ///
215    /// The number and order of elements must match the required signers from transaction
216    /// [`TxBody`]’s messages. The first element is the primary signer and the one
217    /// which pays the [`Fee`].
218    pub signer_infos: Vec<SignerInfo>,
219    /// [`Fee`] and gas limit for the transaction.
220    ///
221    /// The first signer is the primary signer and the one which pays the fee.
222    /// The fee can be calculated based on the cost of evaluating the body and doing signature
223    /// verification of the signers. This can be estimated via simulation.
224    pub fee: Fee,
225}
226
227/// SignerInfo describes the public key and signing mode of a single top-level
228/// signer.
229#[derive(Debug, Clone, PartialEq)]
230#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
231#[cfg_attr(
232    all(target_arch = "wasm32", feature = "wasm-bindgen"),
233    wasm_bindgen(getter_with_clone)
234)]
235pub struct SignerInfo {
236    /// public_key is the public key of the signer. It is optional for accounts
237    /// that already exist in state. If unset, the verifier can use the required \
238    /// signer address for this position and lookup the public key.
239    #[cfg_attr(
240        all(target_arch = "wasm32", feature = "wasm-bindgen"),
241        wasm_bindgen(skip)
242    )]
243    pub public_key: Option<Any>,
244    /// mode_info describes the signing mode of the signer and is a nested
245    /// structure to support nested multisig pubkey's
246    pub mode_info: ModeInfo,
247    /// sequence is the sequence of the account, which describes the
248    /// number of committed transactions signed by a given address. It is used to
249    /// prevent replay attacks.
250    pub sequence: u64,
251}
252
253/// ModeInfo describes the signing mode of a single or nested multisig signer.
254#[derive(Debug, Clone, PartialEq)]
255#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
256#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen"), wasm_bindgen)]
257pub struct ModeInfo {
258    /// sum is the oneof that specifies whether this represents a single or nested
259    /// multisig signer
260    #[cfg_attr(
261        all(target_arch = "wasm32", feature = "wasm-bindgen"),
262        wasm_bindgen(skip)
263    )]
264    pub sum: Sum,
265}
266
267#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
268#[wasm_bindgen]
269impl ModeInfo {
270    /// Return signature mode for the stored signature(s)
271    pub fn signature_mode(&self) -> SignatureMode {
272        match self.sum {
273            Sum::Single { .. } => SignatureMode::Single,
274            Sum::Multi { .. } => SignatureMode::Multi,
275        }
276    }
277
278    /// Single is the mode info for a single signer. It is structured as a message
279    /// to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the
280    /// future
281    #[wasm_bindgen(getter)]
282    pub fn mode(&self) -> Option<i32> {
283        match self.sum {
284            Sum::Single { mode } => Some(mode),
285            _ => None,
286        }
287    }
288
289    /// Multi is the mode info for a multisig public key
290    /// bitarray specifies which keys within the multisig are signing
291    #[wasm_bindgen(getter)]
292    pub fn bitarray(&self) -> Option<JsBitVector> {
293        match &self.sum {
294            Sum::Multi { bitarray, .. } => Some(bitarray.clone().into()),
295            Sum::Single { .. } => None,
296        }
297    }
298
299    /// Multi is the mode info for a multisig public key
300    /// mode_infos is the corresponding modes of the signers of the multisig
301    /// which could include nested multisig public keys
302    #[wasm_bindgen(getter)]
303    pub fn mode_infos(&self) -> Option<Vec<ModeInfo>> {
304        match &self.sum {
305            Sum::Multi { mode_infos, .. } => Some(mode_infos.clone()),
306            Sum::Single { .. } => None,
307        }
308    }
309}
310
311#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
312#[wasm_bindgen]
313pub enum SignatureMode {
314    Single,
315    Multi,
316}
317
318/// sum is the oneof that specifies whether this represents a single or nested
319/// multisig signer
320#[derive(Debug, Clone, PartialEq)]
321#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
322pub enum Sum {
323    /// Single is the mode info for a single signer. It is structured as a message
324    /// to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the
325    /// future
326    Single {
327        /// mode is the signing mode of the single signer
328        mode: i32,
329    },
330    /// Multi is the mode info for a multisig public key
331    Multi {
332        /// bitarray specifies which keys within the multisig are signing
333        bitarray: BitVector,
334        /// mode_infos is the corresponding modes of the signers of the multisig
335        /// which could include nested multisig public keys
336        mode_infos: Vec<ModeInfo>,
337    },
338}
339
340#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
341#[wasm_bindgen]
342impl Tx {
343    /// List of signatures that matches the length and order of [`AuthInfo`]’s `signer_info`s to
344    /// allow connecting signature meta information like public key and signing mode by position.
345    #[wasm_bindgen(getter)]
346    pub fn signatures(&self) -> Vec<JsSignature> {
347        self.signatures
348            .iter()
349            .map(|s| JsSignature::from(&s[..]))
350            .collect()
351    }
352}
353
354#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
355#[wasm_bindgen]
356impl TxBody {
357    /// `messages` is a list of messages to be executed. The required signers of
358    /// those messages define the number and order of elements in `AuthInfo`'s
359    /// signer_infos and Tx's signatures. Each required signer address is added to
360    /// the list only the first time it occurs.
361    ///
362    /// By convention, the first required signer (usually from the first message)
363    /// is referred to as the primary signer and pays the fee for the whole
364    /// transaction.
365    pub fn messages(&self) -> Vec<JsAny> {
366        self.messages.iter().cloned().map(Into::into).collect()
367    }
368
369    /// `timeout` is the block height after which this transaction will not
370    /// be processed by the chain
371    #[wasm_bindgen(getter)]
372    pub fn timeout_height(&self) -> u64 {
373        self.timeout_height.value()
374    }
375
376    /// `extension_options` are arbitrary options that can be added by chains
377    /// when the default options are not sufficient. If any of these are present
378    /// and can't be handled, the transaction will be rejected
379    pub fn extension_options(&self) -> Vec<JsAny> {
380        self.extension_options
381            .iter()
382            .cloned()
383            .map(Into::into)
384            .collect()
385    }
386
387    /// `extension_options` are arbitrary options that can be added by chains
388    /// when the default options are not sufficient. If any of these are present
389    /// and can't be handled, they will be ignored
390    pub fn non_critical_extension_options(&self) -> Vec<JsAny> {
391        self.non_critical_extension_options
392            .iter()
393            .cloned()
394            .map(Into::into)
395            .collect()
396    }
397}
398
399#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
400#[wasm_bindgen]
401impl SignerInfo {
402    /// public_key is the public key of the signer. It is optional for accounts
403    /// that already exist in state. If unset, the verifier can use the required \
404    /// signer address for this position and lookup the public key.
405    pub fn public_key(&self) -> Option<JsAny> {
406        self.public_key.clone().map(Into::into)
407    }
408}
409
410/// Fee includes the amount of coins paid in fees and the maximum
411/// gas to be used by the transaction. The ratio yields an effective "gasprice",
412/// which must be above some miminum to be accepted into the mempool.
413#[derive(Debug, Clone, PartialEq, Default)]
414#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
415#[cfg_attr(
416    all(target_arch = "wasm32", feature = "wasm-bindgen"),
417    wasm_bindgen(getter_with_clone)
418)]
419pub struct Fee {
420    /// amount is the amount of coins to be paid as a fee
421    pub amount: Vec<Coin>,
422    /// gas_limit is the maximum gas that can be used in transaction processing
423    /// before an out of gas error occurs
424    pub gas_limit: u64,
425    /// if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees.
426    /// the payer must be a tx signer (and thus have signed this field in AuthInfo).
427    /// setting this field does *not* change the ordering of required signers for the transaction.
428    #[cfg_attr(
429        all(target_arch = "wasm32", feature = "wasm-bindgen"),
430        wasm_bindgen(skip)
431    )]
432    pub payer: Option<Address>,
433    /// if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used
434    /// to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does
435    /// not support fee grants, this will fail
436    #[cfg_attr(
437        all(target_arch = "wasm32", feature = "wasm-bindgen"),
438        wasm_bindgen(skip)
439    )]
440    pub granter: Option<Address>,
441}
442
443impl Fee {
444    /// Create [`Fee`] struct with provided number of utia and gas limit,
445    /// without setting custom [`Fee::payer`] or [`Fee::granter`] fields,
446    /// which means first tx signer is responsible for paying.
447    pub fn new(utia_fee: u64, gas_limit: u64) -> Self {
448        Fee {
449            amount: vec![Coin::utia(utia_fee)],
450            gas_limit,
451            ..Default::default()
452        }
453    }
454}
455
456#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
457#[wasm_bindgen]
458impl Fee {
459    /// if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees.
460    /// the payer must be a tx signer (and thus have signed this field in AuthInfo).
461    /// setting this field does *not* change the ordering of required signers for the transaction.
462    #[wasm_bindgen(getter)]
463    pub fn payer(&self) -> Option<String> {
464        self.payer.as_ref().map(|a| a.to_string())
465    }
466    /// if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used
467    /// to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does
468    /// not support fee grants, this will fail
469    #[wasm_bindgen(getter)]
470    pub fn granter(&self) -> Option<String> {
471        self.granter.as_ref().map(|a| a.to_string())
472    }
473}
474
475/// Error codes associated with transaction responses.
476#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
477#[repr(u32)]
478#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
479#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen"), wasm_bindgen)]
480pub enum ErrorCode {
481    // source https://github.com/celestiaorg/cosmos-sdk/blob/v1.25.1-sdk-v0.46.16/types/errors/errors.go#L38
482    /// No error
483    Success = 0,
484    /// Cannot parse a transaction
485    TxDecode = 2,
486    /// Sequence number (nonce) is incorrect for the signature
487    InvalidSequence = 3,
488    /// Request without sufficient authorization is handled
489    Unauthorized = 4,
490    /// Account cannot pay requested amount
491    InsufficientFunds = 5,
492    /// Request is unknown
493    UnknownRequest = 6,
494    /// Address is invalid
495    InvalidAddress = 7,
496    /// Pubkey is invalid
497    InvalidPubKey = 8,
498    /// Address is unknown
499    UnknownAddress = 9,
500    /// Coin is invalid
501    InvalidCoins = 10,
502    /// Gas exceeded
503    OutOfGas = 11,
504    /// Memo too large
505    MemoTooLarge = 12,
506    /// Fee is insufficient
507    InsufficientFee = 13,
508    /// Too many signatures
509    TooManySignatures = 14,
510    /// No signatures in transaction
511    NoSignatures = 15,
512    /// Error converting to json
513    JSONMarshal = 16,
514    /// Error converting from json
515    JSONUnmarshal = 17,
516    /// Request contains invalid data
517    InvalidRequest = 18,
518    /// Tx already exists in the mempool
519    TxInMempoolCache = 19,
520    /// Mempool is full
521    MempoolIsFull = 20,
522    /// Tx is too large
523    TxTooLarge = 21,
524    /// Key doesn't exist
525    KeyNotFound = 22,
526    /// Key password is invalid
527    WrongPassword = 23,
528    /// Tx intended signer does not match the given signer
529    InvalidSigner = 24,
530    /// Invalid gas adjustment
531    InvalidGasAdjustment = 25,
532    /// Invalid height
533    InvalidHeight = 26,
534    /// Invalid version
535    InvalidVersion = 27,
536    /// Chain-id is invalid
537    InvalidChainID = 28,
538    /// Invalid type
539    InvalidType = 29,
540    /// Tx rejected due to an explicitly set timeout height
541    TxTimeoutHeight = 30,
542    /// Unknown extension options.
543    UnknownExtensionOptions = 31,
544    /// Account sequence defined in the signer info doesn't match the account's actual sequence
545    WrongSequence = 32,
546    /// Packing a protobuf message to Any failed
547    PackAny = 33,
548    /// Unpacking a protobuf message from Any failed
549    UnpackAny = 34,
550    /// Internal logic error, e.g. an invariant or assertion that is violated
551    Logic = 35,
552    /// Conflict error, e.g. when two goroutines try to access the same resource and one of them fails
553    Conflict = 36,
554    /// Called a branch of a code which is currently not supported
555    NotSupported = 37,
556    /// Requested entity doesn't exist in the state
557    NotFound = 38,
558    /// Internal errors caused by external operation
559    IO = 39,
560    /// Min-gas-prices field in BaseConfig is empty
561    AppConfig = 40,
562    /// Invalid GasWanted value is supplied
563    InvalidGasLimit = 41,
564    /// Node recovered from panic
565    Panic = 111222,
566
567    // source https://github.com/celestiaorg/celestia-app/blob/v3.0.2/x/blob/types/errors.go
568    /// cannot use reserved namespace IDs
569    ReservedNamespace = 11110,
570    /// invalid namespace length
571    InvalidNamespaceLen = 11111,
572    /// data must be multiple of shareSize
573    InvalidDataSize = 11112,
574    /// actual blob size differs from that specified in the MsgPayForBlob
575    BlobSizeMismatch = 11113,
576    /// committed to invalid square size: must be power of two
577    CommittedSquareSizeNotPowOf2 = 11114,
578    /// unexpected error calculating commitment for share
579    CalculateCommitment = 11115,
580    /// invalid commitment for share
581    InvalidShareCommitment = 11116,
582    /// cannot use parity shares namespace ID
583    ParitySharesNamespace = 11117,
584    /// cannot use tail padding namespace ID
585    TailPaddingNamespace = 11118,
586    /// cannot use transaction namespace ID
587    TxNamespace = 11119,
588    /// invalid share commitments: all relevant square sizes must be committed to
589    InvalidShareCommitments = 11122,
590    /// unsupported share version
591    UnsupportedShareVersion = 11123,
592    /// cannot use zero blob size
593    ZeroBlobSize = 11124,
594    /// mismatched number of blobs per MsgPayForBlob
595    MismatchedNumberOfPFBorBlob = 11125,
596    /// no MsgPayForBlobs found in blob transaction
597    NoPFB = 11126,
598    /// namespace of blob and its respective MsgPayForBlobs differ
599    NamespaceMismatch = 11127,
600    /// failure to parse a transaction from its protobuf representation
601    ProtoParsing = 11128,
602    /// not yet supported: multiple sdk.Msgs found in BlobTx
603    MultipleMsgsInBlobTx = 11129,
604    /// number of each component in a MsgPayForBlobs must be identical
605    MismatchedNumberOfPFBComponent = 11130,
606    /// no blobs provided
607    NoBlobs = 11131,
608    /// no namespaces provided
609    NoNamespaces = 11132,
610    /// no share versions provided
611    NoShareVersions = 11133,
612    /// no blob sizes provided
613    NoBlobSizes = 11134,
614    /// no share commitments provided
615    NoShareCommitments = 11135,
616    /// invalid namespace
617    InvalidNamespace = 11136,
618    /// invalid namespace version
619    InvalidNamespaceVersion = 11137,
620    /// total blob size too large
621    ///
622    /// TotalBlobSize is deprecated, use BlobsTooLarge instead.
623    TotalBlobSizeTooLarge = 11138,
624    /// blob(s) too large
625    BlobsTooLarge = 11139,
626    /// invalid blob signer
627    InvalidBlobSigner = 11140,
628}
629
630impl fmt::Display for ErrorCode {
631    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
632        write!(f, "{self:?}")
633    }
634}
635
636impl TryFrom<u32> for ErrorCode {
637    type Error = Error;
638
639    fn try_from(value: u32) -> Result<ErrorCode, Self::Error> {
640        let error_code = match value {
641            // cosmos-sdk
642            0 => ErrorCode::Success,
643            2 => ErrorCode::TxDecode,
644            3 => ErrorCode::InvalidSequence,
645            4 => ErrorCode::Unauthorized,
646            5 => ErrorCode::InsufficientFunds,
647            6 => ErrorCode::UnknownRequest,
648            7 => ErrorCode::InvalidAddress,
649            8 => ErrorCode::InvalidPubKey,
650            9 => ErrorCode::UnknownAddress,
651            10 => ErrorCode::InvalidCoins,
652            11 => ErrorCode::OutOfGas,
653            12 => ErrorCode::MemoTooLarge,
654            13 => ErrorCode::InsufficientFee,
655            14 => ErrorCode::TooManySignatures,
656            15 => ErrorCode::NoSignatures,
657            16 => ErrorCode::JSONMarshal,
658            17 => ErrorCode::JSONUnmarshal,
659            18 => ErrorCode::InvalidRequest,
660            19 => ErrorCode::TxInMempoolCache,
661            20 => ErrorCode::MempoolIsFull,
662            21 => ErrorCode::TxTooLarge,
663            22 => ErrorCode::KeyNotFound,
664            23 => ErrorCode::WrongPassword,
665            24 => ErrorCode::InvalidSigner,
666            25 => ErrorCode::InvalidGasAdjustment,
667            26 => ErrorCode::InvalidHeight,
668            27 => ErrorCode::InvalidVersion,
669            28 => ErrorCode::InvalidChainID,
670            29 => ErrorCode::InvalidType,
671            30 => ErrorCode::TxTimeoutHeight,
672            31 => ErrorCode::UnknownExtensionOptions,
673            32 => ErrorCode::WrongSequence,
674            33 => ErrorCode::PackAny,
675            34 => ErrorCode::UnpackAny,
676            35 => ErrorCode::Logic,
677            36 => ErrorCode::Conflict,
678            37 => ErrorCode::NotSupported,
679            38 => ErrorCode::NotFound,
680            39 => ErrorCode::IO,
681            40 => ErrorCode::AppConfig,
682            41 => ErrorCode::InvalidGasLimit,
683            111222 => ErrorCode::Panic,
684            // celestia-app blob
685            11110 => ErrorCode::ReservedNamespace,
686            11111 => ErrorCode::InvalidNamespaceLen,
687            11112 => ErrorCode::InvalidDataSize,
688            11113 => ErrorCode::BlobSizeMismatch,
689            11114 => ErrorCode::CommittedSquareSizeNotPowOf2,
690            11115 => ErrorCode::CalculateCommitment,
691            11116 => ErrorCode::InvalidShareCommitment,
692            11117 => ErrorCode::ParitySharesNamespace,
693            11118 => ErrorCode::TailPaddingNamespace,
694            11119 => ErrorCode::TxNamespace,
695            11122 => ErrorCode::InvalidShareCommitments,
696            11123 => ErrorCode::UnsupportedShareVersion,
697            11124 => ErrorCode::ZeroBlobSize,
698            11125 => ErrorCode::MismatchedNumberOfPFBorBlob,
699            11126 => ErrorCode::NoPFB,
700            11127 => ErrorCode::NamespaceMismatch,
701            11128 => ErrorCode::ProtoParsing,
702            11129 => ErrorCode::MultipleMsgsInBlobTx,
703            11130 => ErrorCode::MismatchedNumberOfPFBComponent,
704            11131 => ErrorCode::NoBlobs,
705            11132 => ErrorCode::NoNamespaces,
706            11133 => ErrorCode::NoShareVersions,
707            11134 => ErrorCode::NoBlobSizes,
708            11135 => ErrorCode::NoShareCommitments,
709            11136 => ErrorCode::InvalidNamespace,
710            11137 => ErrorCode::InvalidNamespaceVersion,
711            11138 => ErrorCode::TotalBlobSizeTooLarge,
712            11139 => ErrorCode::BlobsTooLarge,
713            11140 => ErrorCode::InvalidBlobSigner,
714            _ => bail_validation!("error code ({}) unknown", value),
715        };
716        Ok(error_code)
717    }
718}
719
720impl TryFrom<RawTxBody> for TxBody {
721    type Error = Error;
722
723    fn try_from(value: RawTxBody) -> Result<Self, Self::Error> {
724        Ok(TxBody {
725            messages: value.messages,
726            memo: value.memo,
727            timeout_height: value.timeout_height.try_into()?,
728            extension_options: value.extension_options,
729            non_critical_extension_options: value.non_critical_extension_options,
730        })
731    }
732}
733
734impl From<TxBody> for RawTxBody {
735    fn from(value: TxBody) -> Self {
736        RawTxBody {
737            messages: value.messages,
738            memo: value.memo,
739            timeout_height: value.timeout_height.into(),
740            extension_options: value.extension_options,
741            non_critical_extension_options: value.non_critical_extension_options,
742        }
743    }
744}
745
746impl TryFrom<RawAuthInfo> for AuthInfo {
747    type Error = Error;
748
749    fn try_from(value: RawAuthInfo) -> Result<Self, Self::Error> {
750        let signer_infos = value
751            .signer_infos
752            .into_iter()
753            .map(TryFrom::try_from)
754            .collect::<Result<_, Error>>()?;
755        Ok(AuthInfo {
756            signer_infos,
757            fee: value.fee.ok_or(Error::MissingFee)?.try_into()?,
758        })
759    }
760}
761
762impl From<AuthInfo> for RawAuthInfo {
763    fn from(value: AuthInfo) -> Self {
764        let signer_infos = value.signer_infos.into_iter().map(Into::into).collect();
765        #[allow(deprecated)] // tip is deprecated
766        RawAuthInfo {
767            signer_infos,
768            fee: Some(value.fee.into()),
769            tip: None,
770        }
771    }
772}
773
774impl TryFrom<RawTxResponse> for TxResponse {
775    type Error = Error;
776
777    fn try_from(response: RawTxResponse) -> Result<TxResponse, Self::Error> {
778        Ok(TxResponse {
779            height: response.height.try_into()?,
780            txhash: response.txhash.parse()?,
781            codespace: response.codespace,
782            code: response.code.try_into()?,
783            data: response.data,
784            raw_log: response.raw_log,
785            logs: response.logs,
786            info: response.info,
787            gas_wanted: response.gas_wanted,
788            gas_used: response.gas_used,
789            tx: response.tx,
790            timestamp: response.timestamp,
791            events: response.events,
792        })
793    }
794}
795
796impl TryFrom<RawSignerInfo> for SignerInfo {
797    type Error = Error;
798
799    fn try_from(value: RawSignerInfo) -> Result<Self, Self::Error> {
800        Ok(SignerInfo {
801            public_key: value.public_key,
802            mode_info: value.mode_info.ok_or(Error::MissingModeInfo)?.try_into()?,
803            sequence: value.sequence,
804        })
805    }
806}
807
808impl From<SignerInfo> for RawSignerInfo {
809    fn from(value: SignerInfo) -> Self {
810        RawSignerInfo {
811            public_key: value.public_key,
812            mode_info: Some(value.mode_info.into()),
813            sequence: value.sequence,
814        }
815    }
816}
817
818impl TryFrom<RawFee> for Fee {
819    type Error = Error;
820
821    fn try_from(value: RawFee) -> Result<Fee, Self::Error> {
822        let amount = value
823            .amount
824            .into_iter()
825            .map(TryInto::try_into)
826            .collect::<Result<_, Error>>()?;
827
828        Ok(Fee {
829            amount,
830            gas_limit: value.gas_limit,
831            payer: (!value.payer.is_empty())
832                .then(|| value.payer.parse())
833                .transpose()?,
834            granter: (!value.granter.is_empty())
835                .then(|| value.granter.parse())
836                .transpose()?,
837        })
838    }
839}
840
841impl From<Fee> for RawFee {
842    fn from(value: Fee) -> Self {
843        let amount = value.amount.into_iter().map(Into::into).collect();
844        RawFee {
845            amount,
846            gas_limit: value.gas_limit,
847            payer: value.payer.map(|acc| acc.to_string()).unwrap_or_default(),
848            granter: value.granter.map(|acc| acc.to_string()).unwrap_or_default(),
849        }
850    }
851}
852
853impl TryFrom<RawModeInfo> for ModeInfo {
854    type Error = Error;
855
856    fn try_from(value: RawModeInfo) -> Result<Self, Self::Error> {
857        Ok(ModeInfo {
858            sum: value.sum.ok_or(Error::MissingSum)?.try_into()?,
859        })
860    }
861}
862
863impl From<ModeInfo> for RawModeInfo {
864    fn from(value: ModeInfo) -> Self {
865        RawModeInfo {
866            sum: Some(value.sum.into()),
867        }
868    }
869}
870
871impl TryFrom<RawSum> for Sum {
872    type Error = Error;
873
874    fn try_from(value: RawSum) -> Result<Self, Self::Error> {
875        let sum = match value {
876            RawSum::Single(Single { mode }) => Sum::Single { mode },
877            RawSum::Multi(Multi {
878                bitarray,
879                mode_infos,
880            }) => {
881                let bitarray = bitarray.ok_or(Error::MissingBitarray)?.try_into()?;
882                let mode_infos = mode_infos
883                    .into_iter()
884                    .map(TryInto::try_into)
885                    .collect::<Result<_, Error>>()?;
886                Sum::Multi {
887                    bitarray,
888                    mode_infos,
889                }
890            }
891        };
892        Ok(sum)
893    }
894}
895
896impl From<Sum> for RawSum {
897    fn from(value: Sum) -> Self {
898        match value {
899            Sum::Single { mode } => RawSum::Single(Single { mode }),
900            Sum::Multi {
901                bitarray,
902                mode_infos,
903            } => {
904                let mode_infos = mode_infos.into_iter().map(Into::into).collect();
905                RawSum::Multi(Multi {
906                    bitarray: Some(bitarray.into()),
907                    mode_infos,
908                })
909            }
910        }
911    }
912}
913
914#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
915pub use wbg::*;
916
917#[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))]
918mod wbg {
919    use tendermint_proto::v0_38::abci::{Event, EventAttribute};
920    use wasm_bindgen::prelude::*;
921
922    /// Event allows application developers to attach additional information to
923    /// ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx.
924    /// Later, transactions may be queried using these events.
925    #[derive(Clone)]
926    #[wasm_bindgen(getter_with_clone)]
927    pub struct JsEvent {
928        pub r#type: String,
929        pub attributes: Vec<JsEventAttribute>,
930    }
931
932    impl From<Event> for JsEvent {
933        fn from(value: Event) -> Self {
934            JsEvent {
935                r#type: value.r#type,
936                attributes: value.attributes.into_iter().map(Into::into).collect(),
937            }
938        }
939    }
940
941    #[derive(Clone)]
942    #[wasm_bindgen(getter_with_clone)]
943    pub struct JsEventAttribute {
944        pub key: String,
945        pub value: String,
946        pub index: bool,
947    }
948
949    /// EventAttribute is a single key-value pair, associated with an event.
950    impl From<EventAttribute> for JsEventAttribute {
951        fn from(value: EventAttribute) -> Self {
952            JsEventAttribute {
953                key: value.key,
954                value: value.value,
955                index: value.index,
956            }
957        }
958    }
959}
960
961impl Protobuf<RawTxBody> for TxBody {}
962impl Protobuf<RawAuthInfo> for AuthInfo {}
963
964#[cfg(feature = "uniffi")]
965mod uniffi_types {
966    use tendermint_proto::v0_38::abci::{Event, EventAttribute};
967
968    #[uniffi::remote(Record)]
969    pub struct Event {
970        pub r#type: String,
971        pub attributes: Vec<EventAttribute>,
972    }
973
974    #[uniffi::remote(Record)]
975    pub struct EventAttribute {
976        pub key: String,
977        pub value: String,
978        pub index: bool,
979    }
980}