diff --git a/Cargo.lock b/Cargo.lock index 68dfd003f03..4c227166cd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6397,6 +6397,7 @@ dependencies = [ "cosmwasm-std 2.1.4", "cw-orch", "ibc-solidity", + "schemars", "serde", "unionlabs-primitives", ] @@ -12979,6 +12980,7 @@ dependencies = [ "hex", "primitive-types 0.12.2", "rlp", + "schemars", "serde", "serde_bytes", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 33cd8823bb1..1f3f46e2488 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -338,6 +338,8 @@ tracing = { version = "0.1.40", default-features = false } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt", "ansi"] } typenum = { version = "1.17.0", default-features = false } +cw-orch = "0.27.0" + [patch."crates-io"] arbitrary = { git = "https://github.com/unionlabs/arbitrary" } # parity-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" } diff --git a/cosmwasm/ibc-union/core/Cargo.toml b/cosmwasm/ibc-union/core/Cargo.toml index dde5974707b..7b33a8d1425 100644 --- a/cosmwasm/ibc-union/core/Cargo.toml +++ b/cosmwasm/ibc-union/core/Cargo.toml @@ -13,7 +13,8 @@ workspace = true crate-type = ["cdylib", "rlib"] [features] -library = [] +cw-orch-interface = ["dep:cosmwasm-schema", "dep:cw-orch"] +library = [] [dependencies] alloy = { workspace = true, features = ["sol-types"] } @@ -28,5 +29,6 @@ serde_json = { workspace = true } strum = { version = "0.26.3", features = ["derive"] } thiserror = { workspace = true } unionlabs = { workspace = true, features = ["ethabi"] } -cw-orch = "0.27.0" -cosmwasm-schema = { workspace = true } \ No newline at end of file + +cosmwasm-schema = { workspace = true, optional = true } +cw-orch = { workspace = true, optional = true } diff --git a/cosmwasm/ibc-union/core/msg/Cargo.toml b/cosmwasm/ibc-union/core/msg/Cargo.toml index 0eed3d6bf1c..42bcd7f8f04 100644 --- a/cosmwasm/ibc-union/core/msg/Cargo.toml +++ b/cosmwasm/ibc-union/core/msg/Cargo.toml @@ -9,10 +9,24 @@ version = "1.0.0" [lints] workspace = true +[features] +cw-orch-interface = [ + "dep:cw-orch", + "dep:schemars", + "dep:cosmwasm-std", + "dep:cosmwasm-schema", + "unionlabs-primitives/schemars", + "ibc-solidity/schemars", +] +schemars = [] + [dependencies] ibc-solidity = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } unionlabs-primitives = { workspace = true, features = ["serde"] } -cosmwasm-std = { workspace = true } -cosmwasm-schema = { workspace = true } -cw-orch = "0.27.0" + + +cosmwasm-schema = { workspace = true, optional = true } +cosmwasm-std = { workspace = true, optional = true } +cw-orch = { workspace = true, optional = true } +schemars = { workspace = true, optional = true } diff --git a/cosmwasm/ibc-union/core/msg/src/lightclient.rs b/cosmwasm/ibc-union/core/msg/src/lightclient.rs index 9924b145c33..f1727d63e7f 100644 --- a/cosmwasm/ibc-union/core/msg/src/lightclient.rs +++ b/cosmwasm/ibc-union/core/msg/src/lightclient.rs @@ -1,6 +1,7 @@ use unionlabs_primitives::Bytes; #[derive(serde::Serialize, serde::Deserialize, Debug)] +#[cfg_attr(feature = "cw-orch-interface", derive(schemars::JsonSchema))] #[serde(deny_unknown_fields, rename_all = "snake_case")] pub enum Status { Active, diff --git a/cosmwasm/ibc-union/core/msg/src/msg.rs b/cosmwasm/ibc-union/core/msg/src/msg.rs index b59de420a02..9a77da44ad0 100644 --- a/cosmwasm/ibc-union/core/msg/src/msg.rs +++ b/cosmwasm/ibc-union/core/msg/src/msg.rs @@ -14,7 +14,7 @@ pub struct MsgRegisterClient { #[derive(Debug, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, rename_all = "snake_case")] -#[derive(cw_orch::ExecuteFns)] // cw-orch automatic +#[cfg_attr(feature = "cw-orch-interface", derive(cw_orch::ExecuteFns))] pub enum ExecuteMsg { RegisterClient(MsgRegisterClient), CreateClient(MsgCreateClient), diff --git a/cosmwasm/ibc-union/core/msg/src/query.rs b/cosmwasm/ibc-union/core/msg/src/query.rs index 2fbf686339b..ba916c40c8c 100644 --- a/cosmwasm/ibc-union/core/msg/src/query.rs +++ b/cosmwasm/ibc-union/core/msg/src/query.rs @@ -2,28 +2,35 @@ use unionlabs_primitives::H256; #[derive(serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields, rename_all = "snake_case")] -#[derive(cosmwasm_schema::QueryResponses, cw_orch::QueryFns)] +#[cfg_attr( + feature = "cw-orch-interface", + derive( + cosmwasm_schema::QueryResponses, + cw_orch::QueryFns, + schemars::JsonSchema + ) +)] pub enum QueryMsg { - #[returns(u64)] + #[cfg_attr(feature = "cw-orch-interface", returns(u64))] GetTimestampAtHeight { client_id: u32, height: u64 }, - #[returns(u64)] + #[cfg_attr(feature = "cw-orch-interface", returns(u64))] GetLatestHeight { client_id: u32 }, - #[returns(cosmwasm_std::Binary)] + #[cfg_attr(feature = "cw-orch-interface", returns(cosmwasm_std::Binary))] GetClientState { client_id: u32 }, - #[returns(cosmwasm_std::Binary)] + #[cfg_attr(feature = "cw-orch-interface", returns(cosmwasm_std::Binary))] GetConsensusState { client_id: u32, height: u64 }, - #[returns(crate::lightclient::Status)] + #[cfg_attr(feature = "cw-orch-interface", returns(crate::lightclient::Status))] GetStatus { client_id: u32 }, - #[returns(u64)] + #[cfg_attr(feature = "cw-orch-interface", returns(u64))] GetClientType { client_id: u32 }, - #[returns(ibc_solidity::Connection)] + #[cfg_attr(feature = "cw-orch-interface", returns(ibc_solidity::Connection))] GetConnection { connection_id: u32 }, - #[returns(ibc_solidity::Channel)] + #[cfg_attr(feature = "cw-orch-interface", returns(ibc_solidity::Channel))] GetChannel { channel_id: u32 }, - #[returns(BTreeSet)] + #[cfg_attr(feature = "cw-orch-interface", returns(std::collections::BTreeSet))] GetChannels { contract: String }, - #[returns(Option)] + #[cfg_attr(feature = "cw-orch-interface", returns(Option>))] GetBatchPackets { channel_id: u32, batch_hash: H256 }, - #[returns(Option)] + #[cfg_attr(feature = "cw-orch-interface", returns(Option>))] GetBatchReceipts { channel_id: u32, batch_hash: H256 }, } diff --git a/cosmwasm/ibc-union/core/src/lib.rs b/cosmwasm/ibc-union/core/src/lib.rs index 2fda1a13d57..e87758e22c5 100644 --- a/cosmwasm/ibc-union/core/src/lib.rs +++ b/cosmwasm/ibc-union/core/src/lib.rs @@ -1,8 +1,9 @@ #![cfg_attr(not(test), warn(clippy::unwrap_used))] pub mod contract; -pub mod state; +#[cfg(feature = "cw-orch-interface")] pub mod interface; +pub mod state; use cosmwasm_std::{Addr, StdError}; use ibc_solidity::{ChannelState, ConnectionState}; diff --git a/lib/ibc-solidity/src/lib.rs b/lib/ibc-solidity/src/lib.rs index e6982ee0392..b602f955e20 100644 --- a/lib/ibc-solidity/src/lib.rs +++ b/lib/ibc-solidity/src/lib.rs @@ -344,6 +344,9 @@ maybe_sol_attr! { feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(deny_unknown_fields) )] + #[cfg_attr( + feature = "schemars", derive(schemars::JsonSchema), + )] enum ConnectionState { Unspecified, Init, @@ -355,6 +358,9 @@ maybe_sol_attr! { feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(deny_unknown_fields) )] + #[cfg_attr( + feature = "schemars", derive(schemars::JsonSchema), + )] struct Connection { ConnectionState state; uint32 client_id; @@ -366,6 +372,9 @@ maybe_sol_attr! { feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(deny_unknown_fields) )] + #[cfg_attr( + feature = "schemars", derive(schemars::JsonSchema), + )] enum ChannelState { Unspecified, Init, @@ -613,6 +622,64 @@ maybe_sol_attr! { } } +#[cfg(feature = "schemars")] +/// We need a custom implementation because alloy::Bytes doesn't implement JsonSchema +impl schemars::JsonSchema for Channel { + fn schema_name() -> String { + "Channel".to_string() + } + fn schema_id() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed("ibc_solidity::Channel") + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + let mut schema_object = schemars::schema::SchemaObject { + instance_type: Some(schemars::schema::InstanceType::Object.into()), + ..Default::default() + }; + let object_validation = schema_object.object(); + { + schemars::_private::insert_object_property::( + object_validation, + "state", + false, + true, + gen.subschema_for::(), + ); + schemars::_private::insert_object_property::( + object_validation, + "connection_id", + false, + true, + gen.subschema_for::(), + ); + schemars::_private::insert_object_property::( + object_validation, + "counterparty_channel_id", + false, + true, + gen.subschema_for::(), + ); + schemars::_private::insert_object_property::>( + object_validation, + "counterparty_port_id", + false, + true, + gen.subschema_for::>(), + ); + schemars::_private::insert_object_property::( + object_validation, + "version", + false, + true, + gen.subschema_for::(), + ); + } + + schemars::schema::Schema::Object(schema_object) + } +} + impl Clone for Ibc::IbcEvents { fn clone(&self) -> Self { match self { diff --git a/lib/unionlabs-primitives/Cargo.toml b/lib/unionlabs-primitives/Cargo.toml index a2f1e3f6689..803f449f404 100644 --- a/lib/unionlabs-primitives/Cargo.toml +++ b/lib/unionlabs-primitives/Cargo.toml @@ -13,6 +13,7 @@ generic-array = { workspace = true, optional = true } hex = { workspace = true, features = ["std"] } primitive-types = { workspace = true, optional = true } rlp = { workspace = true, optional = true } +schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"], optional = true } serde_bytes = { version = "0.11.15", optional = true } thiserror = { workspace = true } @@ -24,10 +25,11 @@ workspace = true [features] default = ["serde", "base64"] -base64 = ["dep:base64"] -bincode = ["dep:bincode"] -rlp = ["dep:rlp"] -serde = ["dep:serde", "dep:serde_bytes"] +base64 = ["dep:base64"] +bincode = ["dep:bincode"] +rlp = ["dep:rlp"] +schemars = ["dep:schemars"] +serde = ["dep:serde", "dep:serde_bytes"] alloy-primitives-compat = ["dep:alloy-primitives"] generic-array-compat = ["dep:generic-array", "dep:typenum", "typenum/const-generics"] diff --git a/lib/unionlabs-primitives/src/fixed_bytes.rs b/lib/unionlabs-primitives/src/fixed_bytes.rs index ded92639e52..6a3602c9149 100644 --- a/lib/unionlabs-primitives/src/fixed_bytes.rs +++ b/lib/unionlabs-primitives/src/fixed_bytes.rs @@ -236,6 +236,32 @@ impl<'de, const BYTES: usize, E: Encoding> serde::Deserialize<'de> for FixedByte } } +#[cfg(feature = "schemars")] +impl schemars::JsonSchema for FixedBytes { + fn schema_name() -> String { + "Hash".to_string() + } + + /// The `FixedBytes` object is serialized as an array in JSON + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + let mut schema_object = schemars::schema::SchemaObject { + instance_type: Some(schemars::schema::InstanceType::Array.into()), + ..Default::default() + }; + + schema_object.array = Some(Box::new(schemars::schema::ArrayValidation { + items: Some(schemars::schema::SingleOrVec::Single(Box::new( + gen.subschema_for::(), + ))), + min_items: Some(BYTES.try_into().unwrap()), + max_items: Some(BYTES.try_into().unwrap()), + ..Default::default() + })); + + schemars::schema::Schema::Object(schema_object) + } +} + impl FromStr for FixedBytes { type Err = E::Error;