nibiru_std/proto/
traits.rs

1//! nibiru-std::proto - traits.rs : Implements extensions for prost::Message
2//! types for easy conversion to types needed for CosmWasm smart contracts.
3
4// Allow deprecated variant `cosmwasm_std::CosmosMsg::Stargate` for compatibility
5// with CosmWasm v1. Once we upgrade everything to v2 on Nibiru, we can remove
6// this deprecate statement.
7#![allow(deprecated)]
8// TODO: remove allow(deprevated) ↑
9
10use cosmwasm_std::{Binary, CosmosMsg, QueryRequest};
11
12use crate::errors::{NibiruError, NibiruResult};
13
14use crate::proto::cosmos;
15
16pub trait NibiruProstMsg: prost::Message {
17    /// Serialize this protobuf message as a byte vector
18    fn to_bytes(&self) -> Vec<u8>;
19    fn to_binary(&self) -> Binary;
20    /// A type implementing prost::Message is not guaranteed to implement
21    /// prost::Name and have a `Name.type_url()` function. This method attempts
22    /// to downcast the message to prost::Name, and if successful, constructs a
23    /// `CosmosMsg::Stargate` object corresponding to the type.
24    fn try_into_stargate_msg(&self, type_url: &str) -> CosmosMsg {
25        let value = self.to_binary();
26        CosmosMsg::Stargate {
27            type_url: type_url.to_string(),
28            value,
29        }
30    }
31
32    /// Parse into this protobuf type from `prost_types::Any`.
33    fn from_any(any: &prost_types::Any) -> Result<Self, prost::DecodeError>
34    where
35        Self: Default + prost::Name + Sized,
36    {
37        any.to_msg()
38    }
39}
40
41impl<M> NibiruProstMsg for M
42where
43    M: prost::Message,
44{
45    fn to_bytes(&self) -> Vec<u8> {
46        self.encode_to_vec()
47    }
48
49    fn to_binary(&self) -> Binary {
50        Binary::from(self.encode_to_vec())
51    }
52}
53
54pub trait NibiruStargateMsg: prost::Message + prost::Name {
55    #![allow(clippy::wrong_self_convention)]
56    fn into_stargate_msg(&self) -> CosmosMsg;
57
58    fn type_url(&self) -> String;
59}
60
61impl<M> NibiruStargateMsg for M
62where
63    M: prost::Message + prost::Name,
64{
65    /// Returns the `prost::Message` as a `CosmosMsg::Stargate` object.
66    fn into_stargate_msg(&self) -> CosmosMsg {
67        CosmosMsg::Stargate {
68            type_url: self.type_url(),
69            value: self.to_binary(),
70        }
71    }
72
73    /// The "type URL" in the context of protobuf is used with a feature
74    /// called "Any", a type that allows one to serialize and embed proto
75    /// message (prost::Message) objects without as opaque values without having
76    /// to predefine the type in the original message declaration.
77    ///
78    /// For example, a protobuf definition like:
79    /// ```proto
80    /// message CustomProtoMsg { string name = 1; }
81    /// ```
82    /// might have a type URL like "googleapis.com/package.name.CustomProtoMsg".
83    /// Usage of `Any` with type URLs enables dynamic message composition and
84    /// flexibility.
85    ///
86    /// We use these type URLs in CosmWasm and the Cosmos-SDK to classify
87    /// gRPC messages for transactions and queries because Tendermint ABCI
88    /// messages are protobuf objects.
89    fn type_url(&self) -> String {
90        format!("/{}.{}", Self::PACKAGE, Self::NAME)
91    }
92}
93
94pub trait NibiruStargateQuery: prost::Message + prost::Name {
95    #![allow(clippy::wrong_self_convention)]
96    fn into_stargate_query(
97        &self,
98    ) -> NibiruResult<QueryRequest<cosmwasm_std::Empty>>;
99
100    fn path(&self) -> String;
101}
102
103impl<M> NibiruStargateQuery for M
104where
105    M: prost::Message + prost::Name,
106{
107    /// Returns the `prost::Message` as a `QueryRequest::Stargate` object.
108    /// Errors if the `prost::Name::type_url` does not indicate the type is a
109    /// query.
110    fn into_stargate_query(
111        &self,
112    ) -> NibiruResult<QueryRequest<cosmwasm_std::Empty>> {
113        if !self.type_url().contains("Query") {
114            return Err(NibiruError::ProstNameisNotQuery {
115                type_url: self.type_url(),
116            });
117        }
118        Ok(QueryRequest::Stargate {
119            path: self.path(),
120            data: self.to_binary(),
121        })
122    }
123
124    /// Fully qualified gRPC service path used for routing.
125    /// Ex.: "/cosmos.bank.v1beta1.Query/SupplyOf"
126    fn path(&self) -> String {
127        let service_name = format!(
128            "Query/{}",
129            Self::NAME
130                .trim_start_matches("Query")
131                .trim_end_matches("Request")
132        );
133        format!("/{}.{}", Self::PACKAGE, service_name)
134    }
135}
136
137impl From<cosmwasm_std::Coin> for cosmos::base::v1beta1::Coin {
138    fn from(cw_coin: cosmwasm_std::Coin) -> Self {
139        cosmos::base::v1beta1::Coin {
140            denom: cw_coin.denom,
141            amount: cw_coin.amount.to_string(),
142        }
143    }
144}