Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added injective-std-derive #232

Merged
merged 6 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
599 changes: 551 additions & 48 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/injective-protobuf/src/proto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// @generated

pub mod account;
pub mod auth;
pub mod coin;
pub mod distribution;
pub mod exchange;
pub mod oracle;
pub mod tx;
pub mod oracle;
pub mod account;
24 changes: 24 additions & 0 deletions packages/injective-std-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
description = "Procedural macro for injective-std"
edition = "2021"
license = "MIT OR Apache-2.0"
name = "injective-std-derive"
version = "1.13.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
proc-macro = true

[dependencies]
cosmwasm-std = { version = "2.1.0", features = [ "stargate" ] }
itertools = "0.10.3"
proc-macro2 = "1.0.40"
quote = "1.0.20"
syn = "1.0.98"

[dev-dependencies]
cosmwasm-std = { version = "2.1.0", features = [ "stargate" ] }
prost = "0.12.4"
serde = "1.0.142"
trybuild = { version = "1.0.63", features = [ "diff" ] }
4 changes: 4 additions & 0 deletions packages/injective-std-derive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# injective-std-derive

Procedural macro for augmenting proto-generated types to create better developer ergonomics.
Internally used by `injective-std`, developed from `osmosis-std-derive`.
193 changes: 193 additions & 0 deletions packages/injective-std-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
use itertools::Itertools;
use proc_macro::TokenStream;
use proc_macro2::TokenTree;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

macro_rules! match_kv_attr {
($key:expr, $value_type:tt) => {
|tt| {
if let [TokenTree::Ident(key), TokenTree::Punct(eq), TokenTree::$value_type(value)] =
&tt[..]
{
if (key == $key) && (eq.as_char() == '=') {
Some(quote!(#value))
} else {
None
}
} else {
None
}
}
};
}

#[proc_macro_derive(CosmwasmExt, attributes(proto_message, proto_query))]
pub fn derive_cosmwasm_ext(input: TokenStream) -> TokenStream {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

brake into smaller functions could make sense

let input = parse_macro_input!(input as DeriveInput);
let ident = input.ident;

let type_url = get_type_url(&input.attrs);

// `EncodeError` always indicates that a message failed to encode because the
// provided buffer had insufficient capacity. Message encoding is otherwise
// infallible.

let (query_request_conversion, cosmwasm_query) = if get_attr("proto_query", &input.attrs).is_some() {
let path = get_query_attrs(&input.attrs, match_kv_attr!("path", Literal));
let res = get_query_attrs(&input.attrs, match_kv_attr!("response_type", Ident));

let query_request_conversion = quote! {
impl <Q: cosmwasm_std::CustomQuery> From<#ident> for cosmwasm_std::QueryRequest<Q> {
fn from(msg: #ident) -> Self {
cosmwasm_std::QueryRequest::<Q>::Stargate {
path: #path.to_string(),
data: msg.into(),
}
}
}
};

let cosmwasm_query = quote! {
pub fn query(self, querier: &cosmwasm_std::QuerierWrapper<impl cosmwasm_std::CustomQuery>) -> cosmwasm_std::StdResult<#res> {
querier.query::<#res>(&self.into())
}
};

(query_request_conversion, cosmwasm_query)
} else {
(quote!(), quote!())
};

(quote! {
impl #ident {
pub const TYPE_URL: &'static str = #type_url;
#cosmwasm_query
}

#query_request_conversion

impl From<#ident> for cosmwasm_std::Binary {
fn from(msg: #ident) -> Self {
let mut bytes = Vec::new();
prost::Message::encode(&msg, &mut bytes)
.expect("Message encoding must be infallible");

cosmwasm_std::Binary::new(bytes)
}
}

impl<T> From<#ident> for cosmwasm_std::CosmosMsg<T> {
fn from(msg: #ident) -> Self {
cosmwasm_std::CosmosMsg::<T>::Stargate {
type_url: #type_url.to_string(),
value: msg.into(),
}
}
}

impl TryFrom<cosmwasm_std::Binary> for #ident {
type Error = cosmwasm_std::StdError;

fn try_from(binary: cosmwasm_std::Binary) -> Result<Self, Self::Error> {
use ::prost::Message;
Self::decode(&binary[..]).map_err(|e| {
cosmwasm_std::StdError::parse_err(
stringify!(#ident).to_string(),
format!(
"Unable to decode binary: \n - base64: {}\n - bytes array: {:?}\n\n{:?}",
binary,
binary.to_vec(),
e
),
)
})
}
}

impl TryFrom<cosmwasm_std::SubMsgResult> for #ident {
type Error = cosmwasm_std::StdError;

fn try_from(result: cosmwasm_std::SubMsgResult) -> Result<Self, Self::Error> {
result
.into_result()
.map_err(|e| cosmwasm_std::StdError::generic_err(e))?
.data
.ok_or_else(|| cosmwasm_std::StdError::not_found(
"cosmwasm_std::SubMsgResult::<T>".to_string()
))?
.try_into()
}
}
})
.into()
}

fn get_type_url(attrs: &[syn::Attribute]) -> proc_macro2::TokenStream {
let proto_message = get_attr("proto_message", attrs).and_then(|a| a.parse_meta().ok());

if let Some(syn::Meta::List(meta)) = proto_message.clone() {
match meta.nested[0].clone() {
syn::NestedMeta::Meta(syn::Meta::NameValue(meta)) => {
if meta.path.is_ident("type_url") {
match meta.lit {
syn::Lit::Str(s) => quote!(#s),
_ => proto_message_attr_error(meta.lit),
}
} else {
proto_message_attr_error(meta.path)
}
}
t => proto_message_attr_error(t),
}
} else {
proto_message_attr_error(proto_message)
}
}

fn get_query_attrs<F>(attrs: &[syn::Attribute], f: F) -> proc_macro2::TokenStream
where
F: FnMut(&Vec<TokenTree>) -> Option<proc_macro2::TokenStream>,
{
let proto_query = get_attr("proto_query", attrs);

if let Some(attr) = proto_query {
if attr.tokens.clone().into_iter().count() != 1 {
return proto_query_attr_error(proto_query);
}

if let Some(TokenTree::Group(group)) = attr.tokens.clone().into_iter().next() {
let kv_groups = group
.stream()
.into_iter()
.group_by(|t| if let TokenTree::Punct(punct) = t { punct.as_char() != ',' } else { true });
let mut key_values: Vec<Vec<TokenTree>> = vec![];

for (non_sep, g) in &kv_groups {
if non_sep {
key_values.push(g.collect());
}
}

return key_values.iter().find_map(f).unwrap_or_else(|| proto_query_attr_error(proto_query));
}

proto_query_attr_error(proto_query)
} else {
proto_query_attr_error(proto_query)
}
}

fn get_attr<'a>(attr_ident: &str, attrs: &'a [syn::Attribute]) -> Option<&'a syn::Attribute> {
attrs
.iter()
.find(|&attr| attr.path.segments.len() == 1 && attr.path.segments[0].ident == attr_ident)
}

fn proto_message_attr_error<T: quote::ToTokens>(tokens: T) -> proc_macro2::TokenStream {
syn::Error::new_spanned(tokens, "expected `proto_message(type_url = \"...\")`").to_compile_error()
}

fn proto_query_attr_error<T: quote::ToTokens>(tokens: T) -> proc_macro2::TokenStream {
syn::Error::new_spanned(tokens, "expected `proto_query(path = \"...\", response_type = ...)`").to_compile_error()
}
6 changes: 6 additions & 0 deletions packages/injective-std-derive/tests/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#[test]
fn tests() {
let t = trybuild::TestCases::new();
t.pass("tests/struct.rs");
t.pass("tests/query.rs");
}
26 changes: 26 additions & 0 deletions packages/injective-std-derive/tests/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use cosmwasm_std::{Empty, QueryRequest};
use injective_std_derive::CosmwasmExt;

#[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize, CosmwasmExt)]
#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorRequest")]
#[proto_query(
path = "/osmosis.tokenfactory.v1beta1.Query/DenomsFromCreator",
response_type = QueryDenomsFromCreatorResponse
)]
pub struct QueryDenomsFromCreatorRequest {
#[prost(string, tag = "1")]
pub creator: ::prost::alloc::string::String,
}
#[derive(Clone, PartialEq, Eq, ::prost::Message, serde::Serialize, serde::Deserialize, CosmwasmExt)]
#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorResponse")]
pub struct QueryDenomsFromCreatorResponse {
#[prost(string, repeated, tag = "1")]
pub denoms: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
}

fn main() {
let _: QueryRequest<Empty> = QueryDenomsFromCreatorRequest {
creator: "osmo1sr9zm2pq3xrru7l7gz632t2rqs9caet9xulwvapcqagq9pytkcgqwfc3nk".to_string(),
}
.into();
}
22 changes: 22 additions & 0 deletions packages/injective-std-derive/tests/struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use cosmwasm_std::CosmosMsg;
use injective_std_derive::CosmwasmExt;

#[derive(Clone, PartialEq, Eq, ::prost::Message, CosmwasmExt)]
#[proto_message(type_url = "/osmosis.tokenfactory.v1beta1.MsgCreateDenom")]
pub struct MsgCreateDenom {
#[prost(string, tag = "1")]
pub sender: ::prost::alloc::string::String,
/// subdenom can be up to 44 "alphanumeric" characters long.
#[prost(string, tag = "2")]
pub subdenom: ::prost::alloc::string::String,
}

fn main() {
assert_eq!(MsgCreateDenom::TYPE_URL, "/osmosis.tokenfactory.v1beta1.MsgCreateDenom");
let msg = MsgCreateDenom {
sender: "osmo1sr9zm2pq3xrru7l7gz632t2rqs9caet9xulwvapcqagq9pytkcgqwfc3nk".to_string(),
subdenom: "uxxx".to_string(),
};

let _: CosmosMsg = msg.into();
}
18 changes: 9 additions & 9 deletions packages/injective-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ license = "MIT OR Apache-2.0"
name = "injective-std"
readme = "README.md"
repository = "https://github.com/InjectiveLabs/cw-injective/tree/dev/packages/injective-std"
version = "1.13.2-testnet"
version = "1.13.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
chrono = { version = "0.4.27", default-features = false }
cosmwasm-std = { version = "1.5.0", features = [ "abort", "cosmwasm_1_2", "cosmwasm_1_3", "cosmwasm_1_4", "iterator", "stargate" ] }
osmosis-std-derive = { version = "0.20.1" }
prost = { version = "0.12.4", features = [ "prost-derive" ] }
prost-types = { version = "0.12.4", default-features = false }
schemars = "0.8.8"
serde = { version = "1.0", default-features = false, features = [ "derive" ] }
serde-cw-value = { version = "0.7.0" }
chrono = { version = "0.4.27", default-features = false }
cosmwasm-std = { version = "2.1.0", features = [ "abort", "cosmwasm_1_2", "cosmwasm_1_3", "cosmwasm_1_4", "cosmwasm_2_0", "iterator", "stargate" ] }
injective-std-derive = { version = "1.13.0" }
prost = { version = "0.12.4", features = [ "prost-derive" ] }
prost-types = { version = "0.12.4", default-features = false }
schemars = "0.8.8"
serde = { version = "1.0", default-features = false, features = [ "derive" ] }
serde-cw-value = { version = "0.7.0" }
2 changes: 1 addition & 1 deletion packages/injective-std/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Injective's proto-generated types and helpers built using [Buf](https://github.c

## Supported Version

- Injective-Core@aa61552b5225f76f284811004925d83531e23311
- Injective-Core@74af4ec3eceea1e7a59e2ae57fe848ae02d2f34f

## Build Instructions

Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/auth/module/v1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Module is the config object for the auth module.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/auth/v1beta1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// BaseAccount defines a base account type. It contains all the necessary fields
/// for basic account functionality. Any custom account type should extend this
/// type for additional functionality (e.g. vesting).
Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/authz/module/v1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Module is the config object of the authz module.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/authz/v1beta1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// GenericAuthorization gives the grantee unrestricted permissions to execute
/// the provided method on behalf of the granter's account.
#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/bank/module/v1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Module is the config object of the bank module.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/bank/v1beta1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// SendAuthorization allows the grantee to spend up to spend_limit coins from
/// the granter's account.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// ConfigRequest defines the request structure for the Config gRPC query.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// PageRequest is to be embedded in gRPC request messages for efficient
/// pagination. Ex:
///
Expand Down
2 changes: 1 addition & 1 deletion packages/injective-std/src/types/cosmos/base/v1beta1.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Coin defines a token with a denomination and an amount.
///
/// NOTE: The amount field is an Int which implements the custom method
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Module is the config object of the distribution module.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Params defines the set of params for the distribution module.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use osmosis_std_derive::CosmwasmExt;
use injective_std_derive::CosmwasmExt;
/// Module is the config object of the feegrant module.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, Eq, ::prost::Message, ::serde::Serialize, ::serde::Deserialize, ::schemars::JsonSchema, CosmwasmExt)]
Expand Down
Loading
Loading