Repository: CosmWasm/sylvia
Pull request #361 (2024-04-22 06:52:53)

feat: Remove types forwarding to interface in sv::messages
TODO:
* [x] JsonSchema implementation for contract's Exec, Query and Sudo message enums
* [x] Improve sylvia generated code's lifetimes names to avoid collisions

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/CosmWasm/sylvia/pull/361?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) Report
Attention: Patch coverage is `81.35593%` with `11 lines` in your changes are missing coverage. Please review.
> Project coverage is 84.46%. Comparing base [(`252758d`)](https://app.codecov.io/gh/CosmWasm/sylvia/commit/252758dfd3639ec08cd244996f04601392407fbc?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) to head [(`67f554d`)](https://app.codecov.io/gh/CosmWasm/sylvia/pull/361?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).
> Report is 1 commits behind head on main.

| [Files](https://app.codecov.io/gh/CosmWasm/sylvia/pull/361?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) | Patch % | Lines |
|---|---|---|
| [sylvia-derive/src/message.rs](https://app.codecov.io/gh/CosmWasm/sylvia/pull/361?src=pr&el=tree&filepath=sylvia-derive%2Fsrc%2Fmessage.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm#diff-c3lsdmlhLWRlcml2ZS9zcmMvbWVzc2FnZS5ycw==) | 70.27% | [11 Missing :warning: ](https://app.codecov.io/gh/CosmWasm/sylvia/pull/361?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #361      +/-   ##
==========================================
- Coverage   84.71%   84.46%   -0.26%     
==========================================
  Files          37       37              
  Lines        2598     2562      -36     
==========================================
- Hits         2201     2164      -37     
- Misses        397      398       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/CosmWasm/sylvia/pull/361?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=CosmWasm).


Patch:
diff --git a/MIGRATING.md b/MIGRATING.md
index 6099eda..556b1b6 100644
--- a/MIGRATING.md
+++ b/MIGRATING.md
@@ -2,6 +2,53 @@
 
 This guide explains what is needed to upgrade contracts when migrating over major releases of `sylvia`. Note that you can also view the [complete CHANGELOG](https://github.com/CosmWasm/sylvia/blob/main/CHANGELOG.md) to understand the differences.
 
+
+## 1.0.2 ->
+
+
+### Generics in `sv::messages` not required
+```diff
+-#[contract]
+-#[sv::messages(generic<SomeType1, SomeType2, SomeType3> as Generic
+-impl Contract {}
++#[contract]
++#[sv::messages(generic as Generic)]
++impl Contract {}
+```
+
+This change is optional, since the generics are still accepted by the parser. Though they are
+ignored in the further process.
+
+
+### CodeId generic over the Contract type
+```diff
+-let code_id: CodeId<
+-    SvCustomMsg,
+-    SvCustomMsg,
+-    _,
+-> = CodeId::store_code(&app);
++let code_id: CodeId<
++    GenericContract<
++        SvCustomMsg,
++        SvCustomMsg,
++    >,
++    _,
++> = CodeId::store_code(&app);
+```
+
+### Lifetime ellision in a contract's impl block not supported
+```diff
+-#[contract]
+-impl Cw1SubkeysContract<'_> {
+-    // [...]
+-}
++#[contract]
++impl<'a> Cw1SubkeysContract<'a> {
++    // [...]
++}
+```
+
+
 ## 0.9.3 -> 0.10.0
 
 ## Multitest proxy
diff --git a/README.md b/README.md
index 383f1df..856d9e2 100644
--- a/README.md
+++ b/README.md
@@ -689,58 +689,6 @@ where
 }
 ```
 
-### Implement interface
-
-```rust
-impl<InstantiateParam, ExecParam, FieldType>
-    Generic
-    for crate::contract::GenericContract<
-        InstantiateParam,
-        ExecParam,
-        FieldType,
-    >
-{
-    type Error = StdError;
-    type ExecParam = ExecParam;
-    type QueryParam: SvCustomMsg;
-    type RetType = SvCustomMsg;
-
-    fn generic_exec(
-        &self,
-        _ctx: ExecCtx,
-        _msgs: Vec<CosmosMsg<Self::ExecParam>>,
-    ) -> StdResult<Response> {
-        Ok(Response::new())
-    }
-
-    fn generic_query(
-        &self,
-        _ctx: QueryCtx,
-        _msg: Self::QueryParam,
-    ) -> StdResult<Self::RetType> {
-        Ok(SvCustomMsg {})
-    }
-}
-```
-
-Now we have to inform Sylvia that the interface implemented for the contract has associated types.
-We have to list those types (generics or concrete) next to the interface in the `#[sv::messages]`
-attribute:
-
-```rust
-#[contract]
-#[sv::messages(generic<ExecParam, SvCustomMsg, SvCustomMsg> as Generic)]
-impl<InstantiateParam, ExecParam, FieldType>
-    GenericContract<InstantiateParam, ExecParam, FieldType>
-where
-    for<'msg_de> InstantiateParam: CustomMsg + Deserialize<'msg_de> + 'msg_de,
-    ExecParam: CustomMsg + DeserializeOwned + 'static,
-    FieldType: 'static,
-{
-    ...
-}
-```
-
 ### Generics in entry_points
 
 Entry points have to be generated with concrete types. Using the `entry_points` macro
diff --git a/examples/contracts/cw1-subkeys/src/contract.rs b/examples/contracts/cw1-subkeys/src/contract.rs
index 30deacb..131c887 100644
--- a/examples/contracts/cw1-subkeys/src/contract.rs
+++ b/examples/contracts/cw1-subkeys/src/contract.rs
@@ -36,7 +36,7 @@ pub struct Cw1SubkeysContract<'a> {
 #[sv::error(ContractError)]
 #[sv::messages(cw1 as Cw1)]
 #[sv::messages(whitelist as Whitelist)]
-impl Cw1SubkeysContract<'_> {
+impl<'abcd> Cw1SubkeysContract<'abcd> {
     pub const fn new() -> Self {
         Self {
             whitelist: Cw1WhitelistContract::new(),
diff --git a/examples/contracts/cw1-whitelist/src/contract.rs b/examples/contracts/cw1-whitelist/src/contract.rs
index a1738d3..fd73cd7 100644
--- a/examples/contracts/cw1-whitelist/src/contract.rs
+++ b/examples/contracts/cw1-whitelist/src/contract.rs
@@ -22,7 +22,7 @@ pub struct Cw1WhitelistContract<'a> {
 #[sv::error(ContractError)]
 #[sv::messages(cw1 as Cw1)]
 #[sv::messages(whitelist as Whitelist)]
-impl Cw1WhitelistContract<'_> {
+impl<'a> Cw1WhitelistContract<'a> {
     pub const fn new() -> Self {
         Self {
             admins: Map::new("admins"),
diff --git a/examples/contracts/cw20-base/src/contract.rs b/examples/contracts/cw20-base/src/contract.rs
index 8979c72..c594fe2 100644
--- a/examples/contracts/cw20-base/src/contract.rs
+++ b/examples/contracts/cw20-base/src/contract.rs
@@ -78,7 +78,7 @@ pub struct Cw20Base<'a> {
 #[sv::messages(cw20_allowances as Allowances)]
 #[sv::messages(cw20_marketing as Marketing)]
 #[sv::messages(cw20_minting as Minting)]
-impl Cw20Base<'_> {
+impl<'abcd> Cw20Base<'abcd> {
     pub const fn new() -> Self {
         Self {
             token_info: Item::new("token_info"),
diff --git a/examples/contracts/generic_contract/src/bin/schema.rs b/examples/contracts/generic_contract/src/bin/schema.rs
index 5cb3b22..6cd3cfd 100644
--- a/examples/contracts/generic_contract/src/bin/schema.rs
+++ b/examples/contracts/generic_contract/src/bin/schema.rs
@@ -7,7 +7,7 @@ fn main() {
 
     write_api! {
         instantiate: InstantiateMsg<SvCustomMsg>,
-        execute: ContractExecMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg>,
-        query: ContractQueryMsg<SvCustomMsg>,
+        execute: ContractExecMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg,>,
+        query: ContractQueryMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg,>,
     }
 }
diff --git a/examples/contracts/generic_contract/src/contract.rs b/examples/contracts/generic_contract/src/contract.rs
index cf97a4a..2b64152 100644
--- a/examples/contracts/generic_contract/src/contract.rs
+++ b/examples/contracts/generic_contract/src/contract.rs
@@ -50,8 +50,8 @@ pub struct GenericContract<
 #[cfg_attr(not(feature = "library"), entry_points(generics<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, String>))]
 #[contract]
 #[sv::messages(cw1 as Cw1: custom(msg, query))]
-#[sv::messages(generic<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg> as Generic: custom(msg, query))]
-#[sv::messages(custom_and_generic<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg> as CustomAndGeneric)]
+#[sv::messages(generic as Generic: custom(msg, query))]
+#[sv::messages(custom_and_generic as CustomAndGeneric)]
 #[sv::custom(msg=SvCustomMsg, query=SvCustomQuery)]
 impl<
         InstantiateT,
@@ -194,7 +194,7 @@ where
 #[cfg(test)]
 mod tests {
     use super::sv::mt::CodeId;
-    use super::{SvCustomMsg, SvCustomQuery};
+    use super::{GenericContract, SvCustomMsg, SvCustomQuery};
     use crate::contract::sv::mt::GenericContractProxy;
     use cw_multi_test::IntoBech32;
     use sylvia::multitest::App;
@@ -204,18 +204,20 @@ mod tests {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         #[allow(clippy::type_complexity)]
         let code_id: CodeId<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            String,
+            GenericContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                String,
+            >,
             _,
         > = CodeId::store_code(&app);
 
diff --git a/examples/contracts/generic_contract/src/custom_and_generic.rs b/examples/contracts/generic_contract/src/custom_and_generic.rs
index ba63562..001cb74 100644
--- a/examples/contracts/generic_contract/src/custom_and_generic.rs
+++ b/examples/contracts/generic_contract/src/custom_and_generic.rs
@@ -2,7 +2,7 @@ use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
 use custom_and_generic::CustomAndGeneric;
 use sylvia::types::{ExecCtx, QueryCtx, SudoCtx};
 
-use crate::contract::{SvCustomMsg, SvCustomQuery};
+use crate::contract::{GenericContract, SvCustomMsg, SvCustomQuery};
 
 impl<
         InstantiateT,
@@ -18,7 +18,7 @@ impl<
         MigrateT,
         FieldT,
     > CustomAndGeneric
-    for crate::contract::GenericContract<
+    for GenericContract<
         InstantiateT,
         Exec1T,
         Exec2T,
@@ -104,7 +104,7 @@ impl<
 
 #[cfg(test)]
 mod tests {
-    use super::{SvCustomMsg, SvCustomQuery};
+    use super::{GenericContract, SvCustomMsg, SvCustomQuery};
     use crate::contract::sv::mt::CodeId;
     use custom_and_generic::sv::mt::CustomAndGenericProxy;
     use cw_multi_test::IntoBech32;
@@ -114,18 +114,20 @@ mod tests {
     fn proxy_methods() {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         let code_id = CodeId::<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            String,
+            GenericContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                String,
+            >,
             _,
         >::store_code(&app);
 
diff --git a/examples/contracts/generic_contract/src/cw1.rs b/examples/contracts/generic_contract/src/cw1.rs
index fddbacf..fff63bb 100644
--- a/examples/contracts/generic_contract/src/cw1.rs
+++ b/examples/contracts/generic_contract/src/cw1.rs
@@ -1,3 +1,4 @@
+use crate::contract::GenericContract;
 use cosmwasm_std::{CosmosMsg, Response, StdError, StdResult};
 use cw1::{CanExecuteResp, Cw1};
 use sylvia::types::{ExecCtx, QueryCtx};
@@ -16,7 +17,7 @@ impl<
         MigrateT,
         FieldT,
     > Cw1
-    for crate::contract::GenericContract<
+    for GenericContract<
         InstantiateT,
         Exec1T,
         Exec2T,
@@ -50,7 +51,7 @@ impl<
 #[cfg(test)]
 mod tests {
     use crate::contract::sv::mt::CodeId;
-    use crate::contract::{SvCustomMsg, SvCustomQuery};
+    use crate::contract::{GenericContract, SvCustomMsg, SvCustomQuery};
     use cosmwasm_std::{CosmosMsg, Empty};
     use cw1::sv::mt::Cw1Proxy;
     use cw_multi_test::IntoBech32;
@@ -60,18 +61,20 @@ mod tests {
     fn proxy_methods() {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         let code_id = CodeId::<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            String,
+            GenericContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                String,
+            >,
             _,
         >::store_code(&app);
 
diff --git a/examples/contracts/generic_contract/src/generic.rs b/examples/contracts/generic_contract/src/generic.rs
index 4c073c8..59541e1 100644
--- a/examples/contracts/generic_contract/src/generic.rs
+++ b/examples/contracts/generic_contract/src/generic.rs
@@ -3,7 +3,7 @@ use generic::Generic;
 use serde::Deserialize;
 use sylvia::types::{CustomMsg, ExecCtx, QueryCtx, SudoCtx};
 
-use crate::contract::SvCustomMsg;
+use crate::contract::{GenericContract, SvCustomMsg};
 
 impl<
         InstantiateT,
@@ -19,7 +19,7 @@ impl<
         MigrateT,
         FieldT,
     > Generic
-    for crate::contract::GenericContract<
+    for GenericContract<
         InstantiateT,
         Exec1T,
         Exec2T,
@@ -117,7 +117,7 @@ where
 #[cfg(test)]
 mod tests {
     use crate::contract::sv::mt::CodeId;
-    use crate::contract::{SvCustomMsg, SvCustomQuery};
+    use crate::contract::{GenericContract, SvCustomMsg, SvCustomQuery};
     use cosmwasm_std::CosmosMsg;
     use cw_multi_test::IntoBech32;
     use generic::sv::mt::GenericProxy;
@@ -128,18 +128,20 @@ mod tests {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         #[allow(clippy::type_complexity)]
         let code_id: CodeId<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            String,
+            GenericContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                String,
+            >,
             _,
         > = CodeId::store_code(&app);
 
diff --git a/examples/contracts/generic_iface_on_contract/src/contract.rs b/examples/contracts/generic_iface_on_contract/src/contract.rs
index 1d89d3c..43f9418 100644
--- a/examples/contracts/generic_iface_on_contract/src/contract.rs
+++ b/examples/contracts/generic_iface_on_contract/src/contract.rs
@@ -17,8 +17,8 @@ impl cosmwasm_std::CustomQuery for SvCustomQuery {}
 
 #[cfg_attr(not(feature = "library"), entry_points)]
 #[contract]
-#[sv::messages(generic<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg> as Generic: custom(msg, query))]
-#[sv::messages(custom_and_generic<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg> as CustomAndGeneric)]
+#[sv::messages(generic as Generic: custom(msg, query))]
+#[sv::messages(custom_and_generic as CustomAndGeneric)]
 #[sv::messages(cw1 as Cw1: custom(msg, query))]
 /// Required if interface returns generic `Response`
 #[sv::custom(msg=SvCustomMsg, query=SvCustomQuery)]
diff --git a/examples/contracts/generics_forwarded/src/bin/schema.rs b/examples/contracts/generics_forwarded/src/bin/schema.rs
index 3295c81..deec2b7 100644
--- a/examples/contracts/generics_forwarded/src/bin/schema.rs
+++ b/examples/contracts/generics_forwarded/src/bin/schema.rs
@@ -3,7 +3,7 @@ use cosmwasm_schema::write_api;
 #[cfg(not(tarpaulin_include))]
 fn main() {
     use generics_forwarded::contract::sv::{ContractExecMsg, ContractQueryMsg, InstantiateMsg};
-    use generics_forwarded::contract::SvCustomMsg;
+    use generics_forwarded::contract::{SvCustomMsg, SvCustomQuery};
 
     write_api! {
         instantiate: InstantiateMsg<SvCustomMsg>,
@@ -18,7 +18,7 @@ fn main() {
         // This potentially could be done with some type alias, not sure how it would affect the
         // schema.
         // execute: <GenericForwardedContract as ContractApi>::ContractExec,
-        execute: ContractExecMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg>,
-        query: ContractQueryMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg>,
+        execute: ContractExecMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomQuery, String>,
+        query: ContractQueryMsg<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomQuery, String>,
     }
 }
diff --git a/examples/contracts/generics_forwarded/src/contract.rs b/examples/contracts/generics_forwarded/src/contract.rs
index 6e7d4f5..945ff61 100644
--- a/examples/contracts/generics_forwarded/src/contract.rs
+++ b/examples/contracts/generics_forwarded/src/contract.rs
@@ -53,9 +53,9 @@ pub struct GenericsForwardedContract<
 #[cfg_attr(not(feature = "library"), sylvia::entry_points(generics<SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomMsg, SvCustomQuery, String>, custom(msg=SvCustomMsg, query=SvCustomQuery)))]
 #[contract]
 #[sv::error(ContractError)]
-#[sv::messages(generic<Exec1T, Exec2T, Exec3T, Query1T, Query2T, Query3T, Sudo1T, Sudo2T, Sudo3T, SvCustomMsg> as Generic: custom(msg, query))]
+#[sv::messages(generic as Generic: custom(msg, query))]
 #[sv::messages(cw1 as Cw1: custom(msg, query))]
-#[sv::messages(custom_and_generic<Exec1T, Exec2T, Exec3T, Query1T, Query2T, Query3T, Sudo1T, Sudo2T, Sudo3T, SvCustomMsg> as CustomAndGeneric)]
+#[sv::messages(custom_and_generic as CustomAndGeneric)]
 #[sv::custom(msg=CustomMsgT, query=CustomQueryT)]
 impl<
         InstantiateT,
@@ -204,7 +204,7 @@ where
 #[cfg(test)]
 mod tests {
     use super::sv::mt::CodeId;
-    use super::{SvCustomMsg, SvCustomQuery};
+    use super::{GenericsForwardedContract, SvCustomMsg, SvCustomQuery};
     use crate::contract::sv::mt::GenericsForwardedContractProxy;
     use cw_multi_test::IntoBech32;
     use sylvia::multitest::App;
@@ -214,20 +214,22 @@ mod tests {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         #[allow(clippy::type_complexity)]
         let code_id: CodeId<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomQuery,
-            String,
+            GenericsForwardedContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomQuery,
+                String,
+            >,
             _,
         > = CodeId::store_code(&app);
 
diff --git a/examples/contracts/generics_forwarded/src/custom_and_generic.rs b/examples/contracts/generics_forwarded/src/custom_and_generic.rs
index 3227136..2d2996a 100644
--- a/examples/contracts/generics_forwarded/src/custom_and_generic.rs
+++ b/examples/contracts/generics_forwarded/src/custom_and_generic.rs
@@ -3,7 +3,7 @@ use custom_and_generic::CustomAndGeneric;
 use serde::Deserialize;
 use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx, SudoCtx};
 
-use crate::contract::SvCustomMsg;
+use crate::contract::{GenericsForwardedContract, SvCustomMsg};
 use crate::error::ContractError;
 
 impl<
@@ -22,7 +22,7 @@ impl<
         CustomQueryT,
         FieldT,
     > CustomAndGeneric
-    for crate::contract::GenericsForwardedContract<
+    for GenericsForwardedContract<
         InstantiateT,
         Exec1T,
         Exec2T,
@@ -126,7 +126,7 @@ where
 #[cfg(test)]
 mod tests {
     use crate::contract::sv::mt::CodeId;
-    use crate::contract::{SvCustomMsg, SvCustomQuery};
+    use crate::contract::{GenericsForwardedContract, SvCustomMsg, SvCustomQuery};
     use cosmwasm_std::CosmosMsg;
     use custom_and_generic::sv::mt::CustomAndGenericProxy;
     use cw_multi_test::IntoBech32;
@@ -136,20 +136,22 @@ mod tests {
     fn proxy_methods() {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         let code_id = CodeId::<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomQuery,
-            String,
+            GenericsForwardedContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomQuery,
+                String,
+            >,
             _,
         >::store_code(&app);
 
diff --git a/examples/contracts/generics_forwarded/src/cw1.rs b/examples/contracts/generics_forwarded/src/cw1.rs
index 0111c87..a4ddc17 100644
--- a/examples/contracts/generics_forwarded/src/cw1.rs
+++ b/examples/contracts/generics_forwarded/src/cw1.rs
@@ -5,6 +5,7 @@ use serde::de::DeserializeOwned;
 use serde::Deserialize;
 use sylvia::types::{CustomQuery, ExecCtx, QueryCtx};
 
+use crate::contract::GenericsForwardedContract;
 use crate::error::ContractError;
 
 impl<
@@ -23,7 +24,7 @@ impl<
         CustomQueryT,
         FieldT,
     > Cw1
-    for crate::contract::GenericsForwardedContract<
+    for GenericsForwardedContract<
         InstantiateT,
         Exec1T,
         Exec2T,
@@ -74,7 +75,7 @@ where
 #[cfg(test)]
 mod tests {
     use crate::contract::sv::mt::CodeId;
-    use crate::contract::{SvCustomMsg, SvCustomQuery};
+    use crate::contract::{GenericsForwardedContract, SvCustomMsg, SvCustomQuery};
     use cosmwasm_std::{CosmosMsg, Empty};
     use cw1::sv::mt::Cw1Proxy;
     use cw_multi_test::IntoBech32;
@@ -84,20 +85,22 @@ mod tests {
     fn proxy_methods() {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         let code_id = CodeId::<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomQuery,
-            String,
+            GenericsForwardedContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomQuery,
+                String,
+            >,
             _,
         >::store_code(&app);
 
diff --git a/examples/contracts/generics_forwarded/src/generic.rs b/examples/contracts/generics_forwarded/src/generic.rs
index 28f943f..c581754 100644
--- a/examples/contracts/generics_forwarded/src/generic.rs
+++ b/examples/contracts/generics_forwarded/src/generic.rs
@@ -3,7 +3,7 @@ use generic::Generic;
 use serde::Deserialize;
 use sylvia::types::{CustomMsg, CustomQuery, ExecCtx, QueryCtx, SudoCtx};
 
-use crate::contract::SvCustomMsg;
+use crate::contract::{GenericsForwardedContract, SvCustomMsg};
 use crate::error::ContractError;
 
 impl<
@@ -22,7 +22,7 @@ impl<
         CustomQueryT,
         FieldT,
     > Generic
-    for crate::contract::GenericsForwardedContract<
+    for GenericsForwardedContract<
         InstantiateT,
         Exec1T,
         Exec2T,
@@ -124,7 +124,7 @@ where
 #[cfg(test)]
 mod tests {
     use crate::contract::sv::mt::CodeId;
-    use crate::contract::{SvCustomMsg, SvCustomQuery};
+    use crate::contract::{GenericsForwardedContract, SvCustomMsg, SvCustomQuery};
     use cosmwasm_std::CosmosMsg;
     use cw_multi_test::IntoBech32;
     use generic::sv::mt::GenericProxy;
@@ -135,20 +135,22 @@ mod tests {
         let app = App::<cw_multi_test::BasicApp<SvCustomMsg, SvCustomQuery>>::custom(|_, _, _| {});
         #[allow(clippy::type_complexity)]
         let code_id: CodeId<
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomMsg,
-            SvCustomQuery,
-            String,
+            GenericsForwardedContract<
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomMsg,
+                SvCustomQuery,
+                String,
+            >,
             _,
         > = CodeId::store_code(&app);
 
diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs
index 0dccfd9..dbf7910 100644
--- a/sylvia-derive/src/input.rs
+++ b/sylvia-derive/src/input.rs
@@ -200,14 +200,13 @@ impl<'a> ImplInput<'a> {
             item,
             generics,
             custom,
-            interfaces,
             ..
         } = self;
         let multitest_helpers = self.emit_multitest_helpers();
 
         let querier = ContractQuerier::new(item).emit();
         let messages = self.emit_messages();
-        let contract_api = ContractApi::new(item, generics, custom, interfaces).emit();
+        let contract_api = ContractApi::new(item, generics, custom).emit();
 
         quote! {
             pub mod sv {
@@ -264,16 +263,12 @@ impl<'a> ImplInput<'a> {
     }
 
     fn emit_glue_msg(&self, msg_ty: MsgType) -> TokenStream {
-        let Self { generics, item, .. } = self;
-        let where_clause = &item.generics.where_clause;
-        let variants = MsgVariants::new(item.as_variants(), msg_ty, generics, where_clause);
         GlueMessage::new(
             self.item,
             msg_ty,
             &self.error,
             &self.custom,
             &self.interfaces,
-            variants,
         )
         .emit()
     }
diff --git a/sylvia-derive/src/interfaces.rs b/sylvia-derive/src/interfaces.rs
index fefc45c..3cd104d 100644
--- a/sylvia-derive/src/interfaces.rs
+++ b/sylvia-derive/src/interfaces.rs
@@ -1,7 +1,7 @@
 use proc_macro2::{Ident, TokenStream};
 use quote::quote;
 use syn::spanned::Spanned;
-use syn::{GenericArgument, ItemImpl};
+use syn::{ItemImpl, Type};
 
 use crate::crate_module;
 use crate::parser::attributes::msg::MsgType;
@@ -18,26 +18,19 @@ impl Interfaces {
         Self { interfaces }
     }
 
-    pub fn emit_glue_message_variants(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
-        let sylvia = crate_module();
-
+    pub fn emit_glue_message_variants(
+        &self,
+        msg_ty: &MsgType,
+        contract: &Type,
+    ) -> Vec<TokenStream> {
         self.interfaces
             .iter()
             .map(|interface| {
                 let ContractMessageAttr {
-                    module,
-                    variant,
-                    generics,
-                    ..
+                    module, variant, ..
                 } = interface;
 
-                let generics = if !generics.is_empty() {
-                    quote! { < #generics > }
-                } else {
-                    quote! {}
-                };
-                let interface_enum =
-                    quote! { <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> };
+                let interface_enum = quote! { < #contract as #module ::sv::InterfaceMessagesApi> };
                 let type_name = msg_ty.as_accessor_name();
 
                 quote! { #variant ( #interface_enum :: #type_name) }
@@ -45,6 +38,20 @@ impl Interfaces {
             .collect()
     }
 
+    pub fn emit_glue_message_types(&self, msg_ty: &MsgType, contract: &Type) -> Vec<TokenStream> {
+        self.interfaces
+            .iter()
+            .map(|interface| {
+                let ContractMessageAttr { module, .. } = interface;
+
+                let interface_enum = quote! { < #contract as #module ::sv::InterfaceMessagesApi> };
+                let type_name = msg_ty.as_accessor_name();
+
+                quote! { #interface_enum :: #type_name }
+            })
+            .collect()
+    }
+
     pub fn emit_messages_call(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
         self.interfaces
             .iter()
@@ -83,25 +90,21 @@ impl Interfaces {
             .collect()
     }
 
-    pub fn emit_response_schemas_calls(&self, msg_ty: &MsgType) -> Vec<TokenStream> {
-        let sylvia = crate_module();
-
+    pub fn emit_response_schemas_calls(
+        &self,
+        msg_ty: &MsgType,
+        contract: &Type,
+    ) -> Vec<TokenStream> {
         self.interfaces
             .iter()
             .map(|interface| {
                 let ContractMessageAttr {
-                    module, generics, ..
+                    module, ..
                 } = interface;
 
-                let generics = if !generics.is_empty() {
-                    quote! { < #generics > }
-                } else {
-                    quote! {}
-                };
-
                 let type_name = msg_ty.as_accessor_name();
                 quote! {
-                    <#module ::sv::Api #generics as #sylvia ::types::InterfaceApi> :: #type_name :: response_schemas_impl()
+                    <#contract as #module ::sv::InterfaceMessagesApi> :: #type_name :: response_schemas_impl()
                 }
             })
             .collect()
@@ -130,11 +133,4 @@ impl Interfaces {
             }
         }).collect()
     }
-
-    pub fn as_generic_args(&self) -> Vec<&GenericArgument> {
-        self.interfaces
-            .iter()
-            .flat_map(|interface| &interface.generics)
-            .collect()
-    }
 }
diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs
index 64bb5f7..365f153 100644
--- a/sylvia-derive/src/message.rs
+++ b/sylvia-derive/src/message.rs
@@ -9,12 +9,11 @@ use crate::parser::{
 use crate::strip_generics::StripGenerics;
 use crate::strip_self_path::StripSelfPath;
 use crate::utils::{
-    as_where_clause, emit_bracketed_generics, extract_return_type, filter_generics, filter_wheres,
-    process_fields, SvCasing,
+    as_where_clause, emit_bracketed_generics, extract_return_type, filter_wheres, process_fields,
+    SvCasing,
 };
 use crate::variant_descs::{AsVariantDescs, VariantDescs};
 use convert_case::{Case, Casing};
-use itertools::Itertools;
 use proc_macro2::{Span, TokenStream};
 use proc_macro_error::emit_error;
 use quote::{quote, ToTokens};
@@ -817,7 +816,6 @@ pub struct GlueMessage<'a> {
     error: &'a ContractErrorAttr,
     custom: &'a Custom,
     interfaces: &'a Interfaces,
-    variants: MsgVariants<'a, GenericParam>,
 }
 
 impl<'a> GlueMessage<'a> {
@@ -827,7 +825,6 @@ impl<'a> GlueMessage<'a> {
         error: &'a ContractErrorAttr,
         custom: &'a Custom,
         interfaces: &'a Interfaces,
-        variants: MsgVariants<'a, GenericParam>,
     ) -> Self {
         GlueMessage {
             source,
@@ -836,7 +833,6 @@ impl<'a> GlueMessage<'a> {
             error,
             custom,
             interfaces,
-            variants,
         }
     }
 
@@ -849,54 +845,24 @@ impl<'a> GlueMessage<'a> {
             error,
             custom,
             interfaces,
-            variants,
+            ..
         } = self;
 
         let generics: Vec<_> = source.generics.params.iter().collect();
-        let interface_generic_args = interfaces.as_generic_args();
-        let interface_used_generics = filter_generics(&generics, &interface_generic_args);
-        let used_generics = variants.used_generics();
-
-        let wrapper_generics: Vec<_> = interface_used_generics
-            .iter()
-            .chain(used_generics.iter())
-            .unique()
-            .copied()
-            .collect();
-
-        let unused_generics: Vec<_> = variants
-            .unused_generics()
-            .iter()
-            .filter(|unused| !wrapper_generics.iter().any(|used| used == *unused))
-            .collect();
-
         let full_where_clause = &source.generics.where_clause;
-        let wrapper_predicates = filter_wheres(full_where_clause, &generics, &wrapper_generics);
-        let wrapper_where_clause = if !wrapper_predicates.is_empty() {
-            quote! { where #(#wrapper_predicates,)* }
-        } else {
-            quote! {}
-        };
-
-        let unused_generics = emit_bracketed_generics(&unused_generics);
-        let bracketed_used_generics = emit_bracketed_generics(used_generics);
-        let bracketed_wrapper_generics = emit_bracketed_generics(&wrapper_generics);
+        let bracketed_wrapper_generics = emit_bracketed_generics(&generics);
 
         let contract_enum_name = msg_ty.emit_msg_wrapper_name();
-        let enum_name = msg_ty.emit_msg_name();
+        let enum_accessor = msg_ty.as_accessor_name();
         let contract_name = StripGenerics.fold_type((*contract).clone());
 
-        let variants = interfaces.emit_glue_message_variants(msg_ty);
+        let variants = interfaces.emit_glue_message_variants(msg_ty, contract);
+        let types = interfaces.emit_glue_message_types(msg_ty, contract);
 
         let ep_name = msg_ty.emit_ep_name();
         let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), contract.span());
-        let contract_variant = quote! { #contract_name ( #enum_name #bracketed_used_generics ) };
+        let contract_variant = quote! { #contract_name ( <#contract as #sylvia ::types::ContractApi> :: #enum_accessor ) };
         let mut messages_call = interfaces.emit_messages_call(msg_ty);
-        let prefixed_used_generics = if !used_generics.is_empty() {
-            quote! { :: #bracketed_used_generics }
-        } else {
-            quote! {}
-        };
         messages_call.push(quote! { &#messages_fn_name() });
 
         let variants_cnt = messages_call.len();
@@ -921,15 +887,15 @@ impl<'a> GlueMessage<'a> {
         let ctx_type = msg_ty.emit_ctx_type(&custom.query_or_default());
         let ret_type = msg_ty.emit_result_type(&custom.msg_or_default(), &error.error);
 
-        let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty);
+        let mut response_schemas_calls = interfaces.emit_response_schemas_calls(msg_ty, contract);
         response_schemas_calls
-            .push(quote! {#enum_name #prefixed_used_generics :: response_schemas_impl()});
+            .push(quote! {<#contract as #sylvia ::types::ContractApi> :: #enum_accessor ::response_schemas_impl()});
 
         let response_schemas = match msg_ty {
             MsgType::Query => {
                 quote! {
                     #[cfg(not(target_arch = "wasm32"))]
-                    impl #bracketed_wrapper_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_wrapper_generics #wrapper_where_clause {
+                    impl #bracketed_wrapper_generics #sylvia ::cw_schema::QueryResponses for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
                         fn response_schemas_impl() -> std::collections::BTreeMap<String, #sylvia ::schemars::schema::RootSchema> {
                             let responses = [#(#response_schemas_calls),*];
                             responses.into_iter().flatten().collect()
@@ -944,15 +910,55 @@ impl<'a> GlueMessage<'a> {
 
         quote! {
             #[allow(clippy::derive_partial_eq_without_eq)]
-            #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)]
+            #[derive(#sylvia ::serde::Serialize, Clone, Debug, PartialEq)]
             #[serde(rename_all="snake_case", untagged)]
-            pub enum #contract_enum_name #bracketed_wrapper_generics #wrapper_where_clause {
+            pub enum #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
                 #(#variants,)*
                 #contract_variant
             }
 
-            impl #bracketed_wrapper_generics #contract_enum_name #bracketed_wrapper_generics #wrapper_where_clause {
-                pub fn dispatch #unused_generics (
+            // `schemars` v0.8.16 requires every generic type to implement JsonSchema in
+            // order to use derive JsonSchema macro. The goal of that trait bound is to
+            // generate schema_name. Currently there's no way to provide such a name in an
+            // attribute, so Sylvia needs to implement this trait manually:
+            //
+            impl #bracketed_wrapper_generics #sylvia ::schemars::JsonSchema
+                for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
+
+                fn schema_name() -> std::string::String {
+                    {
+                        let res = format!(
+                                "{0}",
+                                std::any::type_name::<Self>()
+                        );
+                        res
+                    }
+                }
+
+                fn json_schema(
+                    gen: &mut #sylvia ::schemars::gen::SchemaGenerator,
+                ) -> #sylvia ::schemars::schema::Schema {
+                    #sylvia ::schemars::schema::Schema::Object( #sylvia ::schemars::schema::SchemaObject {
+                        subschemas: Some(
+                            Box::new( #sylvia ::schemars::schema::SubschemaValidation {
+                                any_of: Some(
+                                    <[_]>::into_vec(
+                                        Box::new([
+                                            #(gen.subschema_for::<#types>(),)*
+                                            gen.subschema_for::< <#contract as #sylvia ::types::ContractApi> :: #enum_accessor >(),
+                                        ]),
+                                    ),
+                                ),
+                                ..Default::default()
+                            }),
+                        ),
+                        ..Default::default()
+                    })
+                }
+            }
+
+            impl #bracketed_wrapper_generics #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
+                pub fn dispatch (
                     self,
                     contract: &#contract,
                     ctx: #ctx_type,
@@ -971,9 +977,9 @@ impl<'a> GlueMessage<'a> {
 
             #response_schemas
 
-            impl<'de, #(#wrapper_generics,)* > serde::Deserialize<'de> for #contract_enum_name #bracketed_wrapper_generics #wrapper_where_clause {
+            impl<'sv_de, #(#generics,)* > serde::Deserialize<'sv_de> for #contract_enum_name #bracketed_wrapper_generics #full_where_clause {
                 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-                    where D: serde::Deserializer<'de>,
+                    where D: serde::Deserializer<'sv_de>,
                 {
                     use serde::de::Error;
 
@@ -1021,16 +1027,10 @@ pub struct ContractApi<'a> {
     sudo_variants: MsgVariants<'a, GenericParam>,
     generics: &'a [&'a GenericParam],
     custom: &'a Custom,
-    interfaces: &'a Interfaces,
 }
 
 impl<'a> ContractApi<'a> {
-    pub fn new(
-        source: &'a ItemImpl,
-        generics: &'a [&'a GenericParam],
-        custom: &'a Custom,
-        interfaces: &'a Interfaces,
-    ) -> Self {
+    pub fn new(source: &'a ItemImpl, generics: &'a [&'a GenericParam], custom: &'a Custom) -> Self {
         let exec_variants = MsgVariants::new(
             source.as_variants(),
             MsgType::Exec,
@@ -1075,7 +1075,6 @@ impl<'a> ContractApi<'a> {
             sudo_variants,
             generics,
             custom,
-            interfaces,
         }
     }
 
@@ -1090,7 +1089,7 @@ impl<'a> ContractApi<'a> {
             sudo_variants,
             generics,
             custom,
-            interfaces,
+            ..
         } = self;
 
         let where_clause = &source.generics.where_clause;
@@ -1101,34 +1100,12 @@ impl<'a> ContractApi<'a> {
         let migrate_generics = &migrate_variants.used_generics;
         let sudo_generics = &sudo_variants.used_generics;
 
-        let interface_generics = interfaces.as_generic_args();
-        let interface_generics = filter_generics(generics, &interface_generics);
-
-        let contract_exec_generics: Vec<_> = interface_generics
-            .iter()
-            .chain(exec_generics.iter())
-            .unique()
-            .collect();
-        let contract_query_generics: Vec<_> = interface_generics
-            .iter()
-            .chain(query_generics.iter())
-            .unique()
-            .collect();
-        let contract_sudo_generics: Vec<_> = interface_generics
-            .iter()
-            .chain(sudo_generics.iter())
-            .unique()
-            .collect();
-
         let bracket_generics = emit_bracketed_generics(generics);
         let exec_bracketed_generics = emit_bracketed_generics(exec_generics);
         let query_bracketed_generics = emit_bracketed_generics(query_generics);
         let sudo_bracketed_generics = emit_bracketed_generics(sudo_generics);
         let instantiate_bracketed_generics = emit_bracketed_generics(instantiate_generics);
         let migrate_bracketed_generics = emit_bracketed_generics(migrate_generics);
-        let contract_exec_bracketed_generics = emit_bracketed_generics(&contract_exec_generics);
-        let contract_query_bracketed_generics = emit_bracketed_generics(&contract_query_generics);
-        let contract_sudo_bracketed_generics = emit_bracketed_generics(&contract_sudo_generics);
 
         let migrate_type = if migrate_variants.variants().count() != 0 {
             quote! { type Migrate = MigrateMsg #migrate_bracketed_generics; }
@@ -1139,9 +1116,9 @@ impl<'a> ContractApi<'a> {
 
         quote! {
             impl #bracket_generics #sylvia ::types::ContractApi for #contract_name #where_clause {
-                type ContractExec = ContractExecMsg #contract_exec_bracketed_generics;
-                type ContractQuery = ContractQueryMsg #contract_query_bracketed_generics;
-                type ContractSudo = ContractSudoMsg #contract_sudo_bracketed_generics;
+                type ContractExec = ContractExecMsg #bracket_generics;
+                type ContractQuery = ContractQueryMsg #bracket_generics;
+                type ContractSudo = ContractSudoMsg #bracket_generics;
                 type Exec = ExecMsg #exec_bracketed_generics;
                 type Query = QueryMsg #query_bracketed_generics;
                 type Sudo = SudoMsg #sudo_bracketed_generics;
@@ -1181,6 +1158,7 @@ impl<'a> InterfaceApi<'a> {
             associated_types,
         } = self;
 
+        let interface_name = &source.ident;
         let generics: Vec<_> = associated_types
             .without_special()
             .map(ItemType::as_name)
@@ -1210,11 +1188,6 @@ impl<'a> InterfaceApi<'a> {
         let query_generics = &query_variants.used_generics;
         let sudo_generics = &sudo_variants.used_generics;
 
-        let bracket_generics = emit_bracketed_generics(&generics);
-        let exec_bracketed_generics = emit_bracketed_generics(exec_generics);
-        let query_bracketed_generics = emit_bracketed_generics(query_generics);
-        let sudo_bracketed_generics = emit_bracketed_generics(sudo_generics);
-
         let phantom = if !generics.is_empty() {
             quote! {
                 _phantom: std::marker::PhantomData<( #(#generics,)* )>,
@@ -1224,14 +1197,29 @@ impl<'a> InterfaceApi<'a> {
         };
 
         quote! {
-            pub struct Api #bracket_generics {
+            pub trait InterfaceMessagesApi {
+                type Exec;
+                type Query;
+                type Sudo;
+                type Querier<'querier>;
+            }
+
+            impl<Contract: #interface_name> InterfaceMessagesApi for Contract {
+                type Exec = ExecMsg < #(<Contract as #interface_name >:: #exec_generics,)* >;
+                type Query = QueryMsg < #(<Contract as #interface_name >:: #query_generics,)* >;
+                type Sudo = SudoMsg < #(<Contract as #interface_name >:: #sudo_generics ,)* >;
+                type Querier<'querier> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Contract >;
+            }
+
+
+            pub struct Api < #(#generics,)* > {
                 #phantom
             }
 
-            impl #bracket_generics #sylvia ::types::InterfaceApi for Api #bracket_generics #where_clause {
-                type Exec = ExecMsg #exec_bracketed_generics;
-                type Query = QueryMsg #query_bracketed_generics;
-                type Sudo = SudoMsg #sudo_bracketed_generics;
+            impl < #(#generics,)* > #sylvia ::types::InterfaceApi for Api < #(#generics,)* > #where_clause {
+                type Exec = ExecMsg < #(#exec_generics,)* >;
+                type Query = QueryMsg < #(#query_generics,)* >;
+                type Sudo = SudoMsg < #(#sudo_generics,)* >;
                 type Querier<'querier, Contract> = #sylvia ::types::BoundQuerier<'querier, #custom_query, Contract >;
             }
         }
diff --git a/sylvia-derive/src/parser/attributes/messages.rs b/sylvia-derive/src/parser/attributes/messages.rs
index 6f49b70..0b9e8df 100644
--- a/sylvia-derive/src/parser/attributes/messages.rs
+++ b/sylvia-derive/src/parser/attributes/messages.rs
@@ -105,7 +105,7 @@ impl Parse for ContractMessageAttr {
         if !input.is_empty() {
             emit_error!(input.span(),
                 "Unexpected tokens inside `sv::messages` attribtue.";
-                note = "Maximal supported form of attribute: `#[sv::messages(interface::path<T1, T2> as InterfaceName: custom(msg, query))]`."
+                note = "Maximal supported form of attribute: `#[sv::messages(interface::path as InterfaceName: custom(msg, query))]`."
             )
         }
         Ok(Self {
diff --git a/sylvia-derive/src/querier.rs b/sylvia-derive/src/querier.rs
index 0aae9f2..61d24c5 100644
--- a/sylvia-derive/src/querier.rs
+++ b/sylvia-derive/src/querier.rs
@@ -145,7 +145,7 @@ impl<'a> ContractQuerier<'a> {
                 #(#methods_declaration)*
             }
 
-            impl <'a, C: #sylvia ::cw_std::CustomQuery, #(#generics,)*> Querier #bracketed_generics for #sylvia ::types::BoundQuerier<'a, C, #contract_name > #where_clause {
+            impl <'sv_querier_lifetime, #(#generics,)* C: #sylvia ::cw_std::CustomQuery> Querier #bracketed_generics for #sylvia ::types::BoundQuerier<'sv_querier_lifetime, C, #contract_name > #where_clause {
                 #(#types_implementation)*
 
                 #(#methods_impl)*
diff --git a/sylvia-derive/src/utils.rs b/sylvia-derive/src/utils.rs
index 9be6631..c15b9a2 100644
--- a/sylvia-derive/src/utils.rs
+++ b/sylvia-derive/src/utils.rs
@@ -5,8 +5,8 @@ use quote::{quote, ToTokens};
 use syn::spanned::Spanned;
 use syn::visit::Visit;
 use syn::{
-    parse_quote, FnArg, GenericArgument, GenericParam, Ident, Path, PathArguments, ReturnType,
-    Signature, Type, WhereClause, WherePredicate,
+    parse_quote, FnArg, GenericArgument, Ident, Path, PathArguments, ReturnType, Signature, Type,
+    WhereClause, WherePredicate,
 };
 
 use crate::check_generics::{CheckGenerics, GetPath};
@@ -36,29 +36,6 @@ pub fn filter_wheres<'a, Generic: GetPath + PartialEq>(
         .unwrap_or_default()
 }
 
-/// Filters generic arguments, which are a concrete types,
-/// from the generic parameters
-///
-/// f.e.
-/// impl<A, B, C> MyTrait<A, B, D> for MyContract<C>
-///
-/// where generic parameters are `[A, B, C]` and generic arguments are `[A, B, D]`
-/// should return us the `[A, B]`.
-pub fn filter_generics<'a>(
-    generic_params: &'a [&'a GenericParam],
-    generic_args: &'a [&'a GenericArgument],
-) -> Vec<&'a GenericParam> {
-    generic_params
-        .iter()
-        .filter(|param| {
-            generic_args
-                .iter()
-                .any(|arg| param.get_path() == arg.get_path())
-        })
-        .copied()
-        .collect()
-}
-
 pub fn process_fields<'s, Generic>(
     sig: &'s Signature,
     generics_checker: &mut CheckGenerics<Generic>,


Test patch:
diff --git a/sylvia-derive/src/multitest.rs b/sylvia-derive/src/multitest.rs
index 28f26f7..0054160 100644
--- a/sylvia-derive/src/multitest.rs
+++ b/sylvia-derive/src/multitest.rs
@@ -140,6 +140,14 @@ impl<'a> ContractMtHelpers<'a> {
         };
         let api = quote! { < #contract_name as #sylvia ::types::ContractApi> };
 
+        let contract_ident = get_ident_from_type(contract_name);
+        let contract: Type = if !generic_params.is_empty() {
+            parse_quote! { #contract_ident ::< #(#generic_params,)* > }
+        } else {
+            parse_quote! { #contract_ident }
+        };
+        let trait_name = Ident::new(&format!("{}Proxy", contract_ident), contract_ident.span());
+
         let exec_methods = exec_variants.variants().map(|variant| {
             variant.emit_mt_method_definition(&custom_msg, &mt_app, error_type, &api)
         });
@@ -172,17 +180,14 @@ impl<'a> ContractMtHelpers<'a> {
 
         let impl_contract = self.emit_impl_contract();
         let code_id = self.emit_code_id();
-        let instantiate_proxy = self.emit_instantiate_proxy();
-
-        let contract = get_ident_from_type(contract_name);
-        let trait_name = Ident::new(&format!("{}Proxy", contract), contract.span());
+        let instantiate_proxy = self.emit_instantiate_proxy(&contract);
 
         quote! {
             pub mod mt {
                 use super::*;
                 use #sylvia ::cw_multi_test::Executor;
 
-                pub trait #trait_name <'app, MtApp, #(#generic_params,)* >
+                pub trait #trait_name <'app, #(#generic_params,)* MtApp >
                     #where_clause
                 {
                     #( #exec_methods_declarations )*
@@ -191,8 +196,8 @@ impl<'a> ContractMtHelpers<'a> {
                     #( #sudo_methods_declarations )*
                 }
 
-                impl<'app, BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT, #(#generic_params,)* >
-                    #trait_name <'app, #mt_app, #(#generic_params,)* >
+                impl<'app, #(#generic_params,)* BankT, ApiT, StorageT, CustomT, WasmT, StakingT, DistrT, IbcT, GovT >
+                    #trait_name <'app, #(#generic_params,)* #mt_app >
                         for #sylvia ::multitest::Proxy <'app, #mt_app, #contract_name >
                     where
                         CustomT: #sylvia ::cw_multi_test::Module,
@@ -234,6 +239,15 @@ impl<'a> ContractMtHelpers<'a> {
             ..
         } = self;
 
+        let generic_params_lifetimes_replaced =
+            generic_params.iter().cloned().cloned().map(|generic| {
+                if let GenericParam::Lifetime(_) = generic {
+                    parse_quote! { '_ }
+                } else {
+                    generic
+                }
+            });
+
         let fields_names = instantiate_variants
             .get_only_variant()
             .map(MsgVariant::as_fields_names)
@@ -250,11 +264,11 @@ impl<'a> ContractMtHelpers<'a> {
             .as_ref()
             .map(|where_clause| &where_clause.predicates);
 
-        let contract = get_ident_from_type(contract_name);
+        let contract_ident = get_ident_from_type(contract_name);
         let contract = if !generic_params.is_empty() {
-            quote! { #contract ::< #(#generic_params,)* > }
+            quote! { #contract_ident ::< #(#generic_params,)* > }
         } else {
-            quote! { #contract }
+            quote! { #contract_ident }
         };
 
         let instantiate_msg = if !used_generics.is_empty() {
@@ -291,14 +305,14 @@ impl<'a> ContractMtHelpers<'a> {
         };
 
         quote! {
-            pub struct CodeId<'app, #(#generic_params,)* MtApp> {
+            pub struct CodeId<'app, Contract, MtApp> {
                 code_id: u64,
                 app: &'app #sylvia ::multitest::App<MtApp>,
-                _phantom: std::marker::PhantomData<( #(#generic_params,)* )>,
+                _phantom: std::marker::PhantomData<Contract>,
 
             }
 
-            impl<'app, BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT, #(#generic_params,)* > CodeId<'app, #(#generic_params,)* #mt_app >
+            impl<'app, #(#generic_params,)* BankT, ApiT, StorageT, CustomT, StakingT, DistrT, IbcT, GovT > CodeId<'app, #contract, #mt_app >
                 where
                     BankT: #sylvia ::cw_multi_test::Bank,
                     ApiT: #sylvia ::cw_std::Api,
@@ -313,7 +327,7 @@ impl<'a> ContractMtHelpers<'a> {
                 pub fn store_code(app: &'app #sylvia ::multitest::App< #mt_app >) -> Self {
                     let code_id = app
                         .app_mut()
-                        .store_code(Box::new(#contract ::new()));
+                        .store_code(Box::new( #contract_ident:: < #(#generic_params_lifetimes_replaced),* > ::new() ));
                     Self { code_id, app, _phantom: std::marker::PhantomData::default() }
                 }
 
@@ -327,7 +341,7 @@ impl<'a> ContractMtHelpers<'a> {
                     &self, #(#fields,)*
                 ) -> InstantiateProxy<'_, 'app, #(#generic_params,)* #mt_app > {
                     let msg = #instantiate_msg {#(#fields_names,)*};
-                    InstantiateProxy::< #(#generic_params,)* _> {
+                    InstantiateProxy::<'_, 'app, #(#generic_params,)* _> {
                         code_id: self,
                         funds: &[],
                         label: "Contract",
@@ -340,7 +354,7 @@ impl<'a> ContractMtHelpers<'a> {
         }
     }
 
-    fn emit_instantiate_proxy(&self) -> TokenStream {
+    fn emit_instantiate_proxy(&self, contract: &Type) -> TokenStream {
         let sylvia = crate_module();
         let Self {
             error_type,
@@ -364,7 +378,7 @@ impl<'a> ContractMtHelpers<'a> {
 
         quote! {
             pub struct InstantiateProxy<'proxy, 'app, #(#generic_params,)* MtApp> {
-                code_id: &'proxy CodeId <'app, #(#generic_params,)* MtApp>,
+                code_id: &'proxy CodeId <'app, #contract, MtApp>,
                 funds: &'proxy [#sylvia ::cw_std::Coin],
                 label: &'proxy str,
                 admin: Option<String>,
@@ -385,7 +399,7 @@ impl<'a> ContractMtHelpers<'a> {
                     Self { label, ..self }
                 }
 
-                pub fn with_admin<'s>(self, admin: impl Into<Option<&'s str>>) -> Self {
+                pub fn with_admin<'sv_admins_lifetime>(self, admin: impl Into<Option<&'sv_admins_lifetime str>>) -> Self {
                     let admin = admin.into().map(str::to_owned);
                     Self { admin, ..self }
                 }
diff --git a/sylvia/tests/api.rs b/sylvia/tests/api.rs
index e742d43..ba2b3eb 100644
--- a/sylvia/tests/api.rs
+++ b/sylvia/tests/api.rs
@@ -89,7 +89,7 @@ mod tests {
             SvCustomMsg
         );
 
-        let _: crate::sv::ContractExecMsg<SvCustomMsg> = <SomeContract<
+        let _: crate::sv::ContractExecMsg<_, _, _, _, _> = <SomeContract<
             SvCustomMsg,
             SvCustomMsg,
             SvCustomMsg,
@@ -99,7 +99,7 @@ mod tests {
             exec
         );
 
-        let _: crate::sv::ContractQueryMsg<SvCustomMsg> = <SomeContract<
+        let _: crate::sv::ContractQueryMsg<_, _, _, _, _> = <SomeContract<
             SvCustomMsg,
             SvCustomMsg,
             SvCustomMsg,
diff --git a/sylvia/tests/multitest.rs b/sylvia/tests/multitest.rs
index d43671a..f20a8c2 100644
--- a/sylvia/tests/multitest.rs
+++ b/sylvia/tests/multitest.rs
@@ -35,7 +35,7 @@ fn instantiate_with_salt() {
 
     let app = App::default();
 
-    let code_id = sv::mt::CodeId::<Empty, _>::store_code(&app);
+    let code_id = sv::mt::CodeId::<SomeContract<Empty>, _>::store_code(&app);
 
     let _: sylvia::multitest::Proxy<_, SomeContract<Empty>> = code_id
         .instantiate(Empty {})
@@ -48,7 +48,7 @@ fn instantiate_with_salt() {
 fn code_info() {
     let app = App::default();
 
-    let code_id = sv::mt::CodeId::<Empty, _>::store_code(&app);
+    let code_id = sv::mt::CodeId::<SomeContract<Empty>, _>::store_code(&app);
 
     let _: CodeInfoResponse = code_id.code_info().unwrap();
     let _: CodeInfoResponse = app.code_info(code_id.code_id()).unwrap();
diff --git a/sylvia/tests/querier.rs b/sylvia/tests/querier.rs
index 917ad90..fe39632 100644
--- a/sylvia/tests/querier.rs
+++ b/sylvia/tests/querier.rs
@@ -95,7 +95,7 @@ pub struct CounterContract<'a> {
 
 #[contract]
 #[sv::messages(counter)]
-impl CounterContract<'_> {
+impl<'asdf> CounterContract<'asdf> {
     #[allow(clippy::new_without_default)]
     pub fn new() -> Self {
         Self {
diff --git a/sylvia/tests/ui/attributes/messages/unexpected_token.stderr b/sylvia/tests/ui/attributes/messages/unexpected_token.stderr
index fada585..6e1bcc9 100644
--- a/sylvia/tests/ui/attributes/messages/unexpected_token.stderr
+++ b/sylvia/tests/ui/attributes/messages/unexpected_token.stderr
@@ -1,6 +1,6 @@
 error: Unexpected tokens inside `sv::messages` attribtue.
 
-         = note: Maximal supported form of attribute: `#[sv::messages(interface::path<T1, T2> as InterfaceName: custom(msg, query))]`.
+         = note: Maximal supported form of attribute: `#[sv::messages(interface::path as InterfaceName: custom(msg, query))]`.
 
   --> tests/ui/attributes/messages/unexpected_token.rs:24:25
    |