From 29d17bcbaeaca90cd1c0d23273d9f7565586ce49 Mon Sep 17 00:00:00 2001 From: Tomasz Kulik Date: Wed, 10 Jul 2024 09:08:22 +0200 Subject: [PATCH] docs: Update generics module --- src/advanced/custom.md | 7 ---- src/advanced/generics.md | 72 ++++++++++++++-------------------------- 2 files changed, 25 insertions(+), 54 deletions(-) diff --git a/src/advanced/custom.md b/src/advanced/custom.md index 4aab35b..9043947 100644 --- a/src/advanced/custom.md +++ b/src/advanced/custom.md @@ -1,7 +1,5 @@ # Custom messages -*Since version 0.8.0.* - Blockchain creators might define chain-specific logic triggered through defined by them messages. `CosmWasm` provides a way to send such messages through `cosmwasm_std::CosmosMsg::Custom(..)` variant. @@ -146,8 +144,6 @@ Now that we have defined the interfaces, we can implement them on the contract. cast `Response` to `Response` or `Deps` to `Deps`, implementation of the custom interface on non-custom contracts is not possible. It is possible, however, to implement a non-custom interface on a custom contract. -Implementation of chain-specific custom interfaces is simple. We have to pass the `sv::custom(..)` attribute once again. - To implement the interface with the associated type, we have to assign types for them. Because the type of `ExecC` and `QueryC` is defined by the user, the interface is reusable in the context of different chains. @@ -248,9 +244,6 @@ impl NonCustom for CustomContract { } ``` -As you can see, although it's non-custom, we still have to inform ^sylvia custom types from the contract. -It's required for the `MultiTest` helpers to be generic over the same types as the contract. - Let's add the last `messages` attribute to the contract. It has to end with `: custom(msg query)`. This way ^sylvia will know that it has to cast `Response` to `Response` for `msg` and `Deps` to `Deps` for `query`. diff --git a/src/advanced/generics.md b/src/advanced/generics.md index e4faa9c..734abd7 100644 --- a/src/advanced/generics.md +++ b/src/advanced/generics.md @@ -17,17 +17,17 @@ version = "0.1.0" edition = "2021" [dependencies] -sylvia = "0.9.0" -cosmwasm-std = "1.5" -schemars = "0.8" +sylvia = "1.1.0" +cosmwasm-std = "2.0.4" +schemars = "0.8.16" serde = "1" -cosmwasm-schema = "1.5" -cw-storage-plus = "1.1.0" +cosmwasm-schema = "2.0.4" +cw-storage-plus = "2.0.0" [dev-dependencies] anyhow = "1.0" -cw-multi-test = "0.16" -sylvia = { version = "0.9.0", features = ["mt"] } +cw-multi-test = "2.1.0" +sylvia = { version = "1.1.0", features = ["mt"] } ``` ## Generics in interface @@ -101,12 +101,10 @@ impl NonGenericContract { We are set and ready to implement the interface. -`src/associated_impl.rs` +`src/associated.rs` ```rust -use cosmwasm_std::{Response, StdError, StdResult}; -use sylvia::types::{ExecCtx, QueryCtx}; +// [...] -use crate::associated::Associated; use crate::contract::NonGenericContract; use crate::messages::MyMsg; @@ -132,7 +130,6 @@ main `sylvia::contract` call about it. `src/contract.rs` ```rust -use crate::messages::MyMsg; use cosmwasm_std::{Response, StdResult}; use sylvia::contract; use sylvia::types::InstantiateCtx; @@ -140,7 +137,7 @@ use sylvia::types::InstantiateCtx; pub struct NonGenericContract; #[contract] -#[sv::messages(crate::associated as Associated)] +#[sv::messages(crate::associated as Associated)] impl NonGenericContract { pub const fn new() -> Self { Self {} @@ -154,12 +151,6 @@ impl NonGenericContract { ``` As in case of regular interface, we have to add the `messages` attribute to the contract. -However because the interface has associated types, we have to pass the types to the `messages`. -We do that by adding them in the `<>` brackets after the path to the interface module. - -In this case we passed concrete types, but it is also possible to pass generic types -defined on the contract. -More on that in the next paragraph. ## Generic contract @@ -175,6 +166,7 @@ Let us define a new module in which we will define a generic contract. ```rust use cosmwasm_std::{Response, StdResult}; use cw_storage_plus::Item; +use serde::Deserialize; use std::marker::PhantomData; use sylvia::contract; use sylvia::types::{CustomMsg, InstantiateCtx}; @@ -187,7 +179,7 @@ pub struct GenericContract { #[contract] impl GenericContract where - InstantiateParam: CustomMsg + 'static, + for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, for<'data> DataType: 'data, { pub const fn new() -> Self { @@ -224,15 +216,10 @@ Just like that, we created a generic contract. Now that we have the generic contract, let's implement an interface from previous paragraphs on it. -`src/associated_impl.rs` +`src/associated.rs` ```rust -use cosmwasm_std::{Response, StdError, StdResult}; -use sylvia::contract; -use sylvia::types::{ExecCtx, QueryCtx}; - -use crate::associated::Associated; +// [...] use crate::generic_contract::GenericContract; -use crate::messages::MyMsg; impl Associated for GenericContract { type Error = StdError; @@ -249,19 +236,16 @@ impl Associated for GenericContract as Associated)] +#[sv::messages(crate::associated as Associated)] impl GenericContract where - InstantiateParam: CustomMsg + 'static, + for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de, for<'data> DataType: 'data, { .. @@ -307,14 +291,10 @@ where The implementation of the interface should look like this: -`src/associated_impl.rs` +`src/associated.rs` ```rust -use cosmwasm_std::{Response, StdError, StdResult}; -use sylvia::contract; -use sylvia::types::{CustomMsg, ExecCtx, QueryCtx}; - -use crate::associated::Associated; -use crate::generic_contract::GenericContract; +// [...] +use crate::forward_contract::ForwardContract; impl Associated for ForwardContract where @@ -340,7 +320,7 @@ And as always, we have to add the `messages` attribute to the contract implement `src/forward_contract.rs` ```rust #[contract] -#[sv::messages(crate::associated as Associated)] +#[sv::messages(crate::associated as Associated)] impl ForwardContract where ExecParam: CustomMsg + 'static, @@ -375,7 +355,7 @@ pub struct GenericContract { #[entry_points(generics)] #[contract] -#[sv::messages(crate::associated as Associated)] +#[sv::messages(crate::associated as Associated)] impl GenericContract where InstantiateParam: CustomMsg + 'static, @@ -409,7 +389,7 @@ Similar to defining a contract, we have to create a `App` with the `cw_multi `src/contract.rs` ```rust -... +// [...] #[cfg(test)] mod tests { @@ -419,11 +399,12 @@ mod tests { use crate::messages::MyMsg; use super::sv::mt::CodeId; + use super::GenericContract; #[test] fn instantiate_contract() { let app = App::default(); - let code_id: CodeId = CodeId::store_code(&app); + let code_id: CodeId, _> = CodeId::store_code(&app); let owner = "owner".into_addr(); @@ -437,10 +418,7 @@ mod tests { } ``` -While creating the `CodeId` we have to pass the types we want to use in the contract. -It seems strange that `CodeId` is generic over three types while we defined only two, -but `CodeId` is also generic over `MtApp` (`cw-multi-test::App`). The compiler will always deduce this type from -the `app` passed to it, so don't worry and pass a placeholder there. +While creating the `CodeId` we have to pass the contract type as generic parameter. Perfect! We have learned how to create generic contracts and interfaces. Good luck applying this knowledge to your projects!